Benny-Tang commited on
Commit
4680861
·
verified ·
1 Parent(s): c99e2e9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -88
app.py CHANGED
@@ -1,100 +1,118 @@
1
  import os
2
- import json
3
  import random
4
- import subprocess
5
  import gradio as gr
6
 
7
- from agents import AnalyzerAgent, CoachAgent, PredictiveAgent
8
- from ocr_agent import OcrAgent
9
 
10
- # Paths
11
- DATA_DIR = "data"
12
- QUESTIONS_FILE = "questions.json"
13
- os.makedirs(DATA_DIR, exist_ok=True)
14
-
15
- # Load merged question bank
16
- def load_question_bank():
17
- if not os.path.exists(QUESTIONS_FILE) or os.path.getsize(QUESTIONS_FILE) == 0:
18
- return []
19
- try:
20
- with open(QUESTIONS_FILE, "r", encoding="utf-8") as f:
21
- return json.load(f)
22
- except:
23
  return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
- QUESTION_BANK = load_question_bank()
26
-
27
- # Initialize agents
28
- analyzer = AnalyzerAgent()
29
- coach = CoachAgent()
30
- predictor = PredictiveAgent()
31
- ocr_agent = OcrAgent()
32
-
33
- # Functions
34
- def upload_pdf(pdf_file, subject, year, paper):
35
- if pdf_file is None:
36
- return "⚠️ No file uploaded."
37
- questions = ocr_agent.extract_questions(pdf_file, subject, year, paper)
38
- subprocess.run(["python", "merge_questions.py"])
39
- return f"✅ Extracted {len(questions)} questions from {subject.upper()} Paper {paper} ({year})"
40
-
41
- def run_exam(subject, num_questions, include_predicted):
42
- pool = [q for q in QUESTION_BANK if subject.lower() in q["source"]]
43
- if include_predicted:
44
- predicted = predictor.predict(subject)
45
- pool.append({"id": 9999, "text": predicted, "choices": [], "topics": [], "source": "predicted"})
46
- if not pool:
47
- return "⚠️ No questions available.", [], []
48
- selected = random.sample(pool, min(num_questions, len(pool)))
49
- q_texts = [q["text"] for q in selected]
50
- q_ids = [q["id"] for q in selected]
51
- return "\n\n".join(q_texts), q_ids, [q.get("choices", []) for q in selected]
52
-
53
- def submit_answers(q_ids, answers):
54
- correct_answers = ["A"] * len(q_ids) # Placeholder until scheme data ready
55
- score, analysis = analyzer.analyze(answers, correct_answers)
56
- return f"Score: {score}/{len(q_ids)}", "\n".join(analysis)
57
-
58
- def essay_feedback(essay_text):
59
- return coach.coach(essay_text)
60
 
 
61
  # Gradio UI
62
- with gr.Blocks() as demo:
63
- gr.Markdown("## 📝 SPM Exam Simulator (Form 5)")
64
-
65
- with gr.Tab("Upload Past Papers"):
66
- pdf_file = gr.File(label="Upload PDF", type="filepath")
67
- subject = gr.Dropdown(["BM", "English", "Math", "History", "Science", "MoralStudies"], label="Subject")
68
- year = gr.Number(label="Year", value=2018, precision=0)
69
- paper = gr.Dropdown([1, 2], label="Paper")
70
- upload_btn = gr.Button("Process PDF")
71
- upload_out = gr.Textbox(label="Upload Status")
72
- upload_btn.click(fn=upload_pdf, inputs=[pdf_file, subject, year, paper], outputs=upload_out)
73
-
74
- with gr.Tab("Paper 2 Simulator"):
75
- subject_sim = gr.Dropdown(["BM", "English", "Math", "History", "Science", "MoralStudies"], label="Subject")
76
- num_q = gr.Slider(1, 50, step=1, value=10, label="Number of Questions")
77
- include_pred = gr.Checkbox(label="Include AI-Predicted")
78
- run_btn = gr.Button("Start Simulation")
79
- q_out = gr.Textbox(label="Questions")
80
- ids_out = gr.State()
81
- choices_out = gr.State()
82
- run_btn.click(fn=run_exam, inputs=[subject_sim, num_q, include_pred], outputs=[q_out, ids_out, choices_out])
83
-
84
- ans_in = gr.Textbox(label="Your Answers (comma-separated, e.g. A,B,C)")
85
- submit_btn = gr.Button("Submit")
86
- score_out = gr.Textbox(label="Score")
87
- analysis_out = gr.Textbox(label="Analysis")
88
- submit_btn.click(fn=lambda ids, ans: submit_answers(ids, ans.split(",")),
89
- inputs=[ids_out, ans_in], outputs=[score_out, analysis_out])
90
-
91
- with gr.Tab("Paper 1 (Essay Practice)"):
92
- essay_in = gr.Textbox(label="Write your essay here", lines=10)
93
- essay_btn = gr.Button("Get Feedback")
94
- essay_out = gr.Textbox(label="Feedback")
95
- essay_btn.click(fn=essay_feedback, inputs=essay_in, outputs=essay_out)
96
-
97
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
 
100
 
 
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
 
117
 
118