Spaces:
Running
Running
| import streamlit as st | |
| from pptx import Presentation | |
| from pptx.util import Inches, Pt | |
| from pptx.dml.color import RGBColor | |
| from pptx.enum.text import PP_ALIGN | |
| from groq import Groq | |
| import os | |
| import json | |
| from dotenv import load_dotenv | |
| import tempfile | |
| from tenacity import retry, stop_after_attempt, wait_fixed | |
| import random | |
| load_dotenv() | |
| # Color palettes inspired by Shutterstock | |
| COLOR_PALETTES = [ | |
| { | |
| 'primary': RGBColor(255, 87, 51), # Orange | |
| 'secondary': RGBColor(0, 115, 207), # Blue | |
| 'accent': RGBColor(255, 255, 255), # White | |
| 'text': RGBColor(51, 51, 51), # Dark Gray | |
| 'background': RGBColor(242, 242, 242) # Light Gray | |
| }, | |
| { | |
| 'primary': RGBColor(102, 45, 145), # Purple | |
| 'secondary': RGBColor(255, 230, 0), # Yellow | |
| 'accent': RGBColor(0, 255, 255), # Cyan | |
| 'text': RGBColor(51, 51, 51), # Dark Gray | |
| 'background': RGBColor(230, 230, 250) # Lavender | |
| }, | |
| { | |
| 'primary': RGBColor(0, 176, 80), # Green | |
| 'secondary': RGBColor(255, 192, 0), # Gold | |
| 'accent': RGBColor(0, 112, 192), # Blue | |
| 'text': RGBColor(51, 51, 51), # Dark Gray | |
| 'background': RGBColor(240, 255, 240) # Honeydew | |
| }, | |
| { | |
| 'primary': RGBColor(192, 0, 0), # Red | |
| 'secondary': RGBColor(0, 176, 240), # Light Blue | |
| 'accent': RGBColor(255, 255, 255), # White | |
| 'text': RGBColor(51, 51, 51), # Dark Gray | |
| 'background': RGBColor(255, 240, 245) # Lavender Blush | |
| }, | |
| { | |
| 'primary': RGBColor(0, 80, 115), # Dark Blue | |
| 'secondary': RGBColor(255, 140, 0), # Dark Orange | |
| 'accent': RGBColor(0, 176, 80), # Green | |
| 'text': RGBColor(51, 51, 51), # Dark Gray | |
| 'background': RGBColor(240, 248, 255) # Alice Blue | |
| } | |
| ] | |
| def get_random_color_palette(): | |
| return random.choice(COLOR_PALETTES) | |
| def apply_theme(prs, color_scheme): | |
| # Apply theme colors to the master slide | |
| background = prs.slide_masters[0].background | |
| background.fill.solid() | |
| background.fill.fore_color.rgb = color_scheme['background'] | |
| # Apply theme colors to placeholders | |
| for shape in prs.slide_masters[0].placeholders: | |
| if shape.has_text_frame: | |
| for paragraph in shape.text_frame.paragraphs: | |
| for run in paragraph.runs: | |
| run.font.color.rgb = color_scheme['text'] | |
| def create_title_slide(prs, title, color_scheme): | |
| slide_layout = prs.slide_layouts[0] # Title Slide layout | |
| slide = prs.slides.add_slide(slide_layout) | |
| title_shape = slide.shapes.title | |
| subtitle_shape = slide.placeholders[1] | |
| title_shape.text = title | |
| title_shape.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER | |
| title_shape.text_frame.paragraphs[0].font.color.rgb = color_scheme['primary'] | |
| title_shape.text_frame.paragraphs[0].font.size = Pt(44) | |
| subtitle_shape.text = "Generated with AI" | |
| subtitle_shape.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER | |
| subtitle_shape.text_frame.paragraphs[0].font.color.rgb = color_scheme['secondary'] | |
| subtitle_shape.text_frame.paragraphs[0].font.size = Pt(24) | |
| def create_content_slide(prs, title, content, color_scheme): | |
| slide_layout = prs.slide_layouts[1] # Content with Caption layout | |
| slide = prs.slides.add_slide(slide_layout) | |
| title_shape = slide.shapes.title | |
| title_shape.text = title | |
| title_shape.text_frame.paragraphs[0].font.color.rgb = color_scheme['primary'] | |
| title_shape.text_frame.paragraphs[0].font.size = Pt(36) | |
| content_shape = slide.placeholders[1] | |
| tf = content_shape.text_frame | |
| tf.clear() # Clear existing text | |
| # Add main content | |
| p = tf.paragraphs[0] | |
| p.text = content['main'] | |
| p.font.size = Pt(18) | |
| p.font.color.rgb = color_scheme['text'] | |
| # Add bullet points | |
| for bullet in content['bullets']: | |
| p = tf.add_paragraph() | |
| p.text = bullet | |
| p.level = 1 | |
| p.font.size = Pt(16) | |
| p.font.color.rgb = color_scheme['text'] | |
| # Add a subtle accent to the slide | |
| left = Inches(0) | |
| top = Inches(6.5) | |
| width = prs.slide_width | |
| height = Inches(0.5) | |
| shape = slide.shapes.add_shape(1, left, top, width, height) | |
| shape.fill.solid() | |
| shape.fill.fore_color.rgb = color_scheme['accent'] | |
| shape.line.color.rgb = color_scheme['accent'] | |
| def generate_slides_content(user_input, num_slides): | |
| client = Groq( | |
| api_key=os.environ.get("GROQ_API_KEY"),) | |
| prompt = f""" | |
| Based on the following input, generate a PowerPoint presentation structure with exactly {num_slides} slides. | |
| The output should be a JSON array of slides, where each slide is an object with a 'title', 'main' content, and 'bullets' (an array of bullet points). | |
| Make sure the content is concise and suitable for a presentation. | |
| User Input: {user_input} | |
| Example output format: | |
| [ | |
| {{ | |
| "title": "Slide Title", | |
| "main": "Main content of the slide", | |
| "bullets": ["Bullet point 1", "Bullet point 2", "Bullet point 3"] | |
| }}, | |
| // ... more slides (total should be {num_slides}) | |
| ] | |
| """ | |
| try: | |
| chat_completion = client.chat.completions.create( | |
| messages=[ | |
| {"role": "system", "content": "You are a helpful assistant that generates PowerPoint presentation content and return the content in JSON format."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| model="mixtral-8x7b-32768", | |
| temperature=1, | |
| max_tokens=8000 | |
| ) | |
| return json.loads(chat_completion.choices[0].message.content) | |
| except json.JSONDecodeError: | |
| st.error("Error: Invalid JSON response from the AI. Retrying...") | |
| raise | |
| except Exception as e: | |
| st.error(f"An error occurred: {str(e)}. Retrying...") | |
| raise | |
| def generate_presentation(user_input, num_slides): | |
| try: | |
| slides_content = generate_slides_content(user_input, num_slides) | |
| prs = Presentation() | |
| color_scheme = get_random_color_palette() | |
| apply_theme(prs, color_scheme) | |
| # Create title slide | |
| create_title_slide(prs, slides_content[0]['title'], color_scheme) | |
| # Create content slides | |
| for slide in slides_content[1:]: # Skip the first slide as it's used for the title | |
| create_content_slide(prs, slide['title'], {'main': slide['main'], 'bullets': slide['bullets']}, color_scheme) | |
| with tempfile.NamedTemporaryFile(delete=False, suffix='.pptx') as tmp: | |
| prs.save(tmp.name) | |
| return tmp.name | |
| except Exception as e: | |
| st.error(f"Failed to generate presentation: {str(e)}") | |
| return None | |
| def main(): | |
| st.set_page_config(page_title="PowerPoint Generator", page_icon="📊", layout="wide") | |
| st.title("🎨 AI-Powered PowerPoint Generator") | |
| st.write("Enter your presentation idea and the number of slides you want, and we'll generate a stylish PowerPoint for you!") | |
| user_input = st.text_area("Enter the content idea for your presentation:", height=150) | |
| num_slides = st.number_input("Number of slides:", min_value=2, max_value=20, value=5) | |
| if st.button("Generate Presentation", use_container_width=True, type="primary"): | |
| if user_input and num_slides: | |
| with st.spinner("Generating your stylish presentation... This may take a moment."): | |
| ppt_file = generate_presentation(user_input, num_slides) | |
| if ppt_file: | |
| st.success("Presentation generated successfully!") | |
| with open(ppt_file, "rb") as file: | |
| st.download_button( | |
| label="Download PowerPoint", | |
| data=file, | |
| file_name="generated_presentation.pptx", | |
| mime="application/vnd.openxmlformats-officedocument.presentationml.presentation" | |
| ) | |
| else: | |
| st.error("Failed to generate the presentation. Please try again.") | |
| else: | |
| st.warning("Please enter content and specify the number of slides (minimum 2).") | |
| if __name__ == "__main__": | |
| main() |