capradeepgujaran commited on
Commit
91ac2b1
1 Parent(s): a05bac0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +300 -180
app.py CHANGED
@@ -33,7 +33,8 @@ class QuizGenerator:
33
  messages=[
34
  {
35
  "role": "system",
36
- "content": "You are a quiz generator. Create clear questions with concise answer options."
 
37
  },
38
  {
39
  "role": "user",
@@ -41,7 +42,7 @@ class QuizGenerator:
41
  }
42
  ],
43
  model="llama-3.2-3b-preview",
44
- temperature=0.2,
45
  max_tokens=2048
46
  )
47
 
@@ -49,68 +50,105 @@ class QuizGenerator:
49
  return self._validate_questions(questions, num_questions)
50
 
51
  except Exception as e:
 
 
52
  raise QuizGenerationError(f"Failed to generate questions: {str(e)}")
53
 
54
  def _create_prompt(self, text: str, num_questions: int) -> str:
55
- return f"""Create exactly {num_questions} multiple choice questions based on this text:
56
- {text}
57
-
58
- For each question:
59
- 1. Create a clear, concise question
60
- 2. Provide exactly 4 options
61
- 3. Mark the correct answer with the index (0-3)
62
- 4. Ensure options are concise and clear
63
-
64
- Return ONLY a JSON array with this EXACT format - no other text:
65
- [
66
- {{
67
- "question": "Question text here?",
68
- "options": [
69
- "Brief option 1",
70
- "Brief option 2",
71
- "Brief option 3",
72
- "Brief option 4"
73
- ],
74
- "correct_answer": 0
75
- }}
76
- ]
77
- Keep all options concise (10 words or less each).
78
- """
79
 
80
  def _parse_response(self, response_text: str) -> List[Dict]:
81
- response_text = response_text.replace("```json", "").replace("```", "").strip()
82
- start_idx = response_text.find("[")
83
- end_idx = response_text.rfind("]")
84
-
85
- if start_idx == -1 or end_idx == -1:
86
- raise ValueError("No valid JSON array found in response")
 
 
 
 
 
 
 
 
87
 
88
- response_text = response_text[start_idx:end_idx + 1]
89
- return json.loads(response_text)
 
 
 
 
 
 
 
 
 
 
90
 
91
  def _validate_questions(self, questions: List[Dict], num_questions: int) -> List[Question]:
 
92
  validated = []
 
93
  for q in questions:
94
- if not self._is_valid_question(q):
 
 
 
 
 
 
 
 
 
 
 
95
  continue
96
- validated.append(Question(
97
- question=q["question"].strip(),
98
- options=[opt.strip()[:100] for opt in q["options"]],
99
- correct_answer=int(q["correct_answer"]) % 4
100
- ))
101
 
102
  if not validated:
103
  raise ValueError("No valid questions after validation")
104
-
105
  return validated[:num_questions]
106
 
107
  def _is_valid_question(self, question: Dict) -> bool:
108
- return (
109
- all(key in question for key in ["question", "options", "correct_answer"]) and
110
- isinstance(question["options"], list) and
111
- len(question["options"]) == 4 and
112
- all(isinstance(opt, str) for opt in question["options"])
113
- )
 
 
 
 
 
 
 
 
 
114
 
115
  class FontManager:
116
  """Manages font installation and loading for the certificate generator"""
@@ -558,7 +596,7 @@ def create_quiz_interface():
558
  # State management
559
  current_questions = gr.State([])
560
  current_question_idx = gr.State(0)
561
- answer_state = gr.State([None] * 5) # Store all answers
562
 
563
  # Header
