Benny-Tang commited on
Commit
75deb9d
·
verified ·
1 Parent(s): ed730b0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -94
app.py CHANGED
@@ -1,116 +1,127 @@
1
  import os
2
  import random
3
  import gradio as gr
4
-
5
- from demo_questions import QUESTIONS # 60 demo questions
6
-
7
- # ===============================
8
- # Utilities
9
- # ===============================
10
- def get_questions(subject):
11
- """Return 10 random questions for the selected subject."""
12
- if subject not in QUESTIONS or len(QUESTIONS[subject]) == 0:
13
- return []
14
- return random.sample(QUESTIONS[subject], min(10, len(QUESTIONS)))
15
-
16
-
17
- def grade_exam(answers, questions):
18
- """Grade answers, return score + analysis."""
19
- correct = 0
20
- analysis = []
21
- for idx, (ans, q) in enumerate(zip(answers, questions), start=1):
22
- if ans == q["answer"]:
23
- correct += 1
24
- analysis.append(f"✅ Q{idx}: Correct")
 
 
 
25
  else:
26
- analysis.append(
27
- f"❌ Q{idx}: Your answer: {ans or 'No Answer'} | Correct: {q['answer']}"
28
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- score = f"🎯 Final Score: {correct} / {len(questions)}"
31
- return score, "\n".join(analysis)
 
32
 
 
 
33
 
34
- # ===============================
35
- # Gradio UI
36
- # ===============================
37
- with gr.Blocks(title="SPM Exam Simulator") as demo:
38
- gr.Markdown(
39
- """
40
- # 📘 SPM Exam Simulator (Demo Version)
41
 
42
- Welcome! This platform allows Form 5 students to practice **real-world SPM-style exam papers**.
43
- 👉 Select a subject, click **Start Exam**, and answer **10 questions** just like in the actual exam.
44
- """
45
- )
 
 
 
46
 
47
  with gr.Row():
48
- subject_dd = gr.Dropdown(
49
- choices=list(QUESTIONS.keys()),
50
- label="Select Subject",
51
- value="BM",
52
  )
53
- start_btn = gr.Button("🚀 Start Exam", variant="primary")
54
-
55
- with gr.Column() as exam_area:
56
- gr.Markdown("### 📝 Exam Section")
57
- gr.Markdown("Answer all questions below:")
58
-
59
- question_boxes = []
60
- answer_boxes = []
61
-
62
- for i in range(10):
63
- q_text = gr.Markdown(f"Q{i+1}: (Will load after Start)")
64
- q_opts = gr.Radio(choices=[], label=f"Answer Q{i+1}", type="value")
65
- question_boxes.append(q_text)
66
- answer_boxes.append(q_opts)
67
-
68
- submit_btn = gr.Button("✅ Submit Answers", visible=False, variant="primary")
69
-
70
- with gr.Column():
71
- gr.Markdown("### 📊 Results Section")
72
- score_out = gr.Textbox(label="Score", interactive=False)
73
- analysis_out = gr.Textbox(label="Answer Analysis", lines=15, interactive=False)
74
-
75
- # ===========================
76
- # Handlers
77
- # ===========================
78
- def load_exam(subject):
79
- qs = get_questions(subject)
80
- updates = []
81
- for i, q in enumerate(qs):
82
- updates.append(gr.update(value=f"**Q{i+1}:** {q['text']}"))
83
- updates.append(gr.update(choices=q["choices"], value=None))
84
- # Fill remaining slots if less than 10
85
- for j in range(len(qs), 10):
86
- updates.append(gr.update(value=f"Q{j+1}: (No Question)"))
87
- updates.append(gr.update(choices=[], value=None))
88
- return updates + [gr.update(visible=True)], qs
89
-
90
- def submit_exam(*answers, subject, questions):
91
- score, analysis = grade_exam(list(answers), questions)
92
- return score, analysis
93
-
94
- # ===========================
95
- # Events
96
- # ===========================
97
- exam_state = gr.State([])
98
 
99
- start_btn.click(
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  fn=load_exam,
101
- inputs=subject_dd,
102
- outputs=[*question_boxes, *answer_boxes, submit_btn, exam_state],
103
  )
104
 
105
  submit_btn.click(
106
- fn=submit_exam,
107
- inputs=[*answer_boxes, subject_dd, exam_state],
108
- outputs=[score_out, analysis_out],
109
  )
110
 
111
- # ===============================
 
112
  # Launch
113
- # ===============================
114
  if __name__ == "__main__":
115
  demo.launch(server_name="0.0.0.0", server_port=7860)
116
 
@@ -130,3 +141,4 @@ if __name__ == "__main__":
130
 
131
 
132
 
 
 
1
  import os
2
  import random
3
  import gradio as gr
4
+ from demo_questions import QUESTIONS # demo 60 questions
5
+
6
+ # =====================================================
7
+ # Load Exam
8
+ # =====================================================
9
+ def load_exam(subject):
10
+ """Load 10 random questions for a subject (from demo_questions)."""
11
+ try:
12
+ if subject in QUESTIONS:
13
+ qlist = random.sample(QUESTIONS[subject], min(10, len(QUESTIONS[subject])))
14
+ else:
15
+ qlist = []
16
+ except Exception as e:
17
+ print(f"⚠️ Error loading questions for {subject}: {e}")
18
+ qlist = []
19
+
20
+ outputs = []
21
+
22
+ # Generate 10 question slots
23
+ for i in range(10):
24
+ if i < len(qlist):
25
+ q = qlist[i]
26
+ outputs.append(gr.Markdown.update(value=f"**Q{i+1}:** {q['text']}"))
27
+ outputs.append(gr.Radio.update(choices=q['choices'], value=None))
28
  else:
29
+ outputs.append(gr.Markdown.update(value=f"Q{i+1}: (No Question)"))
30
+ outputs.append(gr.Radio.update(choices=[], value=None))
31
+
32
+ # Show submit button
33
+ outputs.append(gr.Button.update(visible=True))
34
+
35
+ # Save state with selected questions
36
+ outputs.append(qlist)
37
+
38
+ return outputs
39
+
40
+
41
+ # =====================================================
42
+ # Submit Answers
43
+ # =====================================================
44
+ def submit_exam_answers(*args):
45
+ """
46
+ Args:
47
+ First 20 are answers (from radios)
48
+ Last one is the state (qlist with correct answers)
49
+ """
50
+ answers = args[:-1]
51
+ qlist = args[-1]
52
+
53
+ if not qlist:
54
+ return "⚠️ No questions loaded.", "", "", ""
55
+
56
+ score = 0
57
+ results = []
58
+ for i, q in enumerate(qlist):
59
+ user_ans = answers[i]
60
+ correct = q.get("answer", None)
61
+ if user_ans == correct:
62
+ score += 1
63
+ results.append(f"✅ Q{i+1}: Correct")
64
+ else:
65
+ results.append(f"❌ Q{i+1}: Your answer: {user_ans}, Correct: {correct}")
66
 
67
+ total = len(qlist)
68
+ analysis = "\n".join(results)
69
+ coach = f"Your Score: {score}/{total}\nKeep practicing to improve!"
70
 
71
+ # Placeholder for predictive agent
72
+ predictions = "📊 Predictive Agent: Future hot topics may include grammar, peribahasa, algebra, and world history."
73
 
74
+ return f"✅ Final Score: {score}/{total}", analysis, coach, predictions
 
 
 
 
 
 
75
 
76
+
77
+ # =====================================================
78
+ # Build Gradio UI
79
+ # =====================================================
80
+ with gr.Blocks(title="SPM Exam Simulator (Form 5)") as demo:
81
+ gr.Markdown("# 🎓 SPM Exam Simulator (Form 5)")
82
+ gr.Markdown("Practice core subjects for upcoming SPM exam (2018–2024 past papers, AI-predicted questions, and OCR upload support).")
83
 
84
  with gr.Row():
85
+ subject = gr.Dropdown(
86
+ ["BM", "English", "Math", "History", "Science", "MoralStudies"],
87
+ label="Choose Subject",
88
+ value="BM"
89
  )
90
+ load_btn = gr.Button("📘 Load Exam")
91
+
92
+ # Question placeholders (10 questions × text + radio)
93
+ q_markdowns = [gr.Markdown(f"Q{i+1}: (Not Loaded)") for i in range(10)]
94
+ q_radios = [gr.Radio(choices=[], label=f"Answer Q{i+1}") for i in range(10)]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
+ # Submit
97
+ submit_btn = gr.Button("✅ Submit Answers", visible=False)
98
+
99
+ # Outputs
100
+ score_out = gr.Markdown()
101
+ analysis_out = gr.Markdown()
102
+ coach_out = gr.Markdown()
103
+ pred_out = gr.Markdown()
104
+
105
+ # Hidden state for selected questions
106
+ state = gr.State([])
107
+
108
+ # Events
109
+ load_btn.click(
110
  fn=load_exam,
111
+ inputs=[subject],
112
+ outputs=q_markdowns + q_radios + [submit_btn, state]
113
  )
114
 
115
  submit_btn.click(
116
+ fn=submit_exam_answers,
117
+ inputs=q_radios + [state],
118
+ outputs=[score_out, analysis_out, coach_out, pred_out]
119
  )
120
 
121
+
122
+ # =====================================================
123
  # Launch
124
+ # =====================================================
125
  if __name__ == "__main__":
126
  demo.launch(server_name="0.0.0.0", server_port=7860)
127
 
 
141
 
142
 
143
 
144
+