YashMK89 commited on
Commit
456f180
·
verified ·
1 Parent(s): d46e010

update app.py

Browse files
Files changed (1) hide show
  1. app.py +306 -54
app.py CHANGED
@@ -1,23 +1,104 @@
1
  import gradio as gr
2
- import os
3
- from pptx import Presentation
4
- from pptx.util import Inches
5
  import google.generativeai as genai
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
- # Initialize Google Generative AI
8
- GOOGLE_API_KEY = os.getenv("AIzaSyDMRDzpIx2OuiO8xoV8UntyRYhl7LbRup8")
9
- genai.configure(api_key=GOOGLE_API_KEY)
10
- model = genai.GenerativeModel("gemini-pro")
11
 
12
- # Function to generate slides using Gemini
13
- def generate_slide_content(topic):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  prompt = f"""Create a comprehensive presentation on '{topic}' with exactly {slide_count} slides.
15
  For each slide, provide:
16
  1. A clear title in [Title:] format
17
  2. 3-5 detailed bullet points in [Content:] format (each point should have a brief explanation)
18
  3. Optional speaker notes in [Notes:] format
19
  4. Layout suggestion in [Layout:] format (title-only, title-content, two-column, section-header)
20
-
21
  Structure your response like this:
22
  [Title:] Slide Title
23
  [Layout:] title-content
@@ -26,7 +107,6 @@ def generate_slide_content(topic):
26
  - Main Point 2: Brief explanation of this point (keep to one line)
27
  - Main Point 3: Brief explanation of this point (keep to one line)
28
  [Notes:] Additional notes here
29
-
30
  Include these sections (adjust based on requested slide count):
31
  - Title slide
32
  - Introduction/Overview
@@ -38,55 +118,227 @@ def generate_slide_content(topic):
38
  - Future Trends (with supporting evidence)
39
  - Conclusion (with summary points)
40
  - Q&A
41
-
42
  Ensure each bullet point follows the format:
43
  - Main Point: Explanation (keep explanation concise but informative)"""
44
  response = model.generate_content(prompt)
45
  return response.text
46
 
47
- # Function to create a PowerPoint from the generated content
48
- def create_ppt_from_text(content, filename="output.pptx"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  prs = Presentation()
50
- slides = content.strip().split("Slide")[1:] # Skip any leading text
51
-
52
- for slide in slides:
53
- lines = slide.strip().split("\n")
54
- title = ""
55
- bullets = []
56
-
57
- for line in lines:
58
- if "Title:" in line:
59
- title = line.split("Title:")[1].strip()
60
- elif "Bullet" in line and ":" in line:
61
- bullets.append(line.split(":", 1)[1].strip())
62
-
63
- slide_layout = prs.slide_layouts[1] # Title and Content
64
- slide_obj = prs.slides.add_slide(slide_layout)
65
- slide_obj.shapes.title.text = title
66
- content_box = slide_obj.placeholders[1]
67
- content_box.text = bullets[0] if bullets else ""
68
-
69
- for bullet in bullets[1:]:
70
- p = content_box.text_frame.add_paragraph()
71
- p.text = bullet
72
-
73
- prs.save(filename)
74
- return filename
75
-
76
- # Wrapper for Gradio interface
77
- def generate_ppt(topic):
78
- content = generate_slide_content(topic)
79
- ppt_path = create_ppt_from_text(content)
80
- return ppt_path
81
-
82
- # Gradio UI
83
- interface = gr.Interface(
84
- fn=generate_ppt,
85
- inputs=gr.Textbox(label="Enter your presentation topic"),
86
- outputs=gr.File(label="Download your PowerPoint"),
87
- title="AI PowerPoint Generator",
88
- description="Generate a 5-slide PowerPoint presentation using Google Gemini and download the PPTX file."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  )
90
 
91
  if __name__ == "__main__":
92
- interface.launch()
 
1
  import gradio as gr
 
 
 
2
  import google.generativeai as genai
3
+ from pptx import Presentation
4
+ from pptx.util import Inches, Pt
5
+ from pptx.dml.color import RGBColor
6
+ from pptx.enum.text import PP_ALIGN
7
+ from pptx.oxml.xmlchemy import OxmlElement
8
+ import io
9
+ import re
10
+ import tempfile
11
+ import os
12
+
13
+ # Setup Google API key (replace with your actual key)
14
+ genai.configure(api_key="AIzaSyDMRDzpIx2OuiO8xoV8UntyRYhl7LbRup8")
15
+
16
+ # Default theme configurations
17
+ DEFAULT_THEMES = {
18
+ "Professional Blue": {
19
+ "background": RGBColor(12, 35, 64),
20
+ "title_color": RGBColor(255, 255, 255),
21
+ "text_color": RGBColor(200, 200, 200),
22
+ "accent": RGBColor(0, 112, 192),
23
+ "title_font": "Calibri",
24
+ "text_font": "Calibri"
25
+ },
26
+ "Modern Green": {
27
+ "background": RGBColor(22, 82, 66),
28
+ "title_color": RGBColor(255, 255, 255),
29
+ "text_color": RGBColor(220, 220, 220),
30
+ "accent": RGBColor(76, 175, 80),
31
+ "title_font": "Arial",
32
+ "text_font": "Arial"
33
+ },
34
+ "Light Corporate": {
35
+ "background": RGBColor(255, 255, 255),
36
+ "title_color": RGBColor(13, 71, 161),
37
+ "text_color": RGBColor(33, 33, 33),
38
+ "accent": RGBColor(25, 118, 210),
39
+ "title_font": "Segoe UI",
40
+ "text_font": "Segoe UI"
41
+ },
42
+ "Dark Tech": {
43
+ "background": RGBColor(33, 33, 33),
44
+ "title_color": RGBColor(0, 200, 255),
45
+ "text_color": RGBColor(200, 200, 200),
46
+ "accent": RGBColor(0, 150, 255),
47
+ "title_font": "Consolas",
48
+ "text_font": "Consolas"
49
+ }
50
+ }
51
+
52
+ custom_themes = {}
53
 
54
+ def hex_to_rgb(hex_color):
55
+ """Convert hex color to RGBColor"""
56
+ hex_color = hex_color.lstrip('#')
57
+ return RGBColor(*tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)))
58
 
