Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -228,109 +228,182 @@ def create_pdf(proposal, output_path="proposal.pdf"):
|
|
228 |
|
229 |
def create_slides(proposal):
|
230 |
"""Create PowerPoint slides from the proposal"""
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
# Set up slide layouts
|
237 |
-
title_slide_layout = prs.slide_layouts[0]
|
238 |
-
section_title_layout = prs.slide_layouts[2]
|
239 |
-
content_layout = prs.slide_layouts[1]
|
240 |
-
|
241 |
-
# Add title slide
|
242 |
-
title_slide = prs.slides.add_slide(title_slide_layout)
|
243 |
-
title_slide.shapes.title.text = f"Project Proposal: {title}"
|
244 |
-
subtitle = title_slide.placeholders[1]
|
245 |
-
subtitle.text = "Professional Project Proposal"
|
246 |
-
|
247 |
-
# List of sections to look for
|
248 |
-
sections = [
|
249 |
-
"Executive Summary",
|
250 |
-
"Project Background",
|
251 |
-
"Goals and Objectives",
|
252 |
-
"Methodology and Approach",
|
253 |
-
"Timeline",
|
254 |
-
"Budget Considerations",
|
255 |
-
"Expected Outcomes",
|
256 |
-
"Team and Resources",
|
257 |
-
"Risk Assessment",
|
258 |
-
"Conclusion"
|
259 |
-
]
|
260 |
-
|
261 |
-
# Extract sections using regex
|
262 |
-
section_matches = re.finditer(r'\*\*\d+\.\s+(.*?)\*\*\n\n(.*?)(?=\*\*\d+\.|\Z)',
|
263 |
-
proposal, re.DOTALL)
|
264 |
-
|
265 |
-
for match in section_matches:
|
266 |
-
section_title = match.group(1)
|
267 |
-
section_content = match.group(2).strip()
|
268 |
|
269 |
-
#
|
270 |
-
|
271 |
-
|
|
|
272 |
|
273 |
-
#
|
274 |
-
|
|
|
|
|
|
|
275 |
|
276 |
-
#
|
277 |
-
|
278 |
-
|
279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
280 |
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
|
|
|
|
|
|
285 |
|
286 |
-
#
|
287 |
-
|
288 |
-
|
289 |
-
current_slide.shapes.title.text = section_title
|
290 |
-
text_frame = current_slide.placeholders[1].text_frame
|
291 |
-
paragraphs_on_slide = 0
|
292 |
|
293 |
-
#
|
294 |
-
|
295 |
|
296 |
-
#
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
for item in bullet_items:
|
301 |
-
item = item.strip()
|
302 |
-
if item:
|
303 |
-
# Remove leading bullet if present
|
304 |
-
item = re.sub(r'^[\*\-]\s+', '', item)
|
305 |
-
bullet_p = text_frame.add_paragraph()
|
306 |
-
bullet_p.text = item
|
307 |
-
bullet_p.level = 1
|
308 |
-
else:
|
309 |
-
p.text = para
|
310 |
|
311 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
312 |
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
317 |
|
318 |
def process_input(description, project_type):
|
319 |
"""Process the input and generate both proposal, PDF and slides"""
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
334 |
|
335 |
# Create Gradio interface
|
336 |
def create_interface():
|
|
|
228 |
|
229 |
def create_slides(proposal):
|
230 |
"""Create PowerPoint slides from the proposal"""
|
231 |
+
try:
|
232 |
+
prs = Presentation()
|
233 |
+
|
234 |
+
# Extract title
|
235 |
+
title = extract_title(proposal)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
|
237 |
+
# Set up slide layouts
|
238 |
+
title_slide_layout = prs.slide_layouts[0]
|
239 |
+
section_title_layout = prs.slide_layouts[2]
|
240 |
+
content_layout = prs.slide_layouts[1]
|
241 |
|
242 |
+
# Add title slide
|
243 |
+
title_slide = prs.slides.add_slide(title_slide_layout)
|
244 |
+
title_slide.shapes.title.text = f"Project Proposal: {title}"
|
245 |
+
subtitle = title_slide.placeholders[1]
|
246 |
+
subtitle.text = "Professional Project Proposal"
|
247 |
|
248 |
+
# List of sections to look for
|
249 |
+
sections = [
|
250 |
+
"Executive Summary",
|
251 |
+
"Project Background",
|
252 |
+
"Goals and Objectives",
|
253 |
+
"Methodology and Approach",
|
254 |
+
"Timeline",
|
255 |
+
"Budget Considerations",
|
256 |
+
"Expected Outcomes",
|
257 |
+
"Team and Resources",
|
258 |
+
"Risk Assessment",
|
259 |
+
"Conclusion"
|
260 |
+
]
|
261 |
|
262 |
+
# Extract sections using regex
|
263 |
+
section_matches = re.finditer(r'\*\*\d+\.\s+(.*?)\*\*\n\n(.*?)(?=\*\*\d+\.|\Z)',
|
264 |
+
proposal, re.DOTALL)
|
265 |
+
|
266 |
+
for match in section_matches:
|
267 |
+
section_title = match.group(1)
|
268 |
+
section_content = match.group(2).strip()
|
269 |
|
270 |
+
# Add section title slide
|
271 |
+
section_slide = prs.slides.add_slide(section_title_layout)
|
272 |
+
section_slide.shapes.title.text = section_title
|
|
|
|
|
|
|
273 |
|
274 |
+
# Split content into paragraphs
|
275 |
+
paragraphs = section_content.split('\n\n')
|
276 |
|
277 |
+
# Process each paragraph
|
278 |
+
current_slide = None
|
279 |
+
text_frame = None
|
280 |
+
paragraphs_on_slide = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
281 |
|
282 |
+
for para in paragraphs:
|
283 |
+
para = para.strip()
|
284 |
+
if not para:
|
285 |
+
continue
|
286 |
+
|
287 |
+
# Start a new slide if needed
|
288 |
+
if current_slide is None or paragraphs_on_slide >= 3:
|
289 |
+
current_slide = prs.slides.add_slide(content_layout)
|
290 |
+
current_slide.shapes.title.text = section_title
|
291 |
+
text_frame = current_slide.placeholders[1].text_frame
|
292 |
+
paragraphs_on_slide = 0
|
293 |
+
|
294 |
+
# Add the paragraph
|
295 |
+
p = text_frame.add_paragraph()
|
296 |
+
|
297 |
+
# Check if it's a bullet point list
|
298 |
+
if re.match(r'^[\*\-]', para):
|
299 |
+
# Process bullet points
|
300 |
+
bullet_items = re.split(r'\n[\*\-]\s+', para)
|
301 |
+
for item in bullet_items:
|
302 |
+
item = item.strip()
|
303 |
+
if item:
|
304 |
+
# Remove leading bullet if present
|
305 |
+
item = re.sub(r'^[\*\-]\s+', '', item)
|
306 |
+
bullet_p = text_frame.add_paragraph()
|
307 |
+
bullet_p.text = item
|
308 |
+
bullet_p.level = 1
|
309 |
+
else:
|
310 |
+
p.text = para
|
311 |
+
|
312 |
+
paragraphs_on_slide += 1
|
313 |
+
|
314 |
+
# Save the presentation
|
315 |
+
output_path = "proposal_slides.pptx"
|
316 |
+
prs.save(output_path)
|
317 |
+
return output_path
|
318 |
|
319 |
+
except Exception as e:
|
320 |
+
print(f"Error creating PowerPoint: {e}")
|
321 |
+
# Create a simple error presentation
|
322 |
+
try:
|
323 |
+
prs = Presentation()
|
324 |
+
slide = prs.slides.add_slide(prs.slide_layouts[0])
|
325 |
+
slide.shapes.title.text = "Error Creating Presentation"
|
326 |
+
subtitle = slide.placeholders[1]
|
327 |
+
subtitle.text = f"An error occurred: {str(e)}\nPlease try again with a different project description."
|
328 |
+
|
329 |
+
output_path = "proposal_slides.pptx"
|
330 |
+
prs.save(output_path)
|
331 |
+
except:
|
332 |
+
# If even the error presentation fails, return a default path
|
333 |
+
pass
|
334 |
+
return output_path
|
335 |
|
336 |
def process_input(description, project_type):
|
337 |
"""Process the input and generate both proposal, PDF and slides"""
|
338 |
+
try:
|
339 |
+
# Check if input is too short
|
340 |
+
if len(description.strip()) < 10:
|
341 |
+
return "Please provide a more detailed project description (at least 10 characters).", None, None
|
342 |
+
|
343 |
+
# Map project type to specific type value
|
344 |
+
type_value = None
|
345 |
+
if project_type == "SaaS Performance Platform":
|
346 |
+
type_value = "saas_performance"
|
347 |
+
|
348 |
+
# Generate the proposal
|
349 |
+
proposal = generate_proposal(description, type_value)
|
350 |
+
|
351 |
+
# Create a basic PDF without any custom styles - fallback approach
|
352 |
+
try:
|
353 |
+
from reportlab.lib.pagesizes import letter
|
354 |
+
from reportlab.pdfgen import canvas
|
355 |
+
|
356 |
+
# Basic PDF generation using canvas directly
|
357 |
+
c = canvas.Canvas("proposal.pdf", pagesize=letter)
|
358 |
+
width, height = letter
|
359 |
+
|
360 |
+
# Title
|
361 |
+
c.setFont("Helvetica-Bold", 16)
|
362 |
+
c.drawCentredString(width/2, height-50, "Project Proposal")
|
363 |
+
|
364 |
+
# Simple text rendering
|
365 |
+
c.setFont("Helvetica", 10)
|
366 |
+
y = height - 80
|
367 |
+
line_height = 14
|
368 |
+
|
369 |
+
lines = proposal.split('\n')
|
370 |
+
for line in lines:
|
371 |
+
# Check for page break
|
372 |
+
if y < 50:
|
373 |
+
c.showPage()
|
374 |
+
y = height - 50
|
375 |
+
|
376 |
+
# Check if it's a header (starts with **)
|
377 |
+
if line.startswith('**'):
|
378 |
+
c.setFont("Helvetica-Bold", 12)
|
379 |
+
# Remove ** markers
|
380 |
+
line = line.replace('**', '')
|
381 |
+
else:
|
382 |
+
c.setFont("Helvetica", 10)
|
383 |
+
|
384 |
+
# Draw the line
|
385 |
+
if line.strip():
|
386 |
+
c.drawString(50, y, line)
|
387 |
+
y -= line_height
|
388 |
+
|
389 |
+
c.save()
|
390 |
+
pdf_path = "proposal.pdf"
|
391 |
+
except Exception as pdf_error:
|
392 |
+
print(f"Error creating PDF: {pdf_error}")
|
393 |
+
pdf_path = None
|
394 |
+
|
395 |
+
# Create the slides
|
396 |
+
try:
|
397 |
+
ppt_path = create_slides(proposal)
|
398 |
+
except Exception as ppt_error:
|
399 |
+
print(f"Error creating slides: {ppt_error}")
|
400 |
+
ppt_path = None
|
401 |
+
|
402 |
+
return proposal, pdf_path, ppt_path
|
403 |
+
except Exception as e:
|
404 |
+
error_message = f"An error occurred: {str(e)}\nPlease try again with a different project description."
|
405 |
+
print(f"Error in process_input: {e}")
|
406 |
+
return error_message, None, None
|
407 |
|
408 |
# Create Gradio interface
|
409 |
def create_interface():
|