Hidayatmahar commited on
Commit
b592dbc
·
verified ·
1 Parent(s): 2f723e9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -42
app.py CHANGED
@@ -2,6 +2,7 @@ import gradio as gr
2
  import random
3
  import json
4
  from fpdf import FPDF
 
5
 
6
  # Load question bank
7
  with open("questions.json", "r", encoding="utf-8") as f:
@@ -11,55 +12,108 @@ with open("questions.json", "r", encoding="utf-8") as f:
11
  def get_random_questions():
12
  return random.sample(question_bank, 50)
13
 
14
- # Function to evaluate answers
15
  def evaluate_test(student_name, father_name, roll_no, answers, questions):
16
- attempted = sum([1 for a in answers if a is not None and a != ""])
 
17
  correct = 0
18
  for q, ans in zip(questions, answers):
19
- if ans == q["answer"]:
20
- correct += 1
21
- percentage = (correct / len(questions)) * 100
22
-
23
- # Generate PDF report
 
 
 
 
 
 
 
 
 
 
24
  pdf = FPDF()
25
  pdf.add_page()
26
  pdf.set_font("Arial", size=12)
27
  pdf.cell(200, 10, txt="Test Report - Class 6 General Knowledge", ln=True, align="C")
28
  pdf.ln(10)
29
- pdf.cell(200, 10, txt=f"Student Name: {student_name}", ln=True)
30
- pdf.cell(200, 10, txt=f"Father's Name: {father_name}", ln=True)
31
- pdf.cell(200, 10, txt=f"Roll No: {roll_no}", ln=True)
32
- pdf.ln(10)
33
- pdf.cell(200, 10, txt=f"Total Questions: {len(questions)}", ln=True)
34
- pdf.cell(200, 10, txt=f"Attempted: {attempted}", ln=True)
35
- pdf.cell(200, 10, txt=f"Correct: {correct}", ln=True)
36
- pdf.cell(200, 10, txt=f"Percentage: {percentage:.2f}%", ln=True)
37
 
38
- file_name = f"{roll_no}_result.pdf"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  pdf.output(file_name)
40
 
41
- result_summary = f"""
42
- Student: {student_name}
43
- Father's Name: {father_name}
44
- Roll No: {roll_no}
45
-
46
- Attempted: {attempted}
47
- Correct: {correct}
48
- Percentage: {percentage:.2f}%
49
- """
50
 
51
  return result_summary, file_name
52
 
53
- # Build Gradio interface dynamically
54
  def build_interface():
55
- def run_test(student_name, father_name, roll_no, *answers, questions):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  return evaluate_test(student_name, father_name, roll_no, answers, questions)
57
 
58
- # Update UI with fresh random questions
59
- def update_questions(questions):
60
  updates = []
61
  for i, q in enumerate(questions):
62
- updates.append(gr.update(label=f"Q{i+1}: {q['question']}", choices=q["options"], value=None))
 
 
63
  return updates
64
 
65
  with gr.Blocks() as demo:
@@ -70,42 +124,42 @@ def build_interface():
70
  father_name = gr.Textbox(label="Father's Name")
71
  roll_no = gr.Textbox(label="Roll No")
72
 
73
- # State to store current random questions
74
  question_state = gr.State(get_random_questions())
75
 
 
76
  answer_inputs = []
77
  with gr.Accordion("Questions", open=True):
78
  for i in range(50):
79
- q_radio = gr.Radio(label=f"Q{i+1}", choices=[])
80
- answer_inputs.append(q_radio)
81
 
82
  output_text = gr.Textbox(label="Result Summary")
83
  output_file = gr.File(label="Download Result PDF")
84
 
85
- # Buttons
86
  submit_btn = gr.Button("Submit Test", variant="primary")
87
  new_test_btn = gr.Button("New Test", variant="secondary")
88
 
89
- # Load first set of questions when app starts
90
- demo.load(update_questions, inputs=question_state, outputs=answer_inputs)
91
 
92
- # Submit button evaluate answers
93
  submit_btn.click(
94
  run_test,
95
  inputs=[student_name, father_name, roll_no] + answer_inputs + [question_state],
96
- outputs=[output_text, output_file]
97
  )
98
 
99
- # New Test button refresh with fresh 50 questions
100
  def refresh():
101
  qs = get_random_questions()
102
- # return new state + 50 UI updates
103
- return [qs] + update_questions(qs)
104
 
105
  new_test_btn.click(
106
  refresh,
107
  inputs=None,
108
- outputs=[question_state] + answer_inputs
109
  )
110
 
111
  return demo
 
2
  import random
3
  import json
4
  from fpdf import FPDF
5
+ import os
6
 
7
  # Load question bank
8
  with open("questions.json", "r", encoding="utf-8") as f:
 
12
  def get_random_questions():
13
  return random.sample(question_bank, 50)
14
 
15
+ # Function to evaluate answers (handles single or multiple-correct formats)
16
  def evaluate_test(student_name, father_name, roll_no, answers, questions):
17
+ # answers: list of 50 selected option strings (or None)
18
+ attempted = sum(1 for a in answers if a is not None and a != "")
19
  correct = 0
20
  for q, ans in zip(questions, answers):
21
+ if ans is None or ans == "":
22
+ continue
23
+ correct_key = q.get("answer")
24
+ # support either a single string or a list of accepted answers
25
+ if isinstance(correct_key, list):
26
+ if ans in correct_key:
27
+ correct += 1
28
+ else:
29
+ if ans == correct_key:
30
+ correct += 1
31
+
32
+ total = len(questions) if questions else 0
33
+ percentage = (correct / total) * 100 if total > 0 else 0.0
34
+
35
+ # Generate PDF report (safe encoding)
36
  pdf = FPDF()