59
+ def extract_theme_from_pptx(uploaded_file):
60
+ """Extract theme colors and fonts from an uploaded PowerPoint file"""
61
+ with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
62
+ tmp_file.write(uploaded_file.read())
63
+ tmp_file_path = tmp_file.name
64
+ prs = Presentation(tmp_file_path)
65
+ theme = {
66
+ "background": RGBColor(0, 0, 0), # Default black if not found
67
+ "title_color": RGBColor(0, 0, 0),
68
+ "text_color": RGBColor(0, 0, 0),
69
+ "accent": RGBColor(0, 0, 0),
70
+ "title_font": "Calibri",
71
+ "text_font": "Calibri"
72
+ }
73
+ try:
74
+ slide_master = prs.slide_master
75
+ theme["background"] = slide_master.background.fill.fore_color.rgb
76
+ for shape in slide_master.shapes:
77
+ if shape.has_text_frame and shape.text.strip():
78
+ if shape.name.lower() == 'title':
79
+ theme["title_color"] = shape.text_frame.paragraphs[0].font.color.rgb
80
+ theme["title_font"] = shape.text_frame.paragraphs[0].font.name
81
+ else:
82
+ theme["text_color"] = shape.text_frame.paragraphs[0].font.color.rgb
83
+ theme["text_font"] = shape.text_frame.paragraphs[0].font.name
84
+ for shape in slide_master.shapes:
85
+ if hasattr(shape, 'fill'):
86
+ if shape.fill.type == 1: # Solid fill
87
+ theme["accent"] = shape.fill.fore_color.rgb
88
+ break
89
+ except Exception as e:
90
+ print(f"Warning: Couldn't fully extract theme: {str(e)}. Using default colors where needed.")
91
+ os.unlink(tmp_file_path)
92
+ return theme
93
+
94
+ def generate_slide_content(topic, slide_count):
95
+ model = genai.GenerativeModel('gemini-2.0-flash')
96
  prompt = f"""Create a comprehensive presentation on '{topic}' with exactly {slide_count} slides.
97
  For each slide, provide:
98
  1. A clear title in [Title:] format
99
  2. 3-5 detailed bullet points in [Content:] format (each point should have a brief explanation)
100
  3. Optional speaker notes in [Notes:] format
101
  4. Layout suggestion in [Layout:] format (title-only, title-content, two-column, section-header)
 
102
  Structure your response like this:
103
  [Title:] Slide Title
104
  [Layout:] title-content
 
107
  - Main Point 2: Brief explanation of this point (keep to one line)
108
  - Main Point 3: Brief explanation of this point (keep to one line)
109
  [Notes:] Additional notes here
 
110
  Include these sections (adjust based on requested slide count):
111
  - Title slide
112
  - Introduction/Overview
 
118
  - Future Trends (with supporting evidence)
119
  - Conclusion (with summary points)
120
  - Q&A
 
121
  Ensure each bullet point follows the format:
122
  - Main Point: Explanation (keep explanation concise but informative)"""
123
  response = model.generate_content(prompt)
124
  return response.text
125
 
