YashMK89 commited on
Commit
0d55be9
Β·
verified Β·
1 Parent(s): 019e28e

update page slide in QA

Browse files
Files changed (1) hide show
  1. 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
- Please ensure the presentation content is presented first, followed by the separator, and then the questionnaire.
 
 
 
 
 
 
 
 
 
 
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 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'):
@@ -692,15 +1283,25 @@ def parse_slide_content(slide_text):
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:
@@ -715,13 +1316,26 @@ def create_questionnaire_slide(prs, questions, theme):
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:
@@ -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 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
@@ -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 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)
@@ -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.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
 
 
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