37
  pdf.add_page()
38
  pdf.set_font("Arial", size=12)
39
  pdf.cell(200, 10, txt="Test Report - Class 6 General Knowledge", ln=True, align="C")
40
  pdf.ln(10)
 
 
 
 
 
 
 
 
41
 
42
+ # Use latin-1 replacement to avoid crashes for non-latin characters
43
+ def safe_text(t):
44
+ return str(t).encode("latin-1", "replace").decode("latin-1")
45
+
46
+ pdf.cell(200, 8, txt=safe_text(f"Student Name: {student_name}"), ln=True)
47
+ pdf.cell(200, 8, txt=safe_text(f"Father's Name: {father_name}"), ln=True)
48
+ pdf.cell(200, 8, txt=safe_text(f"Roll No: {roll_no}"), ln=True)
49
+ pdf.ln(6)
50
+ pdf.cell(200, 8, txt=f"Total Questions: {total}", ln=True)
51
+ pdf.cell(200, 8, txt=f"Attempted: {attempted}", ln=True)
52
+ pdf.cell(200, 8, txt=f"Correct: {correct}", ln=True)
53
+ pdf.cell(200, 8, txt=f"Percentage: {percentage:.2f}%", ln=True)
54
+
55
+ # Save file
56
+ safe_roll = roll_no if roll_no else "student"
57
+ file_name = f"{safe_roll}_result.pdf"
58
+ # ensure unique filename to avoid race conditions
59
+ idx = 0
60
+ base_name = file_name
61
+ while os.path.exists(file_name):
62
+ idx += 1
63
+ file_name = f"{safe_roll}_result_{idx}.pdf"
64
+
65
  pdf.output(file_name)
66
 
67
+ result_summary = (
68
+ f"Student: {student_name}\n"
69
+ f"Father's Name: {father_name}\n"
70
+ f"Roll No: {roll_no}\n\n"
71
+ f"Attempted: {attempted}\n"
72
+ f"Correct: {correct}\n"
73
+ f"Percentage: {percentage:.2f}%"
74
+ )
 
75
 
76
  return result_summary, file_name
77
 
78
+ # Build Gradio interface using Blocks
79
  def build_interface():
80
+ def run_test(*args):
81
+ """
82
+ args layout:
83
+ 0: student_name
84
+ 1: father_name
85
+ 2: roll_no
86
+ 3..52: answer values for Q1..Q50
87
+ 53: questions state (list of 50 question dicts)
88
+ """
89
+ if len(args) < 4:
90
+ return "Invalid input.", None
91
+
92
+ student_name = args[0]
93
+ father_name = args[1]
94
+ roll_no = args[2]
95
+ # answers are args[3] .. args[-2], last arg is questions
96
+ if len(args) < 5:
97
+ # no answers provided
98
+ answers = []
99
+ questions = []
100
+ else:
101
+ questions = args[-1]
102
+ answers = list(args[3:-1])
103
+
104
+ # Safety: if questions is a single dict (shouldn't be) or None, handle gracefully
105
+ if not isinstance(questions, list):
106
+ questions = list(questions) if questions else []
107
+
108
  return evaluate_test(student_name, father_name, roll_no, answers, questions)
109
 
110
+ def update_questions_ui(questions):
111
+ """Return 50 gr.update objects to populate radio labels and choices."""
112
  updates = []
113
  for i, q in enumerate(questions):
114
+ updates.append(
115
+ gr.update(label=f"Q{i+1}: {q.get('question','')}", choices=q.get("options", []), value=None)
116
+ )
117
  return updates
118
 
119
  with gr.Blocks() as demo:
 
124
  father_name = gr.Textbox(label="Father's Name")
125
  roll_no = gr.Textbox(label="Roll No")
126
 
127
+ # State to store current random questions (list of 50 question dicts)
128
  question_state = gr.State(get_random_questions())
129
 
130
+ # Create 50 radio inputs
131
  answer_inputs = []
132
  with gr.Accordion("Questions", open=True):
133
  for i in range(50):
134
+ r = gr.Radio(label=f"Q{i+1}", choices=[])
135
+ answer_inputs.append(r)
136
 
137
  output_text = gr.Textbox(label="Result Summary")
138
  output_file = gr.File(label="Download Result PDF")
139
 
 
140
  submit_btn = gr.Button("Submit Test", variant="primary")
141
  new_test_btn = gr.Button("New Test", variant="secondary")
142
 
143
+ # On load, populate the 50 radios using the question_state
144
+ demo.load(update_questions_ui, inputs=question_state, outputs=answer_inputs)
145
 
146
+ # Submit test -> run_test which parses args and calls evaluate_test
147
  submit_btn.click(
148
  run_test,
149
  inputs=[student_name, father_name, roll_no] + answer_inputs + [question_state],
150
+ outputs=[output_text, output_file],
151
  )
152
 
153
+ # New Test -> generate fresh questions and update state + radios
154
  def refresh():
155
  qs = get_random_questions()
156
+ # return list whose first element will set question_state, followed by 50 radio updates
157
+ return [qs] + update_questions_ui(qs)
158
 
159
  new_test_btn.click(
160
  refresh,
161
  inputs=None,
162
+ outputs=[question_state] + answer_inputs,
163
  )
164
 
165
  return demo