ash2203's picture
Create app.py
ee87229 verified
raw
history blame
8.35 kB
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']
@retry(stop=stop_after_attempt(5), wait=wait_fixed(2))
def generate_slides_content(user_input, num_slides):
client = Groq(api_key=os.getenv("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=3000
)
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()