126
+ def parse_slide_content(slide_text):
127
+ slides = []
128
+ current_slide = {}
129
+ for line in slide_text.split('\n'):
130
+ line = line.strip()
131
+ if not line:
132
+ continue
133
+ if line.startswith('[Title:]'):
134
+ if current_slide:
135
+ slides.append(current_slide)
136
+ current_slide = {
137
+ 'title': line.replace('[Title:]', '').strip(),
138
+ 'content': [],
139
+ 'notes': '',
140
+ 'layout': 'title-content'
141
+ }
142
+ elif line.startswith('[Content:]'):
143
+ content = line.replace('[Content:]', '').strip()
144
+ if content:
145
+ current_slide['content'].append(content)
146
+ elif line.startswith('[Notes:]'):
147
+ current_slide['notes'] = line.replace('[Notes:]', '').strip()
148
+ elif line.startswith('[Layout:]'):
149
+ layout = line.replace('[Layout:]', '').strip().lower()
150
+ valid_layouts = ['title-only', 'title-content', 'two-column', 'section-header']
151
+ current_slide['layout'] = layout if layout in valid_layouts else 'title-content'
152
+ elif current_slide.get('content') is not None and line.startswith('-'):
153
+ current_slide['content'].append(line.strip())
154
+ if current_slide:
155
+ slides.append(current_slide)
156
+ return slides
157
+
158
+ def create_detailed_pptx(slides_data, theme):
159
  prs = Presentation()
