Spaces:
Sleeping
Sleeping
update page slide in QA
Browse files- pages/1_π_Text_to_PPT.py +686 -40
pages/1_π_Text_to_PPT.py
CHANGED
@@ -435,6 +435,581 @@
|
|
435 |
# if __name__ == "__main__":
|
436 |
# main()
|
437 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
438 |
|
439 |
import streamlit as st
|
440 |
import google.generativeai as genai
|
@@ -608,7 +1183,17 @@ def generate_slide_content(topic, slide_count):
|
|
608 |
- Clearly indicate the correct answer
|
609 |
- Questions test comprehension of the presentation content
|
610 |
|
611 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
612 |
|
613 |
Begin the content generation now.
|
614 |
"""
|
@@ -620,11 +1205,17 @@ def parse_slide_content(slide_text):
|
|
620 |
slides = []
|
621 |
current_slide = {}
|
622 |
questionnaire = []
|
|
|
623 |
|
624 |
-
# Split into
|
625 |
parts = slide_text.split('--- QUESTIONNAIRE ---')
|
626 |
main_content = parts[0].strip()
|
627 |
-
|
|
|
|
|
|
|
|
|
|
|
628 |
|
629 |
# Parse main slides
|
630 |
for line in main_content.split('\n'):
|
@@ -692,15 +1283,25 @@ def parse_slide_content(slide_text):
|
|
692 |
if current_question:
|
693 |
questionnaire.append(current_question)
|
694 |
|
695 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
696 |
|
697 |
-
def
|
698 |
-
"""Create
|
699 |
slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title + Content layout
|
700 |
|
701 |
# Set title
|
702 |
title = slide.shapes.title
|
703 |
-
title.text = "
|
704 |
|
705 |
# Only apply custom formatting if not using a template
|
706 |
if "template_path" not in theme:
|
@@ -715,13 +1316,26 @@ def create_questionnaire_slide(prs, questions, theme):
|
|
715 |
tf = body.text_frame
|
716 |
tf.clear()
|
717 |
|
718 |
-
|
719 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
720 |
p = tf.add_paragraph()
|
721 |
-
p.text = f"{
|
722 |
p.level = 0
|
723 |
-
p.
|
724 |
-
p.space_after = Pt(8)
|
725 |
|
726 |
# Only apply custom formatting if not using a template
|
727 |
if "template_path" not in theme:
|
@@ -729,35 +1343,51 @@ def create_questionnaire_slide(prs, questions, theme):
|
|
729 |
p.font.size = Pt(20)
|
730 |
if "text_font" in theme:
|
731 |
p.font.name = theme["text_font"]
|
732 |
-
|
733 |
-
# Add options
|
734 |
-
for j, opt in enumerate(q['options']):
|
735 |
-
p = tf.add_paragraph()
|
736 |
-
p.text = f" {chr(65+j)}. {opt}"
|
737 |
-
p.level = 1
|
738 |
-
p.space_after = Pt(4)
|
739 |
-
|
740 |
-
# Only apply custom formatting if not using a template
|
741 |
-
if "template_path" not in theme:
|
742 |
-
p.font.color.rgb = theme["text_color"]
|
743 |
-
p.font.size = Pt(18)
|
744 |
-
if "text_font" in theme:
|
745 |
-
p.font.name = theme["text_font"]
|
746 |
-
|
747 |
-
# Add space between questions
|
748 |
-
p = tf.add_paragraph()
|
749 |
-
p.text = ""
|
750 |
-
p.space_after = Pt(16)
|
751 |
|
752 |
-
# Add correct
|
753 |
notes_slide = slide.notes_slide
|
754 |
-
notes_text = "Correct
|
755 |
-
for i, q in enumerate(questions):
|
756 |
-
notes_text += f"{i+1}. {q['correct']}\n"
|
757 |
notes_slide.notes_text_frame.text = notes_text
|
758 |
|
759 |
return slide
|
760 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
761 |
def create_detailed_pptx(slides_data, questions, theme, branding_options=None):
|
762 |
"""Create PowerPoint using the uploaded template"""
|
763 |
# Use the template if one was uploaded
|
@@ -870,9 +1500,19 @@ def create_detailed_pptx(slides_data, questions, theme, branding_options=None):
|
|
870 |
notes_slide = slide.notes_slide
|
871 |
notes_slide.notes_text_frame.text = slide_info['notes']
|
872 |
|
873 |
-
# Add
|
874 |
if questions:
|
875 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
876 |
|
877 |
pptx_io = io.BytesIO()
|
878 |
prs.save(pptx_io)
|
@@ -969,7 +1609,7 @@ def main():
|
|
969 |
with st.spinner(f"Creating {slide_count}-slide presentation about '{topic}'..."):
|
970 |
try:
|
971 |
slide_text = generate_slide_content(topic, slide_count)
|
972 |
-
slides_data, questionnaire = parse_slide_content(slide_text)
|
973 |
|
974 |
# Show slide overview with detailed content
|
975 |
with st.expander("Slide Overview (Detailed)"):
|
@@ -985,11 +1625,17 @@ def main():
|
|
985 |
if questionnaire:
|
986 |
st.subheader("Questionnaire")
|
987 |
for i, q in enumerate(questionnaire, 1):
|
988 |
-
st.
|
|
|
989 |
for j, opt in enumerate(q['options']):
|
990 |
-
st.markdown(f"
|
991 |
st.markdown(f"*Correct: {q['correct']}*")
|
992 |
st.markdown("---")
|
|
|
|
|
|
|
|
|
|
|
993 |
|
994 |
pptx_file = create_detailed_pptx(slides_data, questionnaire, theme)
|
995 |
|
|
|
435 |
# if __name__ == "__main__":
|
436 |
# main()
|
437 |
|
438 |
+
######################################################################################################################################################################
|
439 |
+
# import streamlit as st
|
440 |
+
# import google.generativeai as genai
|
441 |
+
# from pptx import Presentation
|
442 |
+
# from pptx.util import Inches, Pt
|
443 |
+
# from pptx.dml.color import RGBColor
|
444 |
+
# from pptx.enum.text import PP_ALIGN, PP_PARAGRAPH_ALIGNMENT
|
445 |
+
# from pptx.enum.text import MSO_AUTO_SIZE
|
446 |
+
# from pptx.oxml.xmlchemy import OxmlElement
|
447 |
+
# import io
|
448 |
+
# import re
|
449 |
+
# import tempfile
|
450 |
+
# import os
|
451 |
+
|
452 |
+
# # Load the API key securely from environment variable
|
453 |
+
# api_key = os.getenv("GOOGLE_API_KEY")
|
454 |
+
# if not api_key:
|
455 |
+
# st.error("GOOGLE_API_KEY environment variable not set!")
|
456 |
+
# st.stop()
|
457 |
+
# genai.configure(api_key=api_key)
|
458 |
+
|
459 |
+
# # Default theme configurations
|
460 |
+
# DEFAULT_THEMES = {
|
461 |
+
# "Professional Blue": {
|
462 |
+
# "background": RGBColor(12, 35, 64),
|
463 |
+
# "title_color": RGBColor(255, 255, 255),
|
464 |
+
# "text_color": RGBColor(200, 200, 200),
|
465 |
+
# "accent": RGBColor(0, 112, 192),
|
466 |
+
# "title_font": "Calibri",
|
467 |
+
# "text_font": "Calibri"
|
468 |
+
# },
|
469 |
+
# "Modern Green": {
|
470 |
+
# "background": RGBColor(22, 82, 66),
|
471 |
+
# "title_color": RGBColor(255, 255, 255),
|
472 |
+
# "text_color": RGBColor(220, 220, 220),
|
473 |
+
# "accent": RGBColor(76, 175, 80),
|
474 |
+
# "title_font": "Arial",
|
475 |
+
# "text_font": "Arial"
|
476 |
+
# },
|
477 |
+
# "Light Corporate": {
|
478 |
+
# "background": RGBColor(255, 255, 255),
|
479 |
+
# "title_color": RGBColor(13, 71, 161),
|
480 |
+
# "text_color": RGBColor(33, 33, 33),
|
481 |
+
# "accent": RGBColor(25, 118, 210),
|
482 |
+
# "title_font": "Segoe UI",
|
483 |
+
# "text_font": "Segoe UI"
|
484 |
+
# },
|
485 |
+
# "Dark Tech": {
|
486 |
+
# "background": RGBColor(33, 33, 33),
|
487 |
+
# "title_color": RGBColor(0, 200, 255),
|
488 |
+
# "text_color": RGBColor(200, 200, 200),
|
489 |
+
# "accent": RGBColor(0, 150, 255),
|
490 |
+
# "title_font": "Consolas",
|
491 |
+
# "text_font": "Consolas"
|
492 |
+
# }
|
493 |
+
# }
|
494 |
+
|
495 |
+
# def hex_to_rgb(hex_color):
|
496 |
+
# """Convert hex color to RGBColor"""
|
497 |
+
# hex_color = hex_color.lstrip('#')
|
498 |
+
# return RGBColor(*tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)))
|
499 |
+
|
500 |
+
# def extract_theme_from_pptx(uploaded_file):
|
501 |
+
# """Extract theme colors and fonts from an uploaded PowerPoint file"""
|
502 |
+
# with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
|
503 |
+
# tmp_file.write(uploaded_file.read())
|
504 |
+
# tmp_file_path = tmp_file.name
|
505 |
+
|
506 |
+
# prs = Presentation(tmp_file_path)
|
507 |
+
# theme = {
|
508 |
+
# "background": RGBColor(255, 255, 255), # Default white if not found
|
509 |
+
# "title_color": RGBColor(0, 0, 0), # Default black
|
510 |
+
# "text_color": RGBColor(0, 0, 0), # Default black
|
511 |
+
# "accent": RGBColor(79, 129, 189), # Default blue
|
512 |
+
# "title_font": "Calibri",
|
513 |
+
# "text_font": "Calibri",
|
514 |
+
# "template_path": tmp_file_path # Store the template path for later use
|
515 |
+
# }
|
516 |
+
|
517 |
+
# try:
|
518 |
+
# # Get colors from master slide
|
519 |
+
# slide_master = prs.slide_master
|
520 |
+
|
521 |
+
# # Handle background color
|
522 |
+
# if hasattr(slide_master.background, 'fill'):
|
523 |
+
# if slide_master.background.fill.type == 1: # Solid fill
|
524 |
+
# theme["background"] = slide_master.background.fill.fore_color.rgb
|
525 |
+
|
526 |
+
# # Try to get title and text colors from placeholders
|
527 |
+
# for shape in slide_master.shapes:
|
528 |
+
# if shape.has_text_frame and shape.text.strip():
|
529 |
+
# try:
|
530 |
+
# if 'title' in shape.name.lower():
|
531 |
+
# theme["title_color"] = shape.text_frame.paragraphs[0].font.color.rgb
|
532 |
+
# theme["title_font"] = shape.text_frame.paragraphs[0].font.name
|
533 |
+
# else:
|
534 |
+
# theme["text_color"] = shape.text_frame.paragraphs[0].font.color.rgb
|
535 |
+
# theme["text_font"] = shape.text_frame.paragraphs[0].font.name
|
536 |
+
# except:
|
537 |
+
# continue
|
538 |
+
|
539 |
+
# # Try to get accent color from first shape with fill
|
540 |
+
# for shape in slide_master.shapes:
|
541 |
+
# if hasattr(shape, 'fill'):
|
542 |
+
# try:
|
543 |
+
# if shape.fill.type == 1: # Solid fill
|
544 |
+
# theme["accent"] = shape.fill.fore_color.rgb
|
545 |
+
# break
|
546 |
+
# except:
|
547 |
+
# continue
|
548 |
+
|
549 |
+
# except Exception as e:
|
550 |
+
# st.warning(f"Couldn't fully extract theme: {str(e)}. Using default colors where needed.")
|
551 |
+
|
552 |
+
# return theme
|
553 |
+
|
554 |
+
# def generate_slide_content(topic, slide_count):
|
555 |
+
# model = genai.GenerativeModel('gemini-2.0-flash')
|
556 |
+
# prompt = f"""Create a comprehensive presentation on '{topic}' with exactly {slide_count} slides.
|
557 |
+
# For each slide, provide:
|
558 |
+
# 1. A clear title in [Title:] format
|
559 |
+
# 2. 3-5 detailed bullet points in [Content:] format (each point should be 2-3 lines/40-60 words)
|
560 |
+
# 3. Optional speaker notes in [Notes:] format
|
561 |
+
# 4. Layout suggestion in [Layout:] format (title-only, title-content, two-column, section-header)
|
562 |
+
|
563 |
+
# Structure your response like this:
|
564 |
+
# [Title:] Slide Title
|
565 |
+
# [Layout:] title-content
|
566 |
+
# [Content:]
|
567 |
+
# - Main Point 1: Detailed explanation spanning 1-2 lines with supporting information that provides context and value to the audience. This makes each point substantial.
|
568 |
+
# - Main Point 2: Another complete thought with sufficient detail to stand alone as a mini-paragraph, giving the audience concrete information they can use.
|
569 |
+
# - Main Point 3: Final point with enough depth to be meaningful, typically consisting of 2-3 sentences that develop a complete idea.
|
570 |
+
# [Notes:] Additional notes here
|
571 |
+
|
572 |
+
# Important guidelines:
|
573 |
+
# - Do not give only placeholders or labels. Write full, rich content for each bullet point.
|
574 |
+
# - Apply the same for the Questionnaire slide too, don't just keep placeholders, generate 10 questions.
|
575 |
+
# - Avoid general outlines β generate fully written content as if it's going directly into a slide.
|
576 |
+
# - Each bullet point should be 2-3 lines (30-50 words)
|
577 |
+
# - Provide complete thoughts with supporting details
|
578 |
+
# - Maintain parallel structure across points
|
579 |
+
# - Avoid single-sentence bullet points
|
580 |
+
# - Focus on substance over brevity
|
581 |
+
|
582 |
+
# Include these sections (adjust based on requested slide count):
|
583 |
+
# - Title slide
|
584 |
+
# - Introduction/Overview
|
585 |
+
# - Key Concepts
|
586 |
+
# - Detailed Analysis
|
587 |
+
# - Case Studies/Examples
|
588 |
+
# - Applications
|
589 |
+
# - Challenges
|
590 |
+
# - Future Trends
|
591 |
+
# - Conclusion
|
592 |
+
|
593 |
+
# After generating the content for the above presentation sections, add a clear separator:
|
594 |
+
# --- QUESTIONNAIRE ---
|
595 |
+
|
596 |
+
# Then generate 10 multiple-choice questions testing the user's understanding of the presentation:
|
597 |
+
# Format each question like:
|
598 |
+
# [Question: Your question text here?]
|
599 |
+
# - [A] Option A
|
600 |
+
# - [B] Option B
|
601 |
+
# - [C] Option C
|
602 |
+
# - [D] Option D
|
603 |
+
# [Correct: C]
|
604 |
+
|
605 |
+
# Ensure:
|
606 |
+
# - Questions cover all key areas: introduction, concepts, analysis, case studies, applications, challenges, and future trends
|
607 |
+
# - Each question has exactly 4 options
|
608 |
+
# - Clearly indicate the correct answer
|
609 |
+
# - Questions test comprehension of the presentation content
|
610 |
+
|
611 |
+
# Please ensure the presentation content is presented first, followed by the separator, and then the questionnaire.
|
612 |
+
|
613 |
+
# Begin the content generation now.
|
614 |
+
# """
|
615 |
+
|
616 |
+
# response = model.generate_content(prompt)
|
617 |
+
# return response.text
|
618 |
+
|
619 |
+
# def parse_slide_content(slide_text):
|
620 |
+
# slides = []
|
621 |
+
# current_slide = {}
|
622 |
+
# questionnaire = []
|
623 |
+
|
624 |
+
# # Split into main content and questionnaire
|
625 |
+
# parts = slide_text.split('--- QUESTIONNAIRE ---')
|
626 |
+
# main_content = parts[0].strip()
|
627 |
+
# questionnaire_content = parts[1].strip() if len(parts) > 1 else ""
|
628 |
+
|
629 |
+
# # Parse main slides
|
630 |
+
# for line in main_content.split('\n'):
|
631 |
+
# line = line.strip()
|
632 |
+
# if not line:
|
633 |
+
# continue
|
634 |
+
|
635 |
+
# if line.startswith('[Title:]'):
|
636 |
+
# if current_slide:
|
637 |
+
# slides.append(current_slide)
|
638 |
+
# current_slide = {
|
639 |
+
# 'title': line.replace('[Title:]', '').strip(),
|
640 |
+
# 'content': [],
|
641 |
+
# 'notes': '',
|
642 |
+
# 'layout': 'title-content'
|
643 |
+
# }
|
644 |
+
# elif line.startswith('[Content:]'):
|
645 |
+
# content = line.replace('[Content:]', '').strip()
|
646 |
+
# if content:
|
647 |
+
# current_slide['content'].append(content)
|
648 |
+
# elif line.startswith('[Notes:]'):
|
649 |
+
# current_slide['notes'] = line.replace('[Notes:]', '').strip()
|
650 |
+
# elif line.startswith('[Layout:]'):
|
651 |
+
# layout = line.replace('[Layout:]', '').strip().lower()
|
652 |
+
# valid_layouts = ['title-only', 'title-content', 'two-column', 'section-header']
|
653 |
+
# current_slide['layout'] = layout if layout in valid_layouts else 'title-content'
|
654 |
+
# elif current_slide.get('content') is not None and line.startswith('-'):
|
655 |
+
# # Simplify bullet points - remove any explanations after colons
|
656 |
+
# point = line[1:].strip()
|
657 |
+
# if ':' in point:
|
658 |
+
# point = point.split(':')[0].strip()
|
659 |
+
# current_slide['content'].append(point)
|
660 |
+
|
661 |
+
# if current_slide:
|
662 |
+
# slides.append(current_slide)
|
663 |
+
|
664 |
+
# # Parse questionnaire if exists
|
665 |
+
# if questionnaire_content:
|
666 |
+
# current_question = {}
|
667 |
+
# for line in questionnaire_content.split('\n'):
|
668 |
+
# line = line.strip()
|
669 |
+
# if not line:
|
670 |
+
# continue
|
671 |
+
|
672 |
+
# if line.startswith('[Question:'):
|
673 |
+
# if current_question:
|
674 |
+
# questionnaire.append(current_question)
|
675 |
+
# question_text = line.replace('[Question:', '').replace(']', '').strip()
|
676 |
+
# current_question = {
|
677 |
+
# 'text': question_text,
|
678 |
+
# 'options': [],
|
679 |
+
# 'correct': ''
|
680 |
+
# }
|
681 |
+
# elif line.startswith('- [A]'):
|
682 |
+
# current_question['options'].append(line.replace('- [A]', '').strip())
|
683 |
+
# elif line.startswith('- [B]'):
|
684 |
+
# current_question['options'].append(line.replace('- [B]', '').strip())
|
685 |
+
# elif line.startswith('- [C]'):
|
686 |
+
# current_question['options'].append(line.replace('- [C]', '').strip())
|
687 |
+
# elif line.startswith('- [D]'):
|
688 |
+
# current_question['options'].append(line.replace('- [D]', '').strip())
|
689 |
+
# elif line.startswith('[Correct:'):
|
690 |
+
# current_question['correct'] = line.replace('[Correct:', '').replace(']', '').strip()
|
691 |
+
|
692 |
+
# if current_question:
|
693 |
+
# questionnaire.append(current_question)
|
694 |
+
|
695 |
+
# return slides, questionnaire
|
696 |
+
|
697 |
+
# def create_questionnaire_slide(prs, questions, theme):
|
698 |
+
# """Create dedicated questionnaire slide"""
|
699 |
+
# slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title + Content layout
|
700 |
+
|
701 |
+
# # Set title
|
702 |
+
# title = slide.shapes.title
|
703 |
+
# title.text = "Knowledge Check"
|
704 |
+
|
705 |
+
# # Only apply custom formatting if not using a template
|
706 |
+
# if "template_path" not in theme:
|
707 |
+
# title.text_frame.paragraphs[0].font.color.rgb = theme["accent"]
|
708 |
+
# title.text_frame.paragraphs[0].font.size = Pt(36)
|
709 |
+
# title.text_frame.paragraphs[0].font.bold = True
|
710 |
+
# if "title_font" in theme:
|
711 |
+
# title.text_frame.paragraphs[0].font.name = theme["title_font"]
|
712 |
+
|
713 |
+
# # Create content
|
714 |
+
# body = slide.placeholders[1]
|
715 |
+
# tf = body.text_frame
|
716 |
+
# tf.clear()
|
717 |
+
|
718 |
+
# for i, q in enumerate(questions):
|
719 |
+
# # Add question
|
720 |
+
# p = tf.add_paragraph()
|
721 |
+
# p.text = f"{i+1}. {q['text']}"
|
722 |
+
# p.level = 0
|
723 |
+
# p.font.bold = True
|
724 |
+
# p.space_after = Pt(8)
|
725 |
+
|
726 |
+
# # Only apply custom formatting if not using a template
|
727 |
+
# if "template_path" not in theme:
|
728 |
+
# p.font.color.rgb = theme["text_color"]
|
729 |
+
# p.font.size = Pt(20)
|
730 |
+
# if "text_font" in theme:
|
731 |
+
# p.font.name = theme["text_font"]
|
732 |
+
|
733 |
+
# # Add options
|
734 |
+
# for j, opt in enumerate(q['options']):
|
735 |
+
# p = tf.add_paragraph()
|
736 |
+
# p.text = f" {chr(65+j)}. {opt}"
|
737 |
+
# p.level = 1
|
738 |
+
# p.space_after = Pt(4)
|
739 |
+
|
740 |
+
# # Only apply custom formatting if not using a template
|
741 |
+
# if "template_path" not in theme:
|
742 |
+
# p.font.color.rgb = theme["text_color"]
|
743 |
+
# p.font.size = Pt(18)
|
744 |
+
# if "text_font" in theme:
|
745 |
+
# p.font.name = theme["text_font"]
|
746 |
+
|
747 |
+
# # Add space between questions
|
748 |
+
# p = tf.add_paragraph()
|
749 |
+
# p.text = ""
|
750 |
+
# p.space_after = Pt(16)
|
751 |
+
|
752 |
+
# # Add correct answers to speaker notes
|
753 |
+
# notes_slide = slide.notes_slide
|
754 |
+
# notes_text = "Correct Answers:\n\n"
|
755 |
+
# for i, q in enumerate(questions):
|
756 |
+
# notes_text += f"{i+1}. {q['correct']}\n"
|
757 |
+
# notes_slide.notes_text_frame.text = notes_text
|
758 |
+
|
759 |
+
# return slide
|
760 |
+
|
761 |
+
# def create_detailed_pptx(slides_data, questions, theme, branding_options=None):
|
762 |
+
# """Create PowerPoint using the uploaded template"""
|
763 |
+
# # Use the template if one was uploaded
|
764 |
+
# if "template_path" in theme and os.path.exists(theme["template_path"]):
|
765 |
+
# prs = Presentation(theme["template_path"])
|
766 |
+
# else:
|
767 |
+
# prs = Presentation()
|
768 |
+
|
769 |
+
# # Set widescreen layout (16:9 aspect ratio)
|
770 |
+
# prs.slide_width = Inches(13.33)
|
771 |
+
# prs.slide_height = Inches(7.5)
|
772 |
+
|
773 |
+
# # Layout mapping
|
774 |
+
# layout_indices = {
|
775 |
+
# 'title-only': 0,
|
776 |
+
# 'title-content': 1,
|
777 |
+
# 'section-header': 2,
|
778 |
+
# 'two-column': 3
|
779 |
+
# }
|
780 |
+
|
781 |
+
# available_layouts = {}
|
782 |
+
# for name, idx in layout_indices.items():
|
783 |
+
# if idx < len(prs.slide_layouts):
|
784 |
+
# available_layouts[name] = idx
|
785 |
+
|
786 |
+
# default_layout_idx = available_layouts.get('title-content', 0)
|
787 |
+
|
788 |
+
# # Create main slides
|
789 |
+
# for slide_info in slides_data:
|
790 |
+
# layout = slide_info.get('layout', 'title-content').lower()
|
791 |
+
# layout_idx = available_layouts.get(layout, default_layout_idx)
|
792 |
+
|
793 |
+
# try:
|
794 |
+
# slide = prs.slides.add_slide(prs.slide_layouts[layout_idx])
|
795 |
+
# except IndexError:
|
796 |
+
# slide = prs.slides.add_slide(prs.slide_layouts[default_layout_idx])
|
797 |
+
|
798 |
+
# # Set title
|
799 |
+
# title = slide.shapes.title
|
800 |
+
# title.text = slide_info['title']
|
801 |
+
# title.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER
|
802 |
+
|
803 |
+
# # Only apply custom formatting if not using a template
|
804 |
+
# if "template_path" not in theme:
|
805 |
+
# # Apply background
|
806 |
+
# background = slide.background
|
807 |
+
# fill = background.fill
|
808 |
+
# fill.solid()
|
809 |
+
# fill.fore_color.rgb = theme["background"]
|
810 |
+
|
811 |
+
# # Format title
|
812 |
+
# title.text_frame.paragraphs[0].font.color.rgb = theme["title_color"]
|
813 |
+
# title.text_frame.paragraphs[0].font.size = Pt(36)
|
814 |
+
# title.text_frame.paragraphs[0].font.bold = True
|
815 |
+
# if "title_font" in theme:
|
816 |
+
# title.text_frame.paragraphs[0].font.name = theme["title_font"]
|
817 |
+
|
818 |
+
# # Add logo if provided
|
819 |
+
# if branding_options and branding_options.get('logo_path'):
|
820 |
+
# add_logo_to_slide(slide, branding_options['logo_path'],
|
821 |
+
# branding_options.get('logo_position', 'top-right'))
|
822 |
+
|
823 |
+
# # Set content based on layout
|
824 |
+
# if layout_idx == 3: # Two column layout
|
825 |
+
# content = slide_info.get('content', [])
|
826 |
+
# mid_point = len(content) // 2
|
827 |
+
# left_content = content[:mid_point]
|
828 |
+
# right_content = content[mid_point:]
|
829 |
+
|
830 |
+
# left_body = slide.placeholders[1]
|
831 |
+
# left_tf = left_body.text_frame
|
832 |
+
# left_tf.clear()
|
833 |
+
|
834 |
+
# right_body = slide.placeholders[2]
|
835 |
+
# right_tf = right_body.text_frame
|
836 |
+
# right_tf.clear()
|
837 |
+
|
838 |
+
# for content_part, tf in [(left_content, left_tf), (right_content, right_tf)]:
|
839 |
+
# for point in content_part:
|
840 |
+
# p = tf.add_paragraph()
|
841 |
+
# point_text = point.replace('- ', '').strip()
|
842 |
+
# p.text = point_text
|
843 |
+
# p.level = 0
|
844 |
+
# p.alignment = PP_ALIGN.JUSTIFY
|
845 |
+
# if "template_path" not in theme: # Only apply custom formatting if no template
|
846 |
+
# p.font.color.rgb = theme["text_color"]
|
847 |
+
# p.font.size = Pt(18)
|
848 |
+
# if "text_font" in theme:
|
849 |
+
# p.font.name = theme["text_font"]
|
850 |
+
|
851 |
+
# elif layout_idx != 0: # Not title-only
|
852 |
+
# body = slide.placeholders[1]
|
853 |
+
# tf = body.text_frame
|
854 |
+
# tf.clear()
|
855 |
+
|
856 |
+
# for point in slide_info.get('content', []):
|
857 |
+
# p = tf.add_paragraph()
|
858 |
+
# point_text = point.replace('- ', '').strip()
|
859 |
+
# p.text = point_text
|
860 |
+
# p.level = 0
|
861 |
+
# p.alignment = PP_ALIGN.JUSTIFY
|
862 |
+
# if "template_path" not in theme: # Only apply custom formatting if no template
|
863 |
+
# p.font.color.rgb = theme["text_color"]
|
864 |
+
# p.font.size = Pt(18)
|
865 |
+
# if "text_font" in theme:
|
866 |
+
# p.font.name = theme["text_font"]
|
867 |
+
|
868 |
+
# # Add notes if available
|
869 |
+
# if slide_info.get('notes'):
|
870 |
+
# notes_slide = slide.notes_slide
|
871 |
+
# notes_slide.notes_text_frame.text = slide_info['notes']
|
872 |
+
|
873 |
+
# # Add questionnaire slide if questions exist
|
874 |
+
# if questions:
|
875 |
+
# create_questionnaire_slide(prs, questions, theme)
|
876 |
+
|
877 |
+
# pptx_io = io.BytesIO()
|
878 |
+
# prs.save(pptx_io)
|
879 |
+
# pptx_io.seek(0)
|
880 |
+
|
881 |
+
# # Clean up temporary template file
|
882 |
+
# if "template_path" in theme and os.path.exists(theme["template_path"]):
|
883 |
+
# os.unlink(theme["template_path"])
|
884 |
+
|
885 |
+
# return pptx_io
|
886 |
+
|
887 |
+
# def main():
|
888 |
+
# st.set_page_config(page_title="Advanced PPTX Generator", layout="wide")
|
889 |
+
|
890 |
+
# st.title("Advanced PowerPoint Generator")
|
891 |
+
# st.markdown("Create professional presentations with AI")
|
892 |
+
|
893 |
+
# # Initialize session state for custom themes if not exists
|
894 |
+
# if 'custom_themes' not in st.session_state:
|
895 |
+
# st.session_state.custom_themes = {}
|
896 |
+
|
897 |
+
# # Combine default and custom themes
|
898 |
+
# ALL_THEMES = {**DEFAULT_THEMES, **st.session_state.custom_themes}
|
899 |
+
# col1, col2 = st.columns([3, 1])
|
900 |
+
|
901 |
+
# with col1:
|
902 |
+
# topic = st.text_input("Presentation Topic:",
|
903 |
+
# placeholder="Enter your topic (e.g., 'AI in Healthcare')")
|
904 |
+
|
905 |
+
# with st.expander("Advanced Options"):
|
906 |
+
# slide_count = st.slider("Number of Slides:", 5, 20, 10)
|
907 |
+
|
908 |
+
# # Theme selection with example-based option
|
909 |
+
# theme_option = st.radio("Theme Selection Method:",
|
910 |
+
# ["Predefined Theme", "Custom Theme", "Example-Based Theme"])
|
911 |
+
|
912 |
+
# theme = DEFAULT_THEMES["Professional Blue"] # Default theme
|
913 |
+
# uploaded_file = None
|
914 |
+
|
915 |
+
# if theme_option == "Predefined Theme":
|
916 |
+
# theme_name = st.selectbox("Select Theme:", list(DEFAULT_THEMES.keys()))
|
917 |
+
# theme = DEFAULT_THEMES[theme_name]
|
918 |
+
# elif theme_option == "Custom Theme":
|
919 |
+
# theme_name = st.selectbox("Select Custom Theme:",
|
920 |
+
# ["Create New..."] + list(st.session_state.custom_themes.keys()))
|
921 |
+
|
922 |
+
# if theme_name == "Create New...":
|
923 |
+
# with st.form("custom_theme_form"):
|
924 |
+
# new_theme_name = st.text_input("Theme Name")
|
925 |
+
# bg_color = st.color_picker("Background Color", "#0C2340")
|
926 |
+
# title_color = st.color_picker("Title Color", "#FFFFFF")
|
927 |
+
# text_color = st.color_picker("Text Color", "#C8C8C8")
|
928 |
+
# accent_color = st.color_picker("Accent Color", "#0070C0")
|
929 |
+
# title_font = st.text_input("Title Font", "Calibri")
|
930 |
+
# text_font = st.text_input("Text Font", "Calibri")
|
931 |
+
|
932 |
+
# if st.form_submit_button("Save Custom Theme"):
|
933 |
+
# if new_theme_name:
|
934 |
+
# st.session_state.custom_themes[new_theme_name] = {
|
935 |
+
# "background": hex_to_rgb(bg_color),
|
936 |
+
# "title_color": hex_to_rgb(title_color),
|
937 |
+
# "text_color": hex_to_rgb(text_color),
|
938 |
+
# "accent": hex_to_rgb(accent_color),
|
939 |
+
# "title_font": title_font,
|
940 |
+
# "text_font": text_font
|
941 |
+
# }
|
942 |
+
# st.success(f"Theme '{new_theme_name}' saved successfully!")
|
943 |
+
# else:
|
944 |
+
# st.warning("Please enter a theme name")
|
945 |
+
# elif theme_name in st.session_state.custom_themes:
|
946 |
+
# theme = st.session_state.custom_themes[theme_name]
|
947 |
+
# else: # Example-Based Theme
|
948 |
+
# uploaded_file = st.file_uploader("Upload PowerPoint Template", type=["pptx"])
|
949 |
+
# if uploaded_file:
|
950 |
+
# with st.spinner("Extracting theme from template..."):
|
951 |
+
# theme = extract_theme_from_pptx(uploaded_file)
|
952 |
+
# st.success("Theme extracted from template!")
|
953 |
+
# else:
|
954 |
+
# st.info("Please upload a PowerPoint file to extract its theme")
|
955 |
+
|
956 |
+
# with col2:
|
957 |
+
# st.markdown("### Instructions")
|
958 |
+
# st.markdown("1. Enter your presentation topic")
|
959 |
+
# st.markdown("2. Select slide count and theme")
|
960 |
+
# st.markdown("3. Click 'Generate Presentation'")
|
961 |
+
# st.markdown("4. Download your PowerPoint file")
|
962 |
+
|
963 |
+
# if st.button("Generate Presentation", type="primary", key="generate_btn"):
|
964 |
+
# if not topic:
|
965 |
+
# st.warning("Please enter a topic first!")
|
966 |
+
# elif theme_option == "Example-Based Theme" and not uploaded_file:
|
967 |
+
# st.warning("Please upload a PowerPoint template file first")
|
968 |
+
# else:
|
969 |
+
# with st.spinner(f"Creating {slide_count}-slide presentation about '{topic}'..."):
|
970 |
+
# try:
|
971 |
+
# slide_text = generate_slide_content(topic, slide_count)
|
972 |
+
# slides_data, questionnaire = parse_slide_content(slide_text)
|
973 |
+
|
974 |
+
# # Show slide overview with detailed content
|
975 |
+
# with st.expander("Slide Overview (Detailed)"):
|
976 |
+
# for i, slide in enumerate(slides_data, 1):
|
977 |
+
# st.subheader(f"Slide {i}: {slide['title']}")
|
978 |
+
# st.markdown("**Content:**")
|
979 |
+
# for point in slide.get('content', []):
|
980 |
+
# st.markdown(f"- {point}")
|
981 |
+
# if slide.get('notes'):
|
982 |
+
# st.markdown(f"**Notes:** {slide['notes']}")
|
983 |
+
# st.markdown("---")
|
984 |
+
|
985 |
+
# if questionnaire:
|
986 |
+
# st.subheader("Questionnaire")
|
987 |
+
# for i, q in enumerate(questionnaire, 1):
|
988 |
+
# st.markdown(f"**{i}. {q['text']}**")
|
989 |
+
# for j, opt in enumerate(q['options']):
|
990 |
+
# st.markdown(f"- {chr(65+j)}. {opt}")
|
991 |
+
# st.markdown(f"*Correct: {q['correct']}*")
|
992 |
+
# st.markdown("---")
|
993 |
+
|
994 |
+
# pptx_file = create_detailed_pptx(slides_data, questionnaire, theme)
|
995 |
+
|
996 |
+
# st.success("Presentation generated successfully!")
|
997 |
+
|
998 |
+
# st.download_button(
|
999 |
+
# label="Download PowerPoint",
|
1000 |
+
# data=pptx_file,
|
1001 |
+
# file_name=f"{topic.replace(' ', '_')}_presentation.pptx",
|
1002 |
+
# mime="application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
1003 |
+
# )
|
1004 |
+
|
1005 |
+
# except Exception as e:
|
1006 |
+
# st.error(f"An error occurred: {str(e)}")
|
1007 |
+
|
1008 |
+
# if __name__ == "__main__":
|
1009 |
+
# main()
|
1010 |
+
|
1011 |
+
|
1012 |
+
###################################################################################################################################################################
|
1013 |
|
1014 |
import streamlit as st
|
1015 |
import google.generativeai as genai
|
|
|
1183 |
- Clearly indicate the correct answer
|
1184 |
- Questions test comprehension of the presentation content
|
1185 |
|
1186 |
+
Finally, add a separator:
|
1187 |
+
--- ANSWER KEY ---
|
1188 |
+
|
1189 |
+
Then provide an answer key listing all correct answers in the format:
|
1190 |
+
[Answer Key]
|
1191 |
+
1. C
|
1192 |
+
2. A
|
1193 |
+
3. B
|
1194 |
+
... [continue for all 10 questions]
|
1195 |
+
|
1196 |
+
Please ensure the presentation content is presented first, followed by the questionnaire, then the answer key.
|
1197 |
|
1198 |
Begin the content generation now.
|
1199 |
"""
|
|
|
1205 |
slides = []
|
1206 |
current_slide = {}
|
1207 |
questionnaire = []
|
1208 |
+
answer_key = []
|
1209 |
|
1210 |
+
# Split into sections
|
1211 |
parts = slide_text.split('--- QUESTIONNAIRE ---')
|
1212 |
main_content = parts[0].strip()
|
1213 |
+
remaining = parts[1].strip() if len(parts) > 1 else ""
|
1214 |
+
|
1215 |
+
# Split remaining into questionnaire and answer key
|
1216 |
+
qa_parts = remaining.split('--- ANSWER KEY ---')
|
1217 |
+
questionnaire_content = qa_parts[0].strip() if qa_parts else ""
|
1218 |
+
answer_key_content = qa_parts[1].strip() if len(qa_parts) > 1 else ""
|
1219 |
|
1220 |
# Parse main slides
|
1221 |
for line in main_content.split('\n'):
|
|
|
1283 |
if current_question:
|
1284 |
questionnaire.append(current_question)
|
1285 |
|
1286 |
+
# Parse answer key if exists
|
1287 |
+
if answer_key_content:
|
1288 |
+
# Extract answer key lines
|
1289 |
+
for line in answer_key_content.split('\n'):
|
1290 |
+
line = line.strip()
|
1291 |
+
if line.startswith('[') or not line:
|
1292 |
+
continue
|
1293 |
+
if re.match(r'^\d+\.\s*[A-D]$', line):
|
1294 |
+
answer_key.append(line.split('.')[1].strip())
|
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}"
|
1305 |
|
1306 |
# Only apply custom formatting if not using a template
|
1307 |
if "template_path" not in theme:
|
|
|
1316 |
tf = body.text_frame
|
1317 |
tf.clear()
|
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 |
+
|
1326 |
+
# Only apply custom formatting if not using a template
|
1327 |
+
if "template_path" not in theme:
|
1328 |
+
p.font.color.rgb = theme["text_color"]
|
1329 |
+
p.font.size = Pt(24)
|
1330 |
+
if "text_font" in theme:
|
1331 |
+
p.font.name = theme["text_font"]
|
1332 |
+
|
1333 |
+
# Add options
|
1334 |
+
for j, opt in enumerate(question['options']):
|
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
|
1341 |
if "template_path" not in theme:
|
|
|
1343 |
p.font.size = Pt(20)
|
1344 |
if "text_font" in theme:
|
1345 |
p.font.name = theme["text_font"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1346 |
|
1347 |
+
# Add correct answer to speaker notes
|
1348 |
notes_slide = slide.notes_slide
|
1349 |
+
notes_text = f"Correct Answer: {question['correct']}\n"
|
|
|
|
|
1350 |
notes_slide.notes_text_frame.text = notes_text
|
1351 |
|
1352 |
return slide
|
1353 |
|
1354 |
+
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"
|
1361 |
+
|
1362 |
+
# Only apply custom formatting if not using a template
|
1363 |
+
if "template_path" not in theme:
|
1364 |
+
title.text_frame.paragraphs[0].font.color.rgb = theme["accent"]
|
1365 |
+
title.text_frame.paragraphs[0].font.size = Pt(36)
|
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 |
+
body = slide.placeholders[1]
|
1372 |
+
tf = body.text_frame
|
1373 |
+
tf.clear()
|
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(12)
|
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 |
+
|
1389 |
+
return slide
|
1390 |
+
|
1391 |
def create_detailed_pptx(slides_data, questions, theme, branding_options=None):
|
1392 |
"""Create PowerPoint using the uploaded template"""
|
1393 |
# Use the template if one was uploaded
|
|
|
1500 |
notes_slide = slide.notes_slide
|
1501 |
notes_slide.notes_text_frame.text = slide_info['notes']
|
1502 |
|
1503 |
+
# Add question slides (one per question)
|
1504 |
if questions:
|
1505 |
+
# Add section header
|
1506 |
+
section_slide = prs.slides.add_slide(prs.slide_layouts[2]) # Section header layout
|
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)
|
1513 |
+
|
1514 |
+
# Add answer key slide
|
1515 |
+
create_answer_key_slide(prs, questions, theme)
|
1516 |
|
1517 |
pptx_io = io.BytesIO()
|
1518 |
prs.save(pptx_io)
|
|
|
1609 |
with st.spinner(f"Creating {slide_count}-slide presentation about '{topic}'..."):
|
1610 |
try:
|
1611 |
slide_text = generate_slide_content(topic, slide_count)
|
1612 |
+
slides_data, questionnaire, answer_key = parse_slide_content(slide_text)
|
1613 |
|
1614 |
# Show slide overview with detailed content
|
1615 |
with st.expander("Slide Overview (Detailed)"):
|
|
|
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']}**")
|
1630 |
for j, opt in enumerate(q['options']):
|
1631 |
+
st.markdown(f"{chr(65+j)}. {opt}")
|
1632 |
st.markdown(f"*Correct: {q['correct']}*")
|
1633 |
st.markdown("---")
|
1634 |
+
|
1635 |
+
if answer_key:
|
1636 |
+
st.subheader("Answer Key")
|
1637 |
+
for i, ans in enumerate(answer_key, 1):
|
1638 |
+
st.markdown(f"{i}. {ans}")
|
1639 |
|
1640 |
pptx_file = create_detailed_pptx(slides_data, questionnaire, theme)
|
1641 |
|