bluenevus commited on
Commit
d5b0b2f
·
1 Parent(s): 2b3bcb4

Update app.py via AI Editor

Browse files
Files changed (1) hide show
  1. app.py +61 -30
app.py CHANGED
@@ -15,31 +15,26 @@ from PIL import Image
15
  import google.generativeai as genai
16
  import time
17
 
18
- # Set up logging
 
 
19
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
20
  logger = logging.getLogger(__name__)
21
 
22
- # Stability AI API key
23
  STABILITY_API_KEY = os.getenv('STABILITY_API_KEY')
24
-
25
- # Gemini API key
26
  GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
27
  genai.configure(api_key=GOOGLE_API_KEY)
28
-
29
- # Initialize Gemini model
30
  model = genai.GenerativeModel('gemini-2.5-pro-preview-03-25')
31
 
32
  app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP, 'https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap'])
33
  app.config.suppress_callback_exceptions = True
34
 
35
- # Global variables to store logs and generated PowerPoint
36
  log_messages = []
37
  generated_pptx = None
38
 
39
  app.layout = html.Div([
40
  dbc.Container([
41
  dbc.Row([
42
- # Left Column
43
  dbc.Col([
44
  dbc.Card([
45
  dbc.CardHeader("Upload or Paste Document", className="bg-light"),
@@ -72,7 +67,6 @@ app.layout = html.Div([
72
  ])
73
  ], className="mb-4 shadow-sm")
74
  ], md=6),
75
- # Right Column
76
  dbc.Col([
77
  dbc.Card([
78
  dbc.CardHeader("Edit Slide Content", className="bg-light"),
@@ -91,13 +85,56 @@ app.layout = html.Div([
91
  ])
92
  ], fluid=True, className="mt-4"),
93
  dcc.Download(id="download-pptx"),
94
- dcc.Interval(id='interval-component', interval=1*1000, n_intervals=0) # Updates every 1 second
95
  ])
96
 
97
  def add_log(message):
98
  global log_messages
99
  logger.info(message)
100
- log_messages.insert(0, f"{time.strftime('%H:%M:%S')} - {message}") # Insert at the beginning
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
  def process_document(text):
103
  add_log("Starting document processing...")
@@ -111,6 +148,8 @@ def process_document(text):
111
  There can only be one slide heading # and one sub heading ## and no more than 5 bullets with "-" per slide
112
  Document:
113
  {text}
 
 
114
  """
115
  add_log("Sending request to Gemini model...")
116
  response = model.generate_content(prompt)
@@ -143,7 +182,6 @@ def generate_image(prompt):
143
  "output_format": "png"
144
  }
145
  files = {"none": ''}
146
-
147
  try:
148
  response = requests.post(url, headers=headers, data=data, files=files)
149
  response.raise_for_status()
@@ -170,15 +208,11 @@ def markdown_to_pptx(md_text):
170
  add_log(f"Creating slide {i+1}/{len(slides)}...")
171
  lines = slide_content.split('\n')
172
  title = lines[0].strip()
173
- current_slide = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout
174
-
175
- # Add title
176
  title_box = current_slide.shapes.add_textbox(Inches(0.5), Inches(0.5), Inches(9), Inches(1))
177
  title_box.text_frame.text = title
178
  title_box.text_frame.paragraphs[0].font.size = Pt(32)
179
  title_box.text_frame.paragraphs[0].font.bold = True
180
-
181
- # Add content (left side)
182
  content_box = current_slide.shapes.add_textbox(Inches(0.5), Inches(1.5), Inches(4.5), Inches(6))
183
  text_frame = content_box.text_frame
184
  text_frame.word_wrap = True
@@ -194,8 +228,6 @@ def markdown_to_pptx(md_text):
194
  p.font.size = Pt(18)
195
  p.level = 1
196
  p.bullet = True
197
-
198
- # Add image (right side)
199
  add_log(f"Adding image to slide {i+1}...")
200
  image_data = future.result()
201
  if image_data:
@@ -215,7 +247,6 @@ def markdown_to_pptx(md_text):
215
  add_log(f"Failed to generate image for slide {i+1}: {title}")
216
  placeholder = current_slide.shapes.add_textbox(Inches(5.5), Inches(1.5), Inches(4), Inches(6))
217
  placeholder.text_frame.text = "Image generation failed"
218
-
219
  add_log("PowerPoint generation completed.")
220
  output = io.BytesIO()
221
  prs.save(output)
@@ -230,22 +261,24 @@ def update_filename(filename):
230
  return html.Div(f"Selected file: {filename}")
231
  return ""
232
 
233
-
234
  @app.callback(
235
  Output('slides-content', 'value'),
236
  Output('download-ppt-button', 'disabled'),
237
  Input('generate-button', 'n_clicks'),
 
238
  State('upload-file', 'contents'),
239
  State('document-text', 'value')
240
  )
241
- def generate_slides(n_clicks, file_contents, document_text):
242
  if n_clicks is None:
243
  raise PreventUpdate
244
 
245
- if file_contents:
246
- content_type, content_string = file_contents.split(',')
247
- decoded = base64.b64decode(content_string)
248
- document_text = decoded.decode('utf-8')
 
 
249
  elif not document_text:
250
  return "Please upload a file or enter text.", True
251
 
@@ -253,7 +286,7 @@ def generate_slides(n_clicks, file_contents, document_text):
253
  add_log("Starting slide generation...")
254
  slides_markdown = process_document(document_text)
255
  add_log("Slide generation completed.")
256
- return slides_markdown, False # Enable download button
257
  except Exception as e:
258
  add_log(f"An error occurred during slide generation: {str(e)}")
259
  return f"An error occurred: {str(e)}", True
@@ -269,12 +302,11 @@ def generate_powerpoint(n_clicks, slides_content):
269
  global generated_pptx
270
  if n_clicks is None or not slides_content:
271
  raise PreventUpdate
272
-
273
  try:
274
  add_log("Starting PowerPoint generation...")
275
  generated_pptx = markdown_to_pptx(slides_content)
276
  add_log("PowerPoint generation completed.")
277
- return '\n'.join(log_messages[:100]), False # Enable download button
278
  except Exception as e:
279
  error_message = f"An error occurred during PowerPoint generation: {str(e)}"
280
  add_log(error_message)
@@ -288,7 +320,6 @@ def generate_powerpoint(n_clicks, slides_content):
288
  def download_powerpoint(n_clicks):
289
  if n_clicks is None or generated_pptx is None:
290
  raise PreventUpdate
291
-
292
  return dcc.send_bytes(generated_pptx, "presentation.pptx")
293
 
294
  @app.callback(
 
15
  import google.generativeai as genai
16
  import time
17
 
18
+ import docx
19
+ import PyPDF2
20
+
21
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
22
  logger = logging.getLogger(__name__)
23
 
 
24
  STABILITY_API_KEY = os.getenv('STABILITY_API_KEY')
 
 
25
  GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
26
  genai.configure(api_key=GOOGLE_API_KEY)
 
 
27
  model = genai.GenerativeModel('gemini-2.5-pro-preview-03-25')
28
 
29
  app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP, 'https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap'])
30
  app.config.suppress_callback_exceptions = True
31
 
 
32
  log_messages = []
33
  generated_pptx = None
34
 
35
  app.layout = html.Div([
36
  dbc.Container([
37
  dbc.Row([
 
38
  dbc.Col([
39
  dbc.Card([
40
  dbc.CardHeader("Upload or Paste Document", className="bg-light"),
 
67
  ])
68
  ], className="mb-4 shadow-sm")
69
  ], md=6),
 
70
  dbc.Col([
71
  dbc.Card([
72
  dbc.CardHeader("Edit Slide Content", className="bg-light"),
 
85
  ])
86
  ], fluid=True, className="mt-4"),
87
  dcc.Download(id="download-pptx"),
88
+ dcc.Interval(id='interval-component', interval=1*1000, n_intervals=0)
89
  ])
90
 
91
  def add_log(message):
92
  global log_messages
93
  logger.info(message)
94
+ log_messages.insert(0, f"{time.strftime('%H:%M:%S')} - {message}")
95
+
96
+ def extract_text_from_docx(docx_bytes):
97
+ doc = docx.Document(io.BytesIO(docx_bytes))
98
+ fullText = []
99
+ for para in doc.paragraphs:
100
+ fullText.append(para.text)
101
+ return '\n'.join(fullText)
102
+
103
+ def extract_text_from_pdf(pdf_bytes):
104
+ pdf_reader = PyPDF2.PdfReader(io.BytesIO(pdf_bytes))
105
+ text = []
106
+ for page in pdf_reader.pages:
107
+ page_text = page.extract_text()
108
+ if page_text:
109
+ text.append(page_text)
110
+ return '\n'.join(text)
111
+
112
+ def process_uploaded_file(filename, file_contents):
113
+ if not filename or not file_contents:
114
+ return ""
115
+ content_type, content_string = file_contents.split(',')
116
+ file_bytes = base64.b64decode(content_string)
117
+ filename_lower = filename.lower()
118
+ if filename_lower.endswith('.txt'):
119
+ try:
120
+ return file_bytes.decode('utf-8')
121
+ except UnicodeDecodeError:
122
+ try:
123
+ return file_bytes.decode('latin1')
124
+ except Exception:
125
+ raise Exception("Could not decode file as UTF-8 or latin1.")
126
+ elif filename_lower.endswith('.docx'):
127
+ try:
128
+ return extract_text_from_docx(file_bytes)
129
+ except Exception as e:
130
+ raise Exception(f"Could not extract text from DOCX: {str(e)}")
131
+ elif filename_lower.endswith('.pdf'):
132
+ try:
133
+ return extract_text_from_pdf(file_bytes)
134
+ except Exception as e:
135
+ raise Exception(f"Could not extract text from PDF: {str(e)}")
136
+ else:
137
+ raise Exception("Unsupported file type. Please upload a .txt, .docx, or .pdf file.")
138
 
139
  def process_document(text):
140
  add_log("Starting document processing...")
 
148
  There can only be one slide heading # and one sub heading ## and no more than 5 bullets with "-" per slide
149
  Document:
150
  {text}
151
+ ---
152
+ The input may be up to 1 million tokens and the output up to 64,000 tokens. Please use as much detail and slides as needed within those limits.
153
  """
154
  add_log("Sending request to Gemini model...")
155
  response = model.generate_content(prompt)
 
182
  "output_format": "png"
183
  }
184
  files = {"none": ''}
 
185
  try:
186
  response = requests.post(url, headers=headers, data=data, files=files)
187
  response.raise_for_status()
 
208
  add_log(f"Creating slide {i+1}/{len(slides)}...")
209
  lines = slide_content.split('\n')
210
  title = lines[0].strip()
211
+ current_slide = prs.slides.add_slide(prs.slide_layouts[6])
 
 
212
  title_box = current_slide.shapes.add_textbox(Inches(0.5), Inches(0.5), Inches(9), Inches(1))
213
  title_box.text_frame.text = title
214
  title_box.text_frame.paragraphs[0].font.size = Pt(32)
215
  title_box.text_frame.paragraphs[0].font.bold = True
 
 
216
  content_box = current_slide.shapes.add_textbox(Inches(0.5), Inches(1.5), Inches(4.5), Inches(6))
217
  text_frame = content_box.text_frame
218
  text_frame.word_wrap = True
 
228
  p.font.size = Pt(18)
229
  p.level = 1
230
  p.bullet = True
 
 
231
  add_log(f"Adding image to slide {i+1}...")
232
  image_data = future.result()
233
  if image_data:
 
247
  add_log(f"Failed to generate image for slide {i+1}: {title}")
248
  placeholder = current_slide.shapes.add_textbox(Inches(5.5), Inches(1.5), Inches(4), Inches(6))
249
  placeholder.text_frame.text = "Image generation failed"
 
250
  add_log("PowerPoint generation completed.")
251
  output = io.BytesIO()
252
  prs.save(output)
 
261
  return html.Div(f"Selected file: {filename}")
262
  return ""
263
 
 
264
  @app.callback(
265
  Output('slides-content', 'value'),
266
  Output('download-ppt-button', 'disabled'),
267
  Input('generate-button', 'n_clicks'),
268
+ State('upload-file', 'filename'),
269
  State('upload-file', 'contents'),
270
  State('document-text', 'value')
271
  )
272
+ def generate_slides(n_clicks, filename, file_contents, document_text):
273
  if n_clicks is None:
274
  raise PreventUpdate
275
 
276
+ if file_contents and filename:
277
+ try:
278
+ document_text = process_uploaded_file(filename, file_contents)
279
+ except Exception as e:
280
+ add_log(f"File error: {str(e)}")
281
+ return f"File error: {str(e)}", True
282
  elif not document_text:
283
  return "Please upload a file or enter text.", True
284
 
 
286
  add_log("Starting slide generation...")
287
  slides_markdown = process_document(document_text)
288
  add_log("Slide generation completed.")
289
+ return slides_markdown, False
290
  except Exception as e:
291
  add_log(f"An error occurred during slide generation: {str(e)}")
292
  return f"An error occurred: {str(e)}", True
 
302
  global generated_pptx
303
  if n_clicks is None or not slides_content:
304
  raise PreventUpdate
 
305
  try:
306
  add_log("Starting PowerPoint generation...")
307
  generated_pptx = markdown_to_pptx(slides_content)
308
  add_log("PowerPoint generation completed.")
309
+ return '\n'.join(log_messages[:100]), False
310
  except Exception as e:
311
  error_message = f"An error occurred during PowerPoint generation: {str(e)}"
312
  add_log(error_message)
 
320
  def download_powerpoint(n_clicks):
321
  if n_clicks is None or generated_pptx is None:
322
  raise PreventUpdate
 
323
  return dcc.send_bytes(generated_pptx, "presentation.pptx")
324
 
325
  @app.callback(