564
  gr.Markdown("""
@@ -568,7 +606,7 @@ def create_quiz_interface():
568
 
569
  with gr.Tabs() as tabs:
570
  # Profile Setup Tab
571
- with gr.Tab(id=1,label="📋 Step 1: Profile Setup"):
572
  with gr.Row():
573
  name = gr.Textbox(label="Full Name", placeholder="Enter your full name")
574
  email = gr.Textbox(label="Email", placeholder="Enter your email")
@@ -594,21 +632,17 @@ def create_quiz_interface():
594
  generate_btn = gr.Button("Generate Assessment", variant="primary", size="lg")
595
 
596
  # Assessment Tab
597
- with gr.Tab(id=2,label="📝 Step 2: Take Assessment") as assessment_tab:
598
- with gr.Column(visible=True) as question_box:
599
- # Question section
600
- with gr.Group():
601
- # Question display
602
- question_display = gr.Markdown("", label="Current Question")
603
-
604
- # Single radio group for current question
605
  current_options = gr.Radio(
606
  choices=[],
607
  label="Select your answer:",
608
  visible=False
609
  )
610
 
611
- # Navigation
612
  with gr.Row():
613
  prev_btn = gr.Button("← Previous", variant="secondary", size="sm")
614
  question_counter = gr.Markdown("Question 1")
@@ -616,125 +650,135 @@ def create_quiz_interface():
616
 
617
  gr.Markdown("---") # Separator
618
 
619
- with gr.Row():
620
- submit_btn = gr.Button(
621
- "Submit Assessment",
622
- variant="primary",
623
- size="lg"
624
- )
 
 
 
 
 
 
 
 
 
 
 
 
625
  reset_btn = gr.Button(
626
  "Reset Quiz",
627
  variant="secondary",
628
- size="lg"
 
629
  )
630
-
631
- # Results section
632
- with gr.Group(visible=False) as results_group:
633
- feedback_box = gr.Markdown("")
634
- with gr.Row():
635
- view_cert_btn = gr.Button(
636
- "View Certificate",
637
- variant="primary",
638
- size="lg",
639
- visible=False
640
- )
641
- back_to_assessment = gr.Button( # Add this button definition
642
- "Back to Assessment",
643
- variant="secondary",
644
- size="lg",
645
- visible=True
646
- )
647
-
648
- # Certification Tab
649
- with gr.Tab(id=3,label="🎓 Step 3: Get Certified"):
650
- score_display = gr.Number(label="Your Score")
651
- result_message = gr.Markdown("")
652
  course_name = gr.Textbox(
653
  label="Certification Title",
654
- value="Professional Assessment Certification"
 
655
  )
656
  certificate_display = gr.Image(label="Your Certificate")
657
 
 
 
 
 
 
 
 
 
658
  # Helper Functions
659
  def on_generate_questions(text, num_questions):
660
- """Generate quiz questions and setup initial state"""
 
 
 
 
 
 
 
 
 
 
 
 
 
661
  success, questions = quiz_app.generate_questions(text, num_questions)
662
- if not success:
663
  return [
664
- "", # question_display
665
- gr.update(choices=[], visible=False), # current_options
666
- "", # question_counter
667
- gr.update(visible=False), # question_box
668
- [], # current_questions
669
- 0, # current_question_idx
670
- [None] * 5, # answer_state
671
- gr.Tabs(selected=2), # tabs
672
- gr.update(visible=False), # results_group
673
- gr.update(visible=False) # view_cert_btn - added this
674
  ]
675
 
676
- # Setup initial state with first question
677
- initial_answers = [None] * len(questions)
678
  question = questions[0]
679
- question_html = f"""## Question 1
680
- {question.question}
681
-
682
- Please select one answer:"""
683
 
684
  return [
685
- question_html, # question_display
 
686
  gr.update(
687
  choices=question.options,
688
  value=None,
689
  visible=True,
690
- label="Select your answer for Question 1:"
691
- ), # current_options
692
- f"Question 1 of {len(questions)}", # question_counter
693
- gr.update(visible=True), # question_box
694
- questions, # current_questions
695
- 0, # current_question_idx
696
- initial_answers, # answer_state
697
- gr.Tabs(selected=2), # tabs
698
- gr.update(visible=False), # results_group
699
- gr.update(visible=False) # view_cert_btn - added this
700
  ]
 
701
  def navigate(direction, current_idx, questions, answers, current_answer):
702
- """Handle navigation between questions"""
703
  if not questions:
704
- return [
705
- 0,
706
- answers,
707
- "",
708
- gr.update(choices=[], value=None, visible=False),
709
- "",
710
- gr.update(visible=False)
711
- ]
712
 
713
  new_answers = list(answers)
714
- if current_answer and 0 <= current_idx < len(new_answers):
715
  new_answers[current_idx] = current_answer
716
 
717
  new_idx = max(0, min(len(questions) - 1, current_idx + direction))
718
  question = questions[new_idx]
719
 
 
 
 
720
  return [
721
  new_idx,
722
  new_answers,
723
- f"## Question {new_idx + 1}\n{question.question}\n\nPlease select one answer:",
724
  gr.update(
725
  choices=question.options,
726
  value=new_answers[new_idx] if new_idx < len(new_answers) else None,
727
  visible=True,
728
- label=f"Select your answer for Question {new_idx + 1}:"
729
  ),
730
  f"Question {new_idx + 1} of {len(questions)}",
731
  gr.update(visible=True)
732
- ]
733
- def handle_prev(current_idx, questions, answers, current_answer):
734
- return navigate(-1, current_idx, questions, answers, current_answer)
735
-
736
- def handle_next(current_idx, questions, answers, current_answer):
737
- return navigate(1, current_idx, questions, answers, current_answer)
738
 
739
  def update_answer_state(answer, idx, current_answers):
740
  new_answers = list(current_answers)
@@ -748,9 +792,16 @@ def create_quiz_interface():
748
 
749
  def view_certificate():
750
  """Navigate to certificate tab"""
751
- return gr.Tabs(selected=2)
 
 
 
 
 
 
752
 
753
  def on_submit(questions, answers, current_idx, current_answer):
 
754
  final_answers = list(answers)
755
  if 0 <= current_idx < len(final_answers):
756
  final_answers[current_idx] = current_answer
@@ -762,71 +813,140 @@ def create_quiz_interface():
762
  0,
763
  "",
764
  gr.update(visible=True),
765
- gr.Tabs(selected=2),
 
766
  gr.update(visible=False)
767
  ]
768
 
769
  score, passed, feedback = quiz_app.calculate_score(final_answers[:len(questions)])
770
 
771
- feedback_html = "# Assessment Results\n\n"
 
 
 
 
 
 
772
  for i, (q, f) in enumerate(zip(questions, feedback)):
 
773
  color = "green" if f.is_correct else "red"
774
- symbol = "✅" if f.is_correct else "❌"
775
 
776
- feedback_html += f"""### Question {i+1}
 
777
  {q.question}
778
- <div style="color: {color}; padding: 10px; margin: 10px 0; border-left: 3px solid {color}; background-color: {'#f8fff8' if f.is_correct else '#fff8f8'};">
779
- {symbol} Your answer: {f.selected or "No answer"}
780
- {'' if f.is_correct else f'<br>Correct answer: {f.correct_answer}'}
781
- </div>\n"""
782
-
783
- result_msg = "🎉 Passed!" if passed else "Please try again"
784
- if not passed:
785
- feedback_html += f"""<div style="background-color: #ffe6e6; padding: 20px; margin: 20px 0; border-radius: 10px; border: 1px solid #ffcccc;">
786
- <h3 style="color: #cc0000; margin-top: 0;">Please Try Again</h3>
787
- <p style="margin: 10px 0;">Your score: {score:.1f}%</p>
788
- <p style="margin: 10px 0;">You need 80% or higher to pass and receive a certificate.</p>
789
- </div>"""
 
790
  else:
791
- feedback_html += f"""<div style="background-color: #e6ffe6; padding: 20px; margin: 20px 0; border-radius: 10px; border: 1px solid #ccffcc;">
792
- <h3 style="color: #008000; margin-top: 0;">🎉 Congratulations!</h3>
793
- <p style="margin: 10px 0;">You passed with a score of {score:.1f}%</p>
794
- <p style="margin: 10px 0;">Click "View Certificate" to see your certificate.</p>
795
- </div>"""
 
796
 
797
  return [
798
- feedback_html,
799
- gr.update(visible=True),
800
- score,
801
- result_msg,
802
- gr.update(visible=False),
803
- gr.Tabs(selected=2),
804
- gr.update(visible=passed)
 
805
  ]
806
- # Event handlers
807
-
808
- generate_btn.click(fn=on_generate_questions, inputs=[text_input, num_questions], outputs=[question_display, current_options, question_counter, question_box, current_questions, current_question_idx, answer_state, tabs, results_group, view_cert_btn]).then(fn=lambda: gr.Tabs(selected=2), outputs=tabs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
809
 
810
- prev_btn.click(fn=handle_prev, inputs=[current_question_idx, current_questions, answer_state, current_options], outputs=[current_question_idx, answer_state, question_display, current_options, question_counter, question_box])
 
 
 
 
811
 
812
- next_btn.click(fn=handle_next, inputs=[current_question_idx, current_questions, answer_state, current_options], outputs=[current_question_idx, answer_state, question_display, current_options, question_counter, question_box])
 
 
 
 
813
 
814
- current_options.change(fn=update_answer_state, inputs=[current_options, current_question_idx, answer_state], outputs=answer_state)
 
 
 
 
 
 
 
 
 
 
 
 
 
815
 
816
- submit_btn.click(fn=on_submit, inputs=[current_questions, answer_state, current_question_idx, current_options], outputs=[feedback_box, results_group, score_display, result_message, question_box, tabs, view_cert_btn])
817
-
818
- reset_btn.click(fn=reset_quiz, inputs=[text_input, num_questions], outputs=[question_display, current_options, question_counter, question_box, current_questions, current_question_idx, answer_state, tabs, results_group, view_cert_btn])
 
 
 
 
 
 
 
 
 
 
 
 
 
819
 
820
- view_cert_btn.click(fn=lambda: gr.Tabs(selected=3), outputs=tabs)
 
 
 
821
 
822
- back_to_assessment.click(fn=lambda: gr.Tabs(selected=2), outputs=tabs)
 
 
 
 
 
 
 
 
 
 
823
 
824
- score_display.change(fn=lambda s, n, c, l, p: quiz_app.certificate_generator.generate(s, n, c, l, p) or gr.update(value=None), inputs=[score_display, name, course_name, company_logo, participant_photo], outputs=certificate_display)
825
-
826
-
827
  return demo
828
 
829
  if __name__ == "__main__":
830
  demo = create_quiz_interface()
831
- demo.launch()
832
-
 
33
  messages=[
34
  {
35
  "role": "system",
36
+ "content": """You are a quiz generator. Create clear questions with concise answer options.
37
+ Always return valid JSON array of questions. Format answers as short, clear phrases."""
38
  },
39
  {
40
  "role": "user",
 
42
  }
43
  ],
44
  model="llama-3.2-3b-preview",
45
+ temperature=0,
46
  max_tokens=2048
47
  )
48
 
 
50
  return self._validate_questions(questions, num_questions)
51
 
52
  except Exception as e:
53
+ print(f"Error in generate_questions: {str(e)}")
54
+ print(f"Response content: {response.choices[0].message.content if response else 'No response'}")
55
  raise QuizGenerationError(f"Failed to generate questions: {str(e)}")
56
 
57
  def _create_prompt(self, text: str, num_questions: int) -> str:
58
+ return f"""Generate exactly {num_questions} multiple choice questions based on the following text. Follow these rules strictly:
59
+
60
+ 1. Each question must be clear and focused
61
+ 2. Provide exactly 4 options for each question
62
+ 3. Mark the correct answer with index (0-3)
63
+ 4. Keep all options concise (under 10 words)
64
+ 5. Return ONLY a JSON array with this exact format:
65
+ [
66
+ {{
67
+ "question": "Clear question text here?",
68
+ "options": [
69
+ "Brief option 1",
70
+ "Brief option 2",
71
+ "Brief option 3",
72
+ "Brief option 4"
73
+ ],
74
+ "correct_answer": 0
75
+ }}
76
+ ]
77
+
78
+ Text to generate questions from:
79
+ {text}
80
+
81
+ Important: Return only the JSON array, no other text or formatting."""
82
 
83
  def _parse_response(self, response_text: str) -> List[Dict]:
84
+ """Parse response with improved error handling"""
85
+ try:
86
+ # Clean up the response text
87
+ cleaned_text = response_text.strip()
88
+ cleaned_text = cleaned_text.replace('```json', '').replace('```', '').strip()
89
+
90
+ # Find the JSON array
91
+ start_idx = cleaned_text.find('[')
92
+ end_idx = cleaned_text.rfind(']')
93
+
94
+ if start_idx == -1 or end_idx == -1:
95
+ raise ValueError("No valid JSON array found in response")
96
+
97
+ json_text = cleaned_text[start_idx:end_idx + 1]
98
 
99
+ # Attempt to parse the JSON
100
+ try:
101
+ return json.loads(json_text)
102
+ except json.JSONDecodeError as e:
103
+ print(f"JSON Parse Error: {str(e)}")
104
+ print(f"Attempted to parse: {json_text}")
105
+ raise
106
+
107
+ except Exception as e:
108
+ print(f"Error parsing response: {str(e)}")
109
+ print(f"Original response: {response_text}")
110
+ raise ValueError(f"Failed to parse response: {str(e)}")
111
 
112
  def _validate_questions(self, questions: List[Dict], num_questions: int) -> List[Question]:
113
+ """Validate questions with improved error checking"""
114
  validated = []
115
+
116
  for q in questions:
117
+ try:
118
+ if not self._is_valid_question(q):
119
+ print(f"Invalid question format: {q}")
120
+ continue
121
+
122
+ validated.append(Question(
123
+ question=q["question"].strip(),
124
+ options=[str(opt).strip()[:100] for opt in q["options"]],
125
+ correct_answer=int(q["correct_answer"]) % 4
126
+ ))
127
+ except Exception as e:
128
+ print(f"Error validating question: {str(e)}")
129
  continue
 
 
 
 
 
130
 
131
  if not validated:
132
  raise ValueError("No valid questions after validation")
133
+
134
  return validated[:num_questions]
135
 
136
  def _is_valid_question(self, question: Dict) -> bool:
137
+ """Check if question format is valid"""
138
+ try:
139
+ return (
140
+ isinstance(question, dict) and
141
+ all(key in question for key in ["question", "options", "correct_answer"]) and
142
+ isinstance(question["question"], str) and
143
+ isinstance(question["options"], list) and
144
+ len(question["options"]) == 4 and
145
+ all(isinstance(opt, str) for opt in question["options"]) and
146
+ isinstance(question["correct_answer"], (int, str)) and
147
+ int(question["correct_answer"]) in range(4)
148
+ )
149
+ except Exception as e:
150
+ print(f"Question validation error: {str(e)}")
151
+ return False
152
 
153
  class FontManager:
154
  """Manages font installation and loading for the certificate generator"""
 
596
  # State management
597
  current_questions = gr.State([])
598
  current_question_idx = gr.State(0)
599
+ answer_state = gr.State([None] * 5)
600
 
601
  # Header
602
  gr.Markdown("""
 
606
 
607
  with gr.Tabs() as tabs:
608
  # Profile Setup Tab
609
+ with gr.Tab(id=1, label="📋 Step 1: Profile Setup"):
610
  with gr.Row():
611
  name = gr.Textbox(label="Full Name", placeholder="Enter your full name")
612
  email = gr.Textbox(label="Email", placeholder="Enter your email")
 
632
  generate_btn = gr.Button("Generate Assessment", variant="primary", size="lg")
633
 
634
  # Assessment Tab
635
+ with gr.Tab(id=2, label="📝 Step 2: Take Assessment"):
636
+ with gr.Column() as main_container:
637
+ # Questions Section
638
+ with gr.Column(visible=True) as question_box:
639
+ question_display = gr.Markdown("")
 
 
 
640
  current_options = gr.Radio(
641
  choices=[],
642
  label="Select your answer:",
643
  visible=False
644
  )
645
 
 
646
  with gr.Row():
647
  prev_btn = gr.Button("← Previous", variant="secondary", size="sm")
648
  question_counter = gr.Markdown("Question 1")
 
650
 
651
  gr.Markdown("---") # Separator
652
 
653
+ submit_btn = gr.Button(
654
+ "Submit Assessment",
655
+ variant="primary",
656
+ size="lg"
657
+ )
658
+
659
+ # Results Section in Assessment Tab
660
+ with gr.Column(visible=False) as results_group:
661
+ result_message = gr.Markdown(
662
+ label="Result",
663
+ show_label=True
664
+ )
665
+ feedback_box = gr.Markdown(
666
+ label="Detailed Feedback",
667
+ show_label=True
668
+ )
669
+ gr.Markdown("---") # Separator
670
+ with gr.Row(equal_height=True):
671
  reset_btn = gr.Button(
672
  "Reset Quiz",
673
  variant="secondary",
674
+ size="lg",
675
+ visible=False
676
  )
677
+ view_cert_btn = gr.Button(
678
+ "View Certificate",
679
+ variant="primary",
680
+ size="lg",
681
+ visible=False
682
+ )
683
+
684
+ # Certification Tab (Hidden by default)
685
+ with gr.Tab(id=3, label="🎓 Step 3: Get Certified", visible=False) as cert_tab:
686
+ score_display = gr.Number(label="Your Score", visible=False)
 
 
 
 
 
 
 
 
 
 
 
 
687
  course_name = gr.Textbox(
688
  label="Certification Title",
689
+ value="Professional Assessment Certification",
690
+ interactive=False # Make it non-editable
691
  )
692
  certificate_display = gr.Image(label="Your Certificate")
693
 
694
+ # Update view_cert_btn click handler to show certification tab
695
+ def show_certificate_tab():
696
+ return [
697
+ gr.update(visible=True), # Make cert_tab visible
698
+ gr.update(selected=3) # Switch to cert_tab
699
+ ]
700
+
701
+
702
  # Helper Functions
703
  def on_generate_questions(text, num_questions):
704
+ if not text.strip():
705
+ return [
706
+ "",
707
+ gr.update(visible=False),
708
+ gr.update(choices=[], visible=False),
709
+ "",
710
+ [],
711
+ 0,
712
+ [None] * 5,
713
+ gr.update(selected=1),
714
+ gr.update(visible=False),
715
+ gr.update(visible=False)
716
+ ]
717
+
718
  success, questions = quiz_app.generate_questions(text, num_questions)
719
+ if not success or not questions:
720
  return [
721
+ "",
722
+ gr.update(visible=False),
723
+ gr.update(choices=[], visible=False),
724
+ "",
725
+ [],
726
+ 0,
727
+ [None] * 5,
728
+ gr.update(selected=1),
729
+ gr.update(visible=False),
730
+ gr.update(visible=False)
731
  ]
732
 
 
 
733
  question = questions[0]
734
+ question_md = f"""### Question 1
735
+ {question.question}"""
 
 
736
 
737
  return [
738
+ question_md,
739
+ gr.update(visible=True),
740
  gr.update(
741
  choices=question.options,
742
  value=None,
743
  visible=True,
744
+ label="Select your answer:"
745
+ ),
746
+ f"Question 1 of {len(questions)}",
747
+ questions,
748
+ 0,
749
+ [None] * len(questions),
750
+ gr.update(selected=2),
751
+ gr.update(visible=False),
752
+ gr.update(visible=False)
 
753
  ]
754
+
755
  def navigate(direction, current_idx, questions, answers, current_answer):
 
756
  if not questions:
757
+ return [0, answers, "", gr.update(choices=[], visible=False), "", gr.update(visible=False)]
 
 
 
 
 
 
 
758
 
759
  new_answers = list(answers)
760
+ if current_answer is not None and 0 <= current_idx < len(new_answers):
761
  new_answers[current_idx] = current_answer
762
 
763
  new_idx = max(0, min(len(questions) - 1, current_idx + direction))
764
  question = questions[new_idx]
765
 
766
+ question_md = f"""### Question {new_idx + 1}
767
+ {question.question}"""
768
+
769
  return [
770
  new_idx,
771
  new_answers,
772
+ question_md,
773
  gr.update(
774
  choices=question.options,
775
  value=new_answers[new_idx] if new_idx < len(new_answers) else None,
776
  visible=True,
777
+ label="Select your answer:"
778
  ),
779
  f"Question {new_idx + 1} of {len(questions)}",
780
  gr.update(visible=True)
781
+ ]
 
 
 
 
 
782
 
783
  def update_answer_state(answer, idx, current_answers):
784
  new_answers = list(current_answers)
 
792
 
793
  def view_certificate():
794
  """Navigate to certificate tab"""
795
+ return gr.update(selected=3)
796
+
797
+ def handle_prev(current_idx, questions, answers, current_answer):
798
+ return navigate(-1, current_idx, questions, answers, current_answer)
799
+
800
+ def handle_next(current_idx, questions, answers, current_answer):
801
+ return navigate(1, current_idx, questions, answers, current_answer)
802
 
803
  def on_submit(questions, answers, current_idx, current_answer):
804
+ """Handle quiz submission with proper Markdown rendering and emojis"""
805
  final_answers = list(answers)
806
  if 0 <= current_idx < len(final_answers):
807
  final_answers[current_idx] = current_answer
 
813
  0,
814
  "",
815
  gr.update(visible=True),
816
+ gr.update(visible=True),
817
+ gr.update(visible=False),
818
  gr.update(visible=False)
819
  ]
