Spaces:
Sleeping
Sleeping
update code silde and QA
Browse files- pages/1_π_Text_to_PPT.py +74 -15
pages/1_π_Text_to_PPT.py
CHANGED
@@ -1018,11 +1018,13 @@ from pptx.util import Inches, Pt
|
|
1018 |
from pptx.dml.color import RGBColor
|
1019 |
from pptx.enum.text import PP_ALIGN, PP_PARAGRAPH_ALIGNMENT
|
1020 |
from pptx.enum.text import MSO_AUTO_SIZE
|
|
|
1021 |
from pptx.oxml.xmlchemy import OxmlElement
|
1022 |
import io
|
1023 |
import re
|
1024 |
import tempfile
|
1025 |
import os
|
|
|
1026 |
|
1027 |
# Load the API key securely from environment variable
|
1028 |
api_key = os.getenv("GOOGLE_API_KEY")
|
@@ -1295,10 +1297,30 @@ def parse_slide_content(slide_text):
|
|
1295 |
|
1296 |
return slides, questionnaire, answer_key
|
1297 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1298 |
def create_question_slide(prs, question, question_num, theme):
|
1299 |
"""Create a slide for a single question"""
|
1300 |
slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title + Content layout
|
1301 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1302 |
# Set title
|
1303 |
title = slide.shapes.title
|
1304 |
title.text = f"Question {question_num}"
|
@@ -1310,16 +1332,23 @@ def create_question_slide(prs, question, question_num, theme):
|
|
1310 |
title.text_frame.paragraphs[0].font.bold = True
|
1311 |
if "title_font" in theme:
|
1312 |
title.text_frame.paragraphs[0].font.name = theme["title_font"]
|
|
|
|
|
|
|
1313 |
|
1314 |
# Create content
|
1315 |
-
|
1316 |
-
|
1317 |
-
|
|
|
|
|
|
|
|
|
|
|
1318 |
|
1319 |
# Add question text
|
1320 |
p = tf.add_paragraph()
|
1321 |
p.text = question['text']
|
1322 |
-
p.level = 0
|
1323 |
p.font.bold = True
|
1324 |
p.space_after = Pt(24)
|
1325 |
|
@@ -1335,6 +1364,7 @@ def create_question_slide(prs, question, question_num, theme):
|
|
1335 |
p = tf.add_paragraph()
|
1336 |
p.text = f"{chr(65+j)}. {opt}"
|
1337 |
p.level = 0
|
|
|
1338 |
p.space_after = Pt(12)
|
1339 |
|
1340 |
# Only apply custom formatting if not using a template
|
@@ -1355,6 +1385,12 @@ def create_answer_key_slide(prs, questions, theme):
|
|
1355 |
"""Create answer key slide with all correct answers"""
|
1356 |
slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title + Content layout
|
1357 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1358 |
# Set title
|
1359 |
title = slide.shapes.title
|
1360 |
title.text = "Answer Key"
|
@@ -1366,23 +1402,32 @@ def create_answer_key_slide(prs, questions, theme):
|
|
1366 |
title.text_frame.paragraphs[0].font.bold = True
|
1367 |
if "title_font" in theme:
|
1368 |
title.text_frame.paragraphs[0].font.name = theme["title_font"]
|
|
|
|
|
|
|
1369 |
|
1370 |
# Create content
|
1371 |
-
|
1372 |
-
|
1373 |
-
|
|
|
|
|
|
|
|
|
|
|
1374 |
|
1375 |
# Add answer key
|
1376 |
for i, q in enumerate(questions):
|
1377 |
p = tf.add_paragraph()
|
1378 |
p.text = f"Question {i+1}: {q['correct']}"
|
1379 |
p.level = 0
|
1380 |
-
p.space_after = Pt(
|
1381 |
|
1382 |
# Only apply custom formatting if not using a template
|
1383 |
if "template_path" not in theme:
|
1384 |
p.font.color.rgb = theme["text_color"]
|
1385 |
p.font.size = Pt(24)
|
|
|
1386 |
if "text_font" in theme:
|
1387 |
p.font.name = theme["text_font"]
|
1388 |
|
@@ -1425,6 +1470,12 @@ def create_detailed_pptx(slides_data, questions, theme, branding_options=None):
|
|
1425 |
except IndexError:
|
1426 |
slide = prs.slides.add_slide(prs.slide_layouts[default_layout_idx])
|
1427 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1428 |
# Set title
|
1429 |
title = slide.shapes.title
|
1430 |
title.text = slide_info['title']
|
@@ -1444,11 +1495,9 @@ def create_detailed_pptx(slides_data, questions, theme, branding_options=None):
|
|
1444 |
title.text_frame.paragraphs[0].font.bold = True
|
1445 |
if "title_font" in theme:
|
1446 |
title.text_frame.paragraphs[0].font.name = theme["title_font"]
|
1447 |
-
|
1448 |
-
|
1449 |
-
|
1450 |
-
add_logo_to_slide(slide, branding_options['logo_path'],
|
1451 |
-
branding_options.get('logo_position', 'top-right'))
|
1452 |
|
1453 |
# Set content based on layout
|
1454 |
if layout_idx == 3: # Two column layout
|
@@ -1471,7 +1520,8 @@ def create_detailed_pptx(slides_data, questions, theme, branding_options=None):
|
|
1471 |
point_text = point.replace('- ', '').strip()
|
1472 |
p.text = point_text
|
1473 |
p.level = 0
|
1474 |
-
p.alignment = PP_ALIGN.JUSTIFY
|
|
|
1475 |
if "template_path" not in theme: # Only apply custom formatting if no template
|
1476 |
p.font.color.rgb = theme["text_color"]
|
1477 |
p.font.size = Pt(18)
|
@@ -1488,7 +1538,8 @@ def create_detailed_pptx(slides_data, questions, theme, branding_options=None):
|
|
1488 |
point_text = point.replace('- ', '').strip()
|
1489 |
p.text = point_text
|
1490 |
p.level = 0
|
1491 |
-
p.alignment = PP_ALIGN.JUSTIFY
|
|
|
1492 |
if "template_path" not in theme: # Only apply custom formatting if no template
|
1493 |
p.font.color.rgb = theme["text_color"]
|
1494 |
p.font.size = Pt(18)
|
@@ -1507,6 +1558,11 @@ def create_detailed_pptx(slides_data, questions, theme, branding_options=None):
|
|
1507 |
section_title = section_slide.shapes.title
|
1508 |
section_title.text = "Knowledge Check"
|
1509 |
|
|
|
|
|
|
|
|
|
|
|
1510 |
# Add each question on a separate slide
|
1511 |
for i, question in enumerate(questions, 1):
|
1512 |
create_question_slide(prs, question, i, theme)
|
@@ -1613,6 +1669,8 @@ def main():
|
|
1613 |
|
1614 |
# Show slide overview with detailed content
|
1615 |
with st.expander("Slide Overview (Detailed)"):
|
|
|
|
|
1616 |
for i, slide in enumerate(slides_data, 1):
|
1617 |
st.subheader(f"Slide {i}: {slide['title']}")
|
1618 |
st.markdown("**Content:**")
|
@@ -1624,6 +1682,7 @@ def main():
|
|
1624 |
|
1625 |
if questionnaire:
|
1626 |
st.subheader("Questionnaire")
|
|
|
1627 |
for i, q in enumerate(questionnaire, 1):
|
1628 |
st.subheader(f"Question {i}")
|
1629 |
st.markdown(f"**{q['text']}**")
|
|
|
1018 |
from pptx.dml.color import RGBColor
|
1019 |
from pptx.enum.text import PP_ALIGN, PP_PARAGRAPH_ALIGNMENT
|
1020 |
from pptx.enum.text import MSO_AUTO_SIZE
|
1021 |
+
from pptx.enum.shapes import MSO_SHAPE
|
1022 |
from pptx.oxml.xmlchemy import OxmlElement
|
1023 |
import io
|
1024 |
import re
|
1025 |
import tempfile
|
1026 |
import os
|
1027 |
+
from PIL import Image
|
1028 |
|
1029 |
# Load the API key securely from environment variable
|
1030 |
api_key = os.getenv("GOOGLE_API_KEY")
|
|
|
1297 |
|
1298 |
return slides, questionnaire, answer_key
|
1299 |
|
1300 |
+
def add_title_separator(slide, title_shape, accent_color):
|
1301 |
+
"""Add a decorative line under the title"""
|
1302 |
+
line = slide.shapes.add_shape(
|
1303 |
+
MSO_SHAPE.RECTANGLE,
|
1304 |
+
title_shape.left,
|
1305 |
+
title_shape.top + title_shape.height - Pt(5),
|
1306 |
+
title_shape.width,
|
1307 |
+
Pt(3)
|
1308 |
+
)
|
1309 |
+
fill = line.fill
|
1310 |
+
fill.solid()
|
1311 |
+
fill.fore_color.rgb = accent_color
|
1312 |
+
line.line.fill.background()
|
1313 |
+
|
1314 |
def create_question_slide(prs, question, question_num, theme):
|
1315 |
"""Create a slide for a single question"""
|
1316 |
slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title + Content layout
|
1317 |
|
1318 |
+
# Remove unused placeholders
|
1319 |
+
for shape in slide.shapes:
|
1320 |
+
if shape.has_text_frame and not shape.text.strip() and shape != slide.shapes.title:
|
1321 |
+
sp = shape._element
|
1322 |
+
sp.getparent().remove(sp)
|
1323 |
+
|
1324 |
# Set title
|
1325 |
title = slide.shapes.title
|
1326 |
title.text = f"Question {question_num}"
|
|
|
1332 |
title.text_frame.paragraphs[0].font.bold = True
|
1333 |
if "title_font" in theme:
|
1334 |
title.text_frame.paragraphs[0].font.name = theme["title_font"]
|
1335 |
+
|
1336 |
+
# Add decorative line under title
|
1337 |
+
add_title_separator(slide, title, theme["accent"])
|
1338 |
|
1339 |
# Create content
|
1340 |
+
left = Inches(1)
|
1341 |
+
top = Inches(2)
|
1342 |
+
width = prs.slide_width - Inches(2)
|
1343 |
+
height = prs.slide_height - Inches(3)
|
1344 |
+
|
1345 |
+
content_box = slide.shapes.add_textbox(left, top, width, height)
|
1346 |
+
tf = content_box.text_frame
|
1347 |
+
tf.word_wrap = True
|
1348 |
|
1349 |
# Add question text
|
1350 |
p = tf.add_paragraph()
|
1351 |
p.text = question['text']
|
|
|
1352 |
p.font.bold = True
|
1353 |
p.space_after = Pt(24)
|
1354 |
|
|
|
1364 |
p = tf.add_paragraph()
|
1365 |
p.text = f"{chr(65+j)}. {opt}"
|
1366 |
p.level = 0
|
1367 |
+
p.space_before = Pt(12)
|
1368 |
p.space_after = Pt(12)
|
1369 |
|
1370 |
# Only apply custom formatting if not using a template
|
|
|
1385 |
"""Create answer key slide with all correct answers"""
|
1386 |
slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title + Content layout
|
1387 |
|
1388 |
+
# Remove unused placeholders
|
1389 |
+
for shape in slide.shapes:
|
1390 |
+
if shape.has_text_frame and not shape.text.strip() and shape != slide.shapes.title:
|
1391 |
+
sp = shape._element
|
1392 |
+
sp.getparent().remove(sp)
|
1393 |
+
|
1394 |
# Set title
|
1395 |
title = slide.shapes.title
|
1396 |
title.text = "Answer Key"
|
|
|
1402 |
title.text_frame.paragraphs[0].font.bold = True
|
1403 |
if "title_font" in theme:
|
1404 |
title.text_frame.paragraphs[0].font.name = theme["title_font"]
|
1405 |
+
|
1406 |
+
# Add decorative line under title
|
1407 |
+
add_title_separator(slide, title, theme["accent"])
|
1408 |
|
1409 |
# Create content
|
1410 |
+
left = Inches(1.5)
|
1411 |
+
top = Inches(2)
|
1412 |
+
width = prs.slide_width - Inches(3)
|
1413 |
+
height = prs.slide_height - Inches(3)
|
1414 |
+
|
1415 |
+
content_box = slide.shapes.add_textbox(left, top, width, height)
|
1416 |
+
tf = content_box.text_frame
|
1417 |
+
tf.word_wrap = True
|
1418 |
|
1419 |
# Add answer key
|
1420 |
for i, q in enumerate(questions):
|
1421 |
p = tf.add_paragraph()
|
1422 |
p.text = f"Question {i+1}: {q['correct']}"
|
1423 |
p.level = 0
|
1424 |
+
p.space_after = Pt(18)
|
1425 |
|
1426 |
# Only apply custom formatting if not using a template
|
1427 |
if "template_path" not in theme:
|
1428 |
p.font.color.rgb = theme["text_color"]
|
1429 |
p.font.size = Pt(24)
|
1430 |
+
p.font.bold = True
|
1431 |
if "text_font" in theme:
|
1432 |
p.font.name = theme["text_font"]
|
1433 |
|
|
|
1470 |
except IndexError:
|
1471 |
slide = prs.slides.add_slide(prs.slide_layouts[default_layout_idx])
|
1472 |
|
1473 |
+
# Remove unused placeholders
|
1474 |
+
for shape in slide.shapes:
|
1475 |
+
if shape.has_text_frame and not shape.text.strip() and shape != slide.shapes.title:
|
1476 |
+
sp = shape._element
|
1477 |
+
sp.getparent().remove(sp)
|
1478 |
+
|
1479 |
# Set title
|
1480 |
title = slide.shapes.title
|
1481 |
title.text = slide_info['title']
|
|
|
1495 |
title.text_frame.paragraphs[0].font.bold = True
|
1496 |
if "title_font" in theme:
|
1497 |
title.text_frame.paragraphs[0].font.name = theme["title_font"]
|
1498 |
+
|
1499 |
+
# Add decorative line under title
|
1500 |
+
add_title_separator(slide, title, theme["accent"])
|
|
|
|
|
1501 |
|
1502 |
# Set content based on layout
|
1503 |
if layout_idx == 3: # Two column layout
|
|
|
1520 |
point_text = point.replace('- ', '').strip()
|
1521 |
p.text = point_text
|
1522 |
p.level = 0
|
1523 |
+
p.alignment = PP_ALIGN.JUSTIFY
|
1524 |
+
p.space_after = Pt(12)
|
1525 |
if "template_path" not in theme: # Only apply custom formatting if no template
|
1526 |
p.font.color.rgb = theme["text_color"]
|
1527 |
p.font.size = Pt(18)
|
|
|
1538 |
point_text = point.replace('- ', '').strip()
|
1539 |
p.text = point_text
|
1540 |
p.level = 0
|
1541 |
+
p.alignment = PP_ALIGN.JUSTIFY
|
1542 |
+
p.space_after = Pt(12)
|
1543 |
if "template_path" not in theme: # Only apply custom formatting if no template
|
1544 |
p.font.color.rgb = theme["text_color"]
|
1545 |
p.font.size = Pt(18)
|
|
|
1558 |
section_title = section_slide.shapes.title
|
1559 |
section_title.text = "Knowledge Check"
|
1560 |
|
1561 |
+
# Format section header
|
1562 |
+
if "template_path" not in theme:
|
1563 |
+
section_title.text_frame.paragraphs[0].font.color.rgb = theme["accent"]
|
1564 |
+
section_title.text_frame.paragraphs[0].font.size = Pt(44)
|
1565 |
+
|
1566 |
# Add each question on a separate slide
|
1567 |
for i, question in enumerate(questions, 1):
|
1568 |
create_question_slide(prs, question, i, theme)
|
|
|
1669 |
|
1670 |
# Show slide overview with detailed content
|
1671 |
with st.expander("Slide Overview (Detailed)"):
|
1672 |
+
st.markdown(f"**Total Slides:** {len(slides_data)} content slides + {len(questionnaire)} question slides + 1 answer key slide")
|
1673 |
+
|
1674 |
for i, slide in enumerate(slides_data, 1):
|
1675 |
st.subheader(f"Slide {i}: {slide['title']}")
|
1676 |
st.markdown("**Content:**")
|
|
|
1682 |
|
1683 |
if questionnaire:
|
1684 |
st.subheader("Questionnaire")
|
1685 |
+
st.markdown(f"**Total Questions:** {len(questionnaire)}")
|
1686 |
for i, q in enumerate(questionnaire, 1):
|
1687 |
st.subheader(f"Question {i}")
|
1688 |
st.markdown(f"**{q['text']}**")
|