160
+ prs.slide_width = Inches(13.33)
161
+ prs.slide_height = Inches(7.5)
162
+ layout_mapping = {
163
+ 'title-only': 0,
164
+ 'title-content': 1,
165
+ 'section-header': 2,
166
+ 'two-column': 3
167
+ }
168
+
169
+ for slide_info in slides_data:
170
+ layout = slide_info.get('layout', 'title-content')
171
+ layout_idx = layout_mapping.get(layout, 1)
172
+ slide = prs.slides.add_slide(prs.slide_layouts[layout_idx])
173
+
174
+ background = slide.background
175
+ fill = background.fill
176
+ fill.solid()
177
+ fill.fore_color.rgb = theme["background"]
178
+
179
+ title = slide.shapes.title
180
+ title.text = slide_info['title']
181
+ title.text_frame.paragraphs[0].font.color.rgb = theme["title_color"]
182
+ title.text_frame.paragraphs[0].font.size = Pt(36)
183
+ title.text_frame.paragraphs[0].font.bold = True
184
+ if "title_font" in theme:
185
+ title.text_frame.paragraphs[0].font.name = theme["title_font"]
186
+
187
+ title.text_frame.word_wrap = True
188
+ title.text_frame.auto_size = True
189
+ title.top = Inches(0.5)
190
+ title.height = Inches(1.5)
191
+ title.width = Inches(11)
192
+
193
+ if layout_idx == 3:
194
+ content = slide_info.get('content', [])
195
+ mid_point = len(content) // 2
196
+ left_content = content[:mid_point]
197
+ right_content = content[mid_point:]
198
+
199
+ left_body = slide.placeholders[1]
200
+ left_tf = left_body.text_frame
201
+ left_tf.clear()
202
+ left_body.width = Inches(5.5)
203
+ left_body.left = Inches(0.5)
204
+
205
+ right_body = slide.placeholders[2]
206
+ right_tf = right_body.text_frame
207
+ right_tf.clear()
208
+ right_body.width = Inches(5.5)
209
+ right_body.left = Inches(7.0)
210
+
211
+ for content_part, tf in [(left_content, left_tf), (right_content, right_tf)]:
212
+ for point in content_part:
213
+ p = tf.add_paragraph()
214
+ point_text = point.replace('- ', '').strip()
215
+ if len(point_text) > 80:
216
+ parts = [point_text[i:i+80] for i in range(0, len(point_text), 80)]
217
+ p.text = parts[0]
218
+ for part in parts[1:]:
219
+ new_p = tf.add_paragraph()
220
+ new_p.text = part
221
+ new_p.level = 1
222
+ else:
223
+ p.text = point_text
224
+ p.level = 0
225
+ p.font.color.rgb = theme["text_color"]
226
+ p.font.size = Pt(18)
227
+ if "text_font" in theme:
228
+ p.font.name = theme["text_font"]
229
+ p.space_after = Pt(6)
230
+ p.space_before = Pt(6)
231
+ elif layout_idx != 0:
232
+ body = slide.placeholders[1]
233
+ tf = body.text_frame
234
+ tf.clear()
235
+ body.top = Inches(2.0)
236
+ body.width = Inches(11)
237
+ body.height = Inches(4.5)
238
+ for point in slide_info.get('content', []):
239
+ p = tf.add_paragraph()
240
+ point_text = point.replace('- ', '').strip()
241
+ if len(point_text) > 100:
242
+ parts = [point_text[i:i+100] for i in range(0, len(point_text), 100)]
243
+ p.text = parts[0]
244
+ for part in parts[1:]:
245
+ new_p = tf.add_paragraph()
246
+ new_p.text = part
247
+ new_p.level = 1
248
+ new_p.font.color.rgb = theme["text_color"]
249
+ new_p.font.size = Pt(16)
250
+ if "text_font" in theme:
251
+ new_p.font.name = theme["text_font"]
252
+ else:
253
+ p.text = point_text
254
+ p.level = 0
255
+ p.font.color.rgb = theme["text_color"]
256
+ p.font.size = Pt(18)
257
+ if "text_font" in theme:
258
+ p.font.name = theme["text_font"]
259
+ p.space_after = Pt(6)
260
+ p.space_before = Pt(6)
261
+
262
+ if slide_info.get('notes'):
263
+ notes_slide = slide.notes_slide
264
+ notes_slide.notes_text_frame.text = slide_info['notes']
265
+
266
+ pptx_io = io.BytesIO()
267
+ prs.save(pptx_io)
268
+ pptx_io.seek(0)
269
+ return pptx_io
270
+
271
+ def process_input(topic, slide_count, theme_option, theme_name, bg_color, title_color, text_color, accent_color,
272
+ title_font, text_font, uploaded_file):
273
+ if not topic:
274
+ return "Please enter a topic.", None, None
275
+
276
+ ALL_THEMES = {**DEFAULT_THEMES, **custom_themes}
277
+ theme = DEFAULT_THEMES["Professional Blue"]
278
+
279
+ if theme_option == "Predefined Theme":
280
+ theme = DEFAULT_THEMES[theme_name]
281
+ elif theme_option == "Custom Theme":
282
+ if theme_name == "Create New...":
283
+ custom_theme_name = "Custom_" + str(len(custom_themes))
284
+ custom_themes[custom_theme_name] = {
285
+ "background": hex_to_rgb(bg_color),
286
+ "title_color": hex_to_rgb(title_color),
287
+ "text_color": hex_to_rgb(text_color),
288
+ "accent": hex_to_rgb(accent_color),
289
+ "title_font": title_font,
290
+ "text_font": text_font
291
+ }
292
+ theme = custom_themes[custom_theme_name]
293
+ else:
294
+ theme = custom_themes.get(theme_name, theme)
295
+ elif theme_option == "Example-Based Theme":
296
+ if uploaded_file is None:
297
+ return "Please upload a PowerPoint template.", None, None
298
+ theme = extract_theme_from_pptx(uploaded_file)
299
+
300
+ slide_text = generate_slide_content(topic, slide_count)
301
+ slides_data = parse_slide_content(slide_text)
302
+ pptx_file = create_detailed_pptx(slides_data, theme)
303
+
304
+ overview = ""
305
+ for i, slide in enumerate(slides_data, 1):
306
+ overview += f"Slide {i}: {slide['title']}\nContent:\n"
307
+ for point in slide.get('content', []):
308
+ overview += f"- {point}\n"
309
+ if slide.get('notes'):
310
+ overview += f"Notes: {slide['notes']}\n"
311
+ overview += "---\n"
312
+
313
+ download_name = f"{topic.replace(' ', '_')}_presentation.pptx"
314
+ return overview, pptx_file, download_name
315
+
316
+ # Define Gradio interface
317
+ theme_options = ["Predefined Theme", "Custom Theme", "Example-Based Theme"]
318
+ predefined_themes = list(DEFAULT_THEMES.keys())
319
+
320
+ demo = gr.Interface(
321
+ fn=process_input,
322
+ inputs=[
323
+ gr.Textbox(label="Presentation Topic"),
324
+ gr.Slider(minimum=5, maximum=20, value=10, label="Number of Slides"),
325
+ gr.Radio(choices=theme_options, label="Theme Selection Method"),
326
+ gr.Dropdown(choices=predefined_themes, label="Select Predefined Theme", visible=True),
327
+ gr.ColorPicker(value="#0C2340", label="Background Color"),
328
+ gr.ColorPicker(value="#FFFFFF", label="Title Color"),
329
+ gr.ColorPicker(value="#C8C8C8", label="Text Color"),
330
+ gr.ColorPicker(value="#0070C0", label="Accent Color"),
331
+ gr.Textbox(value="Calibri", label="Title Font"),
332
+ gr.Textbox(value="Calibri", label="Text Font"),
333
+ gr.File(label="Upload PowerPoint Template", file_types=[".pptx"], visible=False)
334
+ ],
335
+ outputs=[
336
+ gr.Textbox(label="Slide Overview (Detailed)"),
337
+ gr.File(label="Download PowerPoint", file_types=[".pptx"]),
338
+ gr.Textbox(label="Filename")
339
+ ],
340
+ allow_flagging="never"
341
  )
342
 
343
  if __name__ == "__main__":
344
+ demo.launch()