820
 
821
  score, passed, feedback = quiz_app.calculate_score(final_answers[:len(questions)])
822
 
823
+ # Create feedback content using proper Markdown with emojis
824
+ feedback_content = f"""# Assessment Results
825
+
826
+ **Score: {score:.1f}%**
827
+
828
+ """
829
+
830
  for i, (q, f) in enumerate(zip(questions, feedback)):
831
+ icon = "✅" if f.is_correct else "❌"
832
  color = "green" if f.is_correct else "red"
 
833
 
834
+ # Using markdown syntax with color formatting
835
+ feedback_content += f"""### Question {i+1}
836
  {q.question}
837
+
838
+ {icon} **Your answer:** {f.selected or 'No answer'}
839
+ {'' if f.is_correct else f'**Correct answer:** {f.correct_answer}'}
840
+
841
+ """
842
+
843
+ # Add summary box
844
+ if passed:
845
+ feedback_content += f"""
846
+ ---
847
+ ## 🎉 Congratulations!
848
+ You passed with a score of {score:.1f}%!
849
+ """
850
  else:
851
+ feedback_content += f"""
852
+ ---
853
+ ## Need Improvement
854
+ You scored {score:.1f}%. You need 80% or higher to pass.
855
+ Please try again.
856
+ """
857
 
