Spaces:
Sleeping
Sleeping
update foramete
Browse files- pages/1_π_Text_to_PPT.py +118 -68
pages/1_π_Text_to_PPT.py
CHANGED
@@ -1326,32 +1326,38 @@ def add_title_separator(slide, title_shape, accent_color):
|
|
1326 |
|
1327 |
def create_question_slide(prs, question, question_num, theme):
|
1328 |
"""Create a slide for a single question"""
|
1329 |
-
|
|
|
|
|
|
|
|
|
1330 |
|
1331 |
# Remove unused placeholders
|
1332 |
for shape in slide.shapes:
|
1333 |
-
if shape.has_text_frame and not shape.text.strip() and shape != slide
|
1334 |
sp = shape._element
|
1335 |
sp.getparent().remove(sp)
|
1336 |
|
1337 |
# Set title
|
1338 |
-
title = slide.shapes.title
|
1339 |
-
title
|
|
|
1340 |
|
1341 |
# Only apply custom formatting if not using a template
|
1342 |
if "template_path" not in theme:
|
1343 |
-
title
|
1344 |
-
|
1345 |
-
|
1346 |
-
|
1347 |
-
|
1348 |
-
|
1349 |
-
|
1350 |
-
|
|
|
1351 |
|
1352 |
# Create content
|
1353 |
left = Inches(1)
|
1354 |
-
top = Inches(2)
|
1355 |
width = prs.slide_width - Inches(2)
|
1356 |
height = prs.slide_height - Inches(3)
|
1357 |
|
@@ -1388,40 +1394,48 @@ def create_question_slide(prs, question, question_num, theme):
|
|
1388 |
p.font.name = theme["text_font"]
|
1389 |
|
1390 |
# Add correct answer to speaker notes
|
1391 |
-
|
1392 |
-
|
1393 |
-
|
|
|
|
|
1394 |
|
1395 |
return slide
|
1396 |
|
1397 |
def create_answer_key_slide(prs, questions, theme):
|
1398 |
"""Create answer key slide with all correct answers"""
|
1399 |
-
|
|
|
|
|
|
|
|
|
1400 |
|
1401 |
# Remove unused placeholders
|
1402 |
for shape in slide.shapes:
|
1403 |
-
if shape.has_text_frame and not shape.text.strip() and shape != slide
|
1404 |
sp = shape._element
|
1405 |
sp.getparent().remove(sp)
|
1406 |
|
1407 |
# Set title
|
1408 |
-
title = slide.shapes.title
|
1409 |
-
title
|
|
|
1410 |
|
1411 |
# Only apply custom formatting if not using a template
|
1412 |
if "template_path" not in theme:
|
1413 |
-
title
|
1414 |
-
|
1415 |
-
|
1416 |
-
|
1417 |
-
|
1418 |
-
|
1419 |
-
|
1420 |
-
|
|
|
1421 |
|
1422 |
# Create content
|
1423 |
left = Inches(1.5)
|
1424 |
-
top = Inches(2)
|
1425 |
width = prs.slide_width - Inches(3)
|
1426 |
height = prs.slide_height - Inches(3)
|
1427 |
|
@@ -1486,14 +1500,43 @@ def create_detailed_pptx(slides_data, questions, theme, branding_options=None, p
|
|
1486 |
|
1487 |
# Remove unused placeholders
|
1488 |
for shape in slide.shapes:
|
1489 |
-
if shape.has_text_frame and not shape.text.strip() and shape != slide
|
1490 |
sp = shape._element
|
1491 |
sp.getparent().remove(sp)
|
1492 |
|
1493 |
-
# Set title
|
1494 |
-
|
1495 |
-
|
1496 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1497 |
|
1498 |
# Only apply custom formatting if not using a template
|
1499 |
if "template_path" not in theme:
|
@@ -1502,31 +1545,26 @@ def create_detailed_pptx(slides_data, questions, theme, branding_options=None, p
|
|
1502 |
fill = background.fill
|
1503 |
fill.solid()
|
1504 |
fill.fore_color.rgb = theme["background"]
|
1505 |
-
|
1506 |
-
# Format title
|
1507 |
-
title.text_frame.paragraphs[0].font.color.rgb = theme["title_color"]
|
1508 |
-
title.text_frame.paragraphs[0].font.size = Pt(36)
|
1509 |
-
title.text_frame.paragraphs[0].font.bold = True
|
1510 |
-
if "title_font" in theme:
|
1511 |
-
title.text_frame.paragraphs[0].font.name = theme["title_font"]
|
1512 |
-
|
1513 |
-
# Add decorative line under title
|
1514 |
-
add_title_separator(slide, title, theme["accent"])
|
1515 |
|
1516 |
# Set content based on layout
|
1517 |
-
if
|
1518 |
content = slide_info.get('content', [])
|
1519 |
mid_point = len(content) // 2
|
1520 |
left_content = content[:mid_point]
|
1521 |
right_content = content[mid_point:]
|
1522 |
|
1523 |
-
|
1524 |
-
|
1525 |
-
|
|
|
|
|
|
|
|
|
1526 |
|
1527 |
-
|
1528 |
-
|
1529 |
-
|
|
|
1530 |
|
1531 |
for content_part, tf in [(left_content, left_tf), (right_content, right_tf)]:
|
1532 |
for point in content_part:
|
@@ -1542,10 +1580,14 @@ def create_detailed_pptx(slides_data, questions, theme, branding_options=None, p
|
|
1542 |
if "text_font" in theme:
|
1543 |
p.font.name = theme["text_font"]
|
1544 |
|
1545 |
-
elif
|
1546 |
-
|
1547 |
-
|
1548 |
-
|
|
|
|
|
|
|
|
|
1549 |
|
1550 |
for point in slide_info.get('content', []):
|
1551 |
p = tf.add_paragraph()
|
@@ -1561,21 +1603,27 @@ def create_detailed_pptx(slides_data, questions, theme, branding_options=None, p
|
|
1561 |
p.font.name = theme["text_font"]
|
1562 |
|
1563 |
# Add notes if available
|
1564 |
-
if slide_info.get('notes'):
|
1565 |
notes_slide = slide.notes_slide
|
1566 |
-
notes_slide
|
|
|
1567 |
|
1568 |
# Add question slides (one per question)
|
1569 |
if questions:
|
1570 |
# Add section header
|
1571 |
-
|
1572 |
-
|
1573 |
-
|
|
|
1574 |
|
1575 |
-
|
1576 |
-
|
1577 |
-
section_title.
|
1578 |
-
|
|
|
|
|
|
|
|
|
1579 |
|
1580 |
# Add each question on a separate slide
|
1581 |
for i, question in enumerate(questions, 1):
|
@@ -1771,10 +1819,11 @@ def main():
|
|
1771 |
st.session_state.pptx_file = pptx_file
|
1772 |
|
1773 |
progress_bar.progress(100)
|
1774 |
-
|
1775 |
-
|
|
|
1776 |
progress_bar.empty()
|
1777 |
-
|
1778 |
|
1779 |
st.success("Presentation generated successfully!")
|
1780 |
|
@@ -1834,6 +1883,7 @@ def main():
|
|
1834 |
ppt_version=ppt_version
|
1835 |
)
|
1836 |
st.session_state.pptx_file = pptx_file
|
|
|
1837 |
st.success("Presentation updated with regenerated slides!")
|
1838 |
|
1839 |
# Download button
|
@@ -1841,7 +1891,7 @@ def main():
|
|
1841 |
st.download_button(
|
1842 |
label="Download PowerPoint",
|
1843 |
data=st.session_state.pptx_file,
|
1844 |
-
file_name=f"{topic.replace(' ', '_')}_presentation.pptx",
|
1845 |
mime="application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
1846 |
)
|
1847 |
|
|
|
1326 |
|
1327 |
def create_question_slide(prs, question, question_num, theme):
|
1328 |
"""Create a slide for a single question"""
|
1329 |
+
# Try to use Title + Content layout, fallback to blank if not available
|
1330 |
+
try:
|
1331 |
+
slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title + Content layout
|
1332 |
+
except IndexError:
|
1333 |
+
slide = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout
|
1334 |
|
1335 |
# Remove unused placeholders
|
1336 |
for shape in slide.shapes:
|
1337 |
+
if shape.has_text_frame and not shape.text.strip() and shape != getattr(slide, 'shapes', None):
|
1338 |
sp = shape._element
|
1339 |
sp.getparent().remove(sp)
|
1340 |
|
1341 |
# Set title
|
1342 |
+
title = slide.shapes.title if hasattr(slide, 'shapes') and hasattr(slide.shapes, 'title') else None
|
1343 |
+
if title:
|
1344 |
+
title.text = f"Question {question_num}"
|
1345 |
|
1346 |
# Only apply custom formatting if not using a template
|
1347 |
if "template_path" not in theme:
|
1348 |
+
if title:
|
1349 |
+
title.text_frame.paragraphs[0].font.color.rgb = theme["accent"]
|
1350 |
+
title.text_frame.paragraphs[0].font.size = Pt(36)
|
1351 |
+
title.text_frame.paragraphs[0].font.bold = True
|
1352 |
+
if "title_font" in theme:
|
1353 |
+
title.text_frame.paragraphs[0].font.name = theme["title_font"]
|
1354 |
+
|
1355 |
+
# Add decorative line under title
|
1356 |
+
add_title_separator(slide, title, theme["accent"])
|
1357 |
|
1358 |
# Create content
|
1359 |
left = Inches(1)
|
1360 |
+
top = Inches(2) if title else Inches(0.5)
|
1361 |
width = prs.slide_width - Inches(2)
|
1362 |
height = prs.slide_height - Inches(3)
|
1363 |
|
|
|
1394 |
p.font.name = theme["text_font"]
|
1395 |
|
1396 |
# Add correct answer to speaker notes
|
1397 |
+
if hasattr(slide, 'notes_slide'):
|
1398 |
+
notes_slide = slide.notes_slide
|
1399 |
+
notes_text = f"Correct Answer: {question['correct']}\n"
|
1400 |
+
if hasattr(notes_slide, 'notes_text_frame'):
|
1401 |
+
notes_slide.notes_text_frame.text = notes_text
|
1402 |
|
1403 |
return slide
|
1404 |
|
1405 |
def create_answer_key_slide(prs, questions, theme):
|
1406 |
"""Create answer key slide with all correct answers"""
|
1407 |
+
# Try to use Title + Content layout, fallback to blank if not available
|
1408 |
+
try:
|
1409 |
+
slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title + Content layout
|
1410 |
+
except IndexError:
|
1411 |
+
slide = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout
|
1412 |
|
1413 |
# Remove unused placeholders
|
1414 |
for shape in slide.shapes:
|
1415 |
+
if shape.has_text_frame and not shape.text.strip() and shape != getattr(slide, 'shapes', None):
|
1416 |
sp = shape._element
|
1417 |
sp.getparent().remove(sp)
|
1418 |
|
1419 |
# Set title
|
1420 |
+
title = slide.shapes.title if hasattr(slide, 'shapes') and hasattr(slide.shapes, 'title') else None
|
1421 |
+
if title:
|
1422 |
+
title.text = "Answer Key"
|
1423 |
|
1424 |
# Only apply custom formatting if not using a template
|
1425 |
if "template_path" not in theme:
|
1426 |
+
if title:
|
1427 |
+
title.text_frame.paragraphs[0].font.color.rgb = theme["accent"]
|
1428 |
+
title.text_frame.paragraphs[0].font.size = Pt(36)
|
1429 |
+
title.text_frame.paragraphs[0].font.bold = True
|
1430 |
+
if "title_font" in theme:
|
1431 |
+
title.text_frame.paragraphs[0].font.name = theme["title_font"]
|
1432 |
+
|
1433 |
+
# Add decorative line under title
|
1434 |
+
add_title_separator(slide, title, theme["accent"])
|
1435 |
|
1436 |
# Create content
|
1437 |
left = Inches(1.5)
|
1438 |
+
top = Inches(2) if title else Inches(0.5)
|
1439 |
width = prs.slide_width - Inches(3)
|
1440 |
height = prs.slide_height - Inches(3)
|
1441 |
|
|
|
1500 |
|
1501 |
# Remove unused placeholders
|
1502 |
for shape in slide.shapes:
|
1503 |
+
if shape.has_text_frame and not shape.text.strip() and shape != getattr(slide, 'shapes', None):
|
1504 |
sp = shape._element
|
1505 |
sp.getparent().remove(sp)
|
1506 |
|
1507 |
+
# Set title if placeholder exists
|
1508 |
+
if hasattr(slide, 'shapes') and hasattr(slide.shapes, 'title'):
|
1509 |
+
title = slide.shapes.title
|
1510 |
+
title.text = slide_info['title']
|
1511 |
+
title.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER
|
1512 |
+
|
1513 |
+
# Only apply custom formatting if not using a template
|
1514 |
+
if "template_path" not in theme:
|
1515 |
+
# Format title
|
1516 |
+
title.text_frame.paragraphs[0].font.color.rgb = theme["title_color"]
|
1517 |
+
title.text_frame.paragraphs[0].font.size = Pt(36)
|
1518 |
+
title.text_frame.paragraphs[0].font.bold = True
|
1519 |
+
if "title_font" in theme:
|
1520 |
+
title.text_frame.paragraphs[0].font.name = theme["title_font"]
|
1521 |
+
|
1522 |
+
# Add decorative line under title
|
1523 |
+
add_title_separator(slide, title, theme["accent"])
|
1524 |
+
else:
|
1525 |
+
# Add title manually if no placeholder
|
1526 |
+
left = Inches(1)
|
1527 |
+
top = Inches(0.5)
|
1528 |
+
width = prs.slide_width - Inches(2)
|
1529 |
+
height = Inches(1)
|
1530 |
+
title_box = slide.shapes.add_textbox(left, top, width, height)
|
1531 |
+
title_tf = title_box.text_frame
|
1532 |
+
p = title_tf.add_paragraph()
|
1533 |
+
p.text = slide_info['title']
|
1534 |
+
p.alignment = PP_ALIGN.CENTER
|
1535 |
+
p.font.bold = True
|
1536 |
+
p.font.size = Pt(36)
|
1537 |
+
if "title_font" in theme:
|
1538 |
+
p.font.name = theme["title_font"]
|
1539 |
+
p.font.color.rgb = theme["title_color"]
|
1540 |
|
1541 |
# Only apply custom formatting if not using a template
|
1542 |
if "template_path" not in theme:
|
|
|
1545 |
fill = background.fill
|
1546 |
fill.solid()
|
1547 |
fill.fore_color.rgb = theme["background"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1548 |
|
1549 |
# Set content based on layout
|
1550 |
+
if layout == 'two-column':
|
1551 |
content = slide_info.get('content', [])
|
1552 |
mid_point = len(content) // 2
|
1553 |
left_content = content[:mid_point]
|
1554 |
right_content = content[mid_point:]
|
1555 |
|
1556 |
+
# Create left column
|
1557 |
+
left = Inches(0.5)
|
1558 |
+
top = Inches(2)
|
1559 |
+
width = prs.slide_width / 2 - Inches(1)
|
1560 |
+
height = prs.slide_height - Inches(3)
|
1561 |
+
left_box = slide.shapes.add_textbox(left, top, width, height)
|
1562 |
+
left_tf = left_box.text_frame
|
1563 |
|
1564 |
+
# Create right column
|
1565 |
+
left = prs.slide_width / 2 + Inches(0.5)
|
1566 |
+
right_box = slide.shapes.add_textbox(left, top, width, height)
|
1567 |
+
right_tf = right_box.text_frame
|
1568 |
|
1569 |
for content_part, tf in [(left_content, left_tf), (right_content, right_tf)]:
|
1570 |
for point in content_part:
|
|
|
1580 |
if "text_font" in theme:
|
1581 |
p.font.name = theme["text_font"]
|
1582 |
|
1583 |
+
elif layout != 'title-only': # Content slides
|
1584 |
+
# Create content area
|
1585 |
+
left = Inches(1)
|
1586 |
+
top = Inches(2)
|
1587 |
+
width = prs.slide_width - Inches(2)
|
1588 |
+
height = prs.slide_height - Inches(3)
|
1589 |
+
content_box = slide.shapes.add_textbox(left, top, width, height)
|
1590 |
+
tf = content_box.text_frame
|
1591 |
|
1592 |
for point in slide_info.get('content', []):
|
1593 |
p = tf.add_paragraph()
|
|
|
1603 |
p.font.name = theme["text_font"]
|
1604 |
|
1605 |
# Add notes if available
|
1606 |
+
if slide_info.get('notes') and hasattr(slide, 'notes_slide'):
|
1607 |
notes_slide = slide.notes_slide
|
1608 |
+
if hasattr(notes_slide, 'notes_text_frame'):
|
1609 |
+
notes_slide.notes_text_frame.text = slide_info['notes']
|
1610 |
|
1611 |
# Add question slides (one per question)
|
1612 |
if questions:
|
1613 |
# Add section header
|
1614 |
+
try:
|
1615 |
+
section_slide = prs.slides.add_slide(prs.slide_layouts[2]) # Section header layout
|
1616 |
+
except IndexError:
|
1617 |
+
section_slide = prs.slides.add_slide(prs.slide_layouts[0]) # Fallback to title slide
|
1618 |
|
1619 |
+
if hasattr(section_slide, 'shapes') and hasattr(section_slide.shapes, 'title'):
|
1620 |
+
section_title = section_slide.shapes.title
|
1621 |
+
section_title.text = "Knowledge Check"
|
1622 |
+
|
1623 |
+
# Format section header
|
1624 |
+
if "template_path" not in theme:
|
1625 |
+
section_title.text_frame.paragraphs[0].font.color.rgb = theme["accent"]
|
1626 |
+
section_title.text_frame.paragraphs[0].font.size = Pt(44)
|
1627 |
|
1628 |
# Add each question on a separate slide
|
1629 |
for i, question in enumerate(questions, 1):
|
|
|
1819 |
st.session_state.pptx_file = pptx_file
|
1820 |
|
1821 |
progress_bar.progress(100)
|
1822 |
+
status_bar = st.empty()
|
1823 |
+
status_bar.success("Done!")
|
1824 |
+
time.sleep(1)
|
1825 |
progress_bar.empty()
|
1826 |
+
status_bar.empty()
|
1827 |
|
1828 |
st.success("Presentation generated successfully!")
|
1829 |
|
|
|
1883 |
ppt_version=ppt_version
|
1884 |
)
|
1885 |
st.session_state.pptx_file = pptx_file
|
1886 |
+
st.session_state.regenerate_map = {i: False for i in range(len(st.session_state.slides_data))}
|
1887 |
st.success("Presentation updated with regenerated slides!")
|
1888 |
|
1889 |
# Download button
|
|
|
1891 |
st.download_button(
|
1892 |
label="Download PowerPoint",
|
1893 |
data=st.session_state.pptx_file,
|
1894 |
+
file_name=f"{topic.replace(' ', '_') if topic else 'presentation'}_presentation.pptx",
|
1895 |
mime="application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
1896 |
)
|
1897 |
|