858
  return [
859
+ feedback_content, # feedback_box
860
+ gr.update(visible=True), # results_group
861
+ score, # score_display
862
+ f"Score: {score:.1f}%", # result_message
863
+ gr.update(visible=False), # question_box
864
+ gr.update(visible=not passed), # reset_btn
865
+ gr.update(visible=passed), # view_cert_btn
866
+ gr.update(selected=2) # tabs
867
  ]
868
+
869
+ # Event Handlers
870
+ generate_btn.click(
871
+ fn=on_generate_questions,
872
+ inputs=[text_input, num_questions],
873
+ outputs=[
874
+ question_display,
875
+ question_box,
876
+ current_options,
877
+ question_counter,
878
+ current_questions,
879
+ current_question_idx,
880
+ answer_state,
881
+ tabs,
882
+ results_group,
883
+ view_cert_btn
884
+ ]
885
+ )
886
 
887
+ prev_btn.click(
888
+ fn=handle_prev,
889
+ inputs=[current_question_idx, current_questions, answer_state, current_options],
890
+ outputs=[current_question_idx, answer_state, question_display, current_options, question_counter, question_box]
891
+ )
892
 
893
+ next_btn.click(
894
+ fn=handle_next,
895
+ inputs=[current_question_idx, current_questions, answer_state, current_options],
896
+ outputs=[current_question_idx, answer_state, question_display, current_options, question_counter, question_box]
897
+ )
898
 
899
+ submit_btn.click(
900
+ fn=on_submit,
901
+ inputs=[current_questions, answer_state, current_question_idx, current_options],
902
+ outputs=[
903
+ feedback_box,
904
+ results_group,
905
+ score_display,
906
+ result_message, # Now properly defined
907
+ question_box,
908
+ reset_btn,
909
+ view_cert_btn,
910
+ tabs
911
+ ]
912
+ )
913
 
914
+ reset_btn.click(
915
+ fn=on_generate_questions,
916
+ inputs=[text_input, num_questions],
917
+ outputs=[
918
+ question_display,
919
+ question_box,
920
+ current_options,
921
+ question_counter,
922
+ current_questions,
923
+ current_question_idx,
924
+ answer_state,
925
+ tabs,
926
+ results_group,
927
+ view_cert_btn
928
+ ]
929
+ )
930
 
931
+ view_cert_btn.click(
932
+ fn=show_certificate_tab,
933
+ outputs=[cert_tab, tabs]
934
+ )
935
 
936
+ current_options.change(
937
+ fn=update_answer_state,
938
+ inputs=[current_options, current_question_idx, answer_state],
939
+ outputs=answer_state
940
+ )
941
+
942
+ score_display.change(
943
+ fn=lambda s, n, c, l, p: quiz_app.certificate_generator.generate(s, n, c, l, p) or gr.update(value=None),
944
+ inputs=[score_display, name, course_name, company_logo, participant_photo],
945
+ outputs=certificate_display
946
+ )
947
 
 
 
 
948
  return demo
949
 
950
  if __name__ == "__main__":
951
  demo = create_quiz_interface()
952
+ demo.launch()