Hidayatmahar commited on
Commit
2630bfd
·
verified ·
1 Parent(s): bb121fa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +269 -56
app.py CHANGED
@@ -1,70 +1,283 @@
 
 
 
 
 
1
  import gradio as gr
2
  from reportlab.lib.pagesizes import letter
3
  from reportlab.pdfgen import canvas
4
- import tempfile
5
- import datetime
6
 
7
- # ---------------- Sample Questions (50 MCQs) ---------------- #
8
- questions = [
9
- {"q": "Which number is odd?", "options": ["2", "4", "9", "8"], "answer": "9"},
10
- {"q": "Find the next number: 2, 4, 6, 8, ?", "options": ["10", "12", "14", "16"], "answer": "10"},
11
- {"q": "If CAT = 3120, what does DOG = ?", "options": ["4157", "4127", "4174", "497"], "answer": "4157"},
12
- {"q": "Which shape has 3 sides?", "options": ["Square", "Triangle", "Circle", "Rectangle"], "answer": "Triangle"},
13
- {"q": "5 + (8 - 3) = ?", "options": ["8", "9", "10", "11"], "answer": "10"},
14
- ]
15
-
16
- # repeat same set to make 50 questions for demo (replace with real ones later)
17
- while len(questions) < 50:
18
- questions.append(questions[len(questions) % 5])
19
-
20
- # ---------------- Test Function ---------------- #
21
- def evaluate(name, father, roll, *answers):
22
- score = 0
23
- for i, ans in enumerate(answers):
24
- if ans == questions[i]["answer"]:
25
- score += 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- percentage = round((score / len(questions)) * 100, 2)
 
 
 
 
 
 
 
 
 
 
28
 
29
- # Generate PDF result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
31
  c = canvas.Canvas(temp_file.name, pagesize=letter)
32
- c.setFont("Helvetica-Bold", 14)
33
- c.drawString(200, 750, "5th Class Intelligence Test Result")
34
  c.setFont("Helvetica", 12)
35
- c.drawString(50, 700, f"Name: {name}")
36
- c.drawString(50, 680, f"Father's Name: {father}")
37
- c.drawString(50, 660, f"Roll Number: {roll}")
38
- c.drawString(50, 640, f"Score: {score} / {len(questions)}")
39
- c.drawString(50, 620, f"Percentage: {percentage}%")
40
- c.drawString(50, 600, f"Date: {datetime.date.today()}")
 
 
 
 
41
  c.save()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
- return f"Score: {score}/{len(questions)}\nPercentage: {percentage}%", temp_file.name
44
-
45
- # ---------------- Build Gradio UI ---------------- #
46
- inputs = [
47
- gr.Textbox(label="Student Name"),
48
- gr.Textbox(label="Father's Name"),
49
- gr.Textbox(label="Roll Number"),
50
- ]
51
-
52
- # Add 50 dropdowns for questions
53
- for i, q in enumerate(questions):
54
- inputs.append(gr.Radio(label=f"Q{i+1}. {q['q']}", choices=q["options"], type="value"))
55
-
56
- outputs = [
57
- gr.Textbox(label="Result"),
58
- gr.File(label="Download Result PDF")
59
- ]
60
-
61
- demo = gr.Interface(
62
- fn=evaluate,
63
- inputs=inputs,
64
- outputs=outputs,
65
- title="5th Class Intelligence Test",
66
- description="Answer 50 Intelligence Questions and Download Your Result Card."
67
- )
68
 
69
  if __name__ == "__main__":
70
  demo.launch()
 
1
+ import random
2
+ import datetime
3
+ import tempfile
4
+ from typing import List, Dict
5
+
6
  import gradio as gr
7
  from reportlab.lib.pagesizes import letter
8
  from reportlab.pdfgen import canvas
 
 
9
 
10
+ # --------------------------- Question Pool Generator --------------------------- #
11
+ def generate_question_pool(seed: int = 42) -> List[Dict]:
12
+ random.seed(seed)
13
+ pool = []
14
+
15
+ # Helper to add a question
16
+ def add(q, options, answer):
17
+ pool.append({"q": q, "options": options, "answer": answer})
18
+
19
+ # 1) Simple sequences
20
+ for start, step in [(2,2), (3,3), (5,5), (1,4), (7,2), (10,5), (4,6)]:
21
+ seq = [start + i*step for i in range(4)]
22
+ next_val = start + 4*step
23
+ wrongs = [next_val + d for d in (-2, 2, 4)]
24
+ options = [str(next_val)] + list(map(str, wrongs))
25
+ random.shuffle(options)
26
+ add(f"Find the next number: {', '.join(map(str, seq))}, ?", options, str(next_val))
27
+
28
+ # 2) Even/odd identification
29
+ for n in [9, 11, 13, 15, 17, 19, 21, 23]:
30
+ options = ["2", "4", "6", str(n)]
31
+ random.shuffle(options)
32
+ add("Which number is odd?", options, str(n))
33
+
34
+ for n in [12, 18, 24, 36, 44, 56, 64, 88]:
35
+ options = [str(n), "15", "21", "27"]
36
+ random.shuffle(options)
37
+ add("Which number is even?", options, str(n))
38
+
39
+ # 3) Odd one out (multiples)
40
+ for base, group in [(3,[6,9,12,14]), (5,[10,20,25,24]), (4,[8,16,12,18]), (7,[14,21,28,26])]:
41
+ # The last one in group is the odd one out
42
+ options = list(map(str, group))
43
+ answer = str([x for x in group if x % base != 0][0])
44
+ random.shuffle(options)
45
+ add("Find the odd one out.", options, answer)
46
+
47
+ # 4) Shapes
48
+ add("Which shape has 3 sides?", ["Square", "Triangle", "Circle", "Rectangle"], "Triangle")
49
+ add("Which shape has no sides?", ["Triangle", "Square", "Circle", "Pentagon"], "Circle")
50
+ add("How many sides does a rectangle have?", ["3", "4", "5", "6"], "4")
51
+ add("A square has all sides...", ["Unequal", "Equal", "Curved", "Zero"], "Equal")
52
+
53
+ # 5) Simple arithmetic in brackets
54
+ for a,b,c in [(5,8,3),(10,6,4),(7,9,5),(12,7,2),(8,5,1)]:
55
+ expr = f"{a} + ({b} - {c})"
56
+ val = a + (b - c)
57
+ options = [str(val), str(val+1), str(val-1), str(val+2)]
58
+ random.shuffle(options)
59
+ add(f"{expr} = ?", options, str(val))
60
+
61
+ # 6) Greatest/Smallest numbers
62
+ for group in [[12,7,19,3],[45,60,58,59],[101,99,100,98],[31,29,35,33]]:
63
+ options = list(map(str, group))
64
+ answer = str(max(group))
65
+ random.shuffle(options)
66
+ add("Which is the greatest number?", options, answer)
67
+
68
+ for group in [[12,7,19,3],[45,60,58,59],[101,99,100,98],[31,29,35,33]]:
69
+ options = list(map(str, group))
70
+ answer = str(min(group))
71
+ random.shuffle(options)
72
+ add("Which is the smallest number?", options, answer)
73
+
74
+ # 7) Word analogies (simple & age-appropriate)
75
+ analogies = [
76
+ ("CAT is to KITTEN as DOG is to ___", ["CUB", "CALF", "PUPPY", "COLT"], "PUPPY"),
77
+ ("HEN is to CHICK as COW is to ___", ["CALF", "PUPPY", "LAMB", "FOAL"], "CALF"),
78
+ ("SUN is to DAY as MOON is to ___", ["LIGHT", "NIGHT", "STAR", "SKY"], "NIGHT"),
79
+ ("BIRD is to NEST as BEE is to ___", ["HIVE", "HOLE", "CAVE", "WEB"], "HIVE"),
80
+ ("TEACHER is to SCHOOL as DOCTOR is to ___", ["HOSPITAL", "MARKET", "HOME", "PARK"], "HOSPITAL"),
81
+ ("HAND is to GLOVE as FOOT is to ___", ["HAT", "SOCK", "BAG", "BELT"], "SOCK"),
82
+ ("FISH is to WATER as CAMEL is to ___", ["DESERT", "RIVER", "FOREST", "ICE"], "DESERT"),
83
+ ("EAR is to HEAR as EYE is to ___", ["SEE", "TOUCH", "SMELL", "TASTE"], "SEE"),
84
+ ("RAIN is to WET as FIRE is to ___", ["HOT", "COLD", "DRY", "DARK"], "HOT"),
85
+ ("DAY is to LIGHT as NIGHT is to ___", ["BLACK", "DARK", "WHITE", "SUN"], "DARK"),
86
+ ]
87
+ for q, opts, ans in analogies:
88
+ options = opts[:]
89
+ random.shuffle(options)
90
+ add(q, options, ans)
91
+
92
+ # 8) Simple ordering
93
+ ordering_groups = [
94
+ ["April", "June", "May", "March"],
95
+ ["Monday", "Wednesday", "Friday", "Sunday"],
96
+ ["One", "Three", "Two", "Four"],
97
+ ["Spring", "Summer", "Winter", "Autumn"],
98
+ ]
99
+ for group in ordering_groups:
100
+ # Ask which comes first alphabetically
101
+ options = group[:]
102
+ answer = min(group)
103
+ random.shuffle(options)
104
+ add("Which comes first alphabetically?", options, answer)
105
+
106
+ # 9) Simple logic
107
+ logic_qs = [
108
+ ("If a clock shows 3 o'clock, the hour hand points to ___", ["3", "6", "9", "12"], "3"),
109
+ ("Ali is older than Sara, and Sara is older than Hamid. Who is the youngest?", ["Ali", "Sara", "Hamid", "All same"], "Hamid"),
110
+ ("A bicycle has ___ wheels.", ["1", "2", "3", "4"], "2"),
111
+ ("Which is a mammal?", ["Frog", "Shark", "Whale", "Lizard"], "Whale"),
112
+ ("Which animal cannot fly?", ["Sparrow", "Eagle", "Bat", "Penguin"], "Penguin"),
113
+ ]
114
+ for q, opts, ans in logic_qs:
115
+ options = opts[:]
116
+ random.shuffle(options)
117
+ add(q, options, ans)
118
+
119
+ # 10) Simple fractions (very basic)
120
+ add("Which is larger?", ["1/2", "1/3", "1/4", "1/5"], "1/2")
121
+ add("Which is smaller?", ["3/4", "1/2", "1/3", "1/5"], "1/5")
122
 
123
+ # 11) Patterns with letters/numbers
124
+ patterns = [
125
+ ("What comes next? A, C, E, G, ___", ["H", "I", "J", "K"], "I"),
126
+ ("What comes next? B, D, F, H, ___", ["I", "J", "K", "L"], "J"),
127
+ ("What comes next? 5, 10, 15, 20, ___", ["22", "24", "25", "30"], "25"),
128
+ ("What comes next? 1, 4, 9, 16, ___", ["20", "24", "25", "30"], "25"),
129
+ ]
130
+ for q, opts, ans in patterns:
131
+ options = opts[:]
132
+ random.shuffle(options)
133
+ add(q, options, ans)
134
 
135
+ # 12) Simple sums & differences
136
+ for a,b in [(25,17),(40,12),(33,9),(18,7),(29,16)]:
137
+ val = a + b
138
+ options = [str(val), str(val-1), str(val+1), str(val-2)]
139
+ random.shuffle(options)
140
+ add(f"{a} + {b} = ?", options, str(val))
141
+
142
+ for a,b in [(25,17),(40,12),(33,9),(18,7),(29,16)]:
143
+ val = a - b
144
+ options = [str(val), str(val-1), str(val+1), str(val+2)]
145
+ random.shuffle(options)
146
+ add(f"{a} - {b} = ?", options, str(val))
147
+
148
+ # Ensure pool size > 100
149
+ # Add a few quick general knowledge suitable for 5th grade
150
+ gk = [
151
+ ("How many days are there in a week?", ["5", "6", "7", "8"], "7"),
152
+ ("How many months are there in a year?", ["10", "11", "12", "13"], "12"),
153
+ ("Which planet do we live on?", ["Mars", "Venus", "Earth", "Jupiter"], "Earth"),
154
+ ("How many letters are there in the English alphabet?", ["24", "25", "26", "27"], "26"),
155
+ ("What do bees make?", ["Milk", "Honey", "Juice", "Oil"], "Honey"),
156
+ ("What do we breathe in?", ["Carbon dioxide", "Oxygen", "Hydrogen", "Nitrogen"], "Oxygen"),
157
+ ("Which color results by mixing red and blue?", ["Green", "Purple", "Orange", "Brown"], "Purple"),
158
+ ("Which season is the coldest?", ["Summer", "Winter", "Spring", "Autumn"], "Winter"),
159
+ ("Which sense organ helps us smell?", ["Ear", "Nose", "Tongue", "Skin"], "Nose"),
160
+ ("How many sides does a triangle have?", ["2", "3", "4", "5"], "3"),
161
+ ]
162
+ for q, opts, ans in gk:
163
+ options = opts[:]
164
+ random.shuffle(options)
165
+ add(q, options, ans)
166
+
167
+ return pool
168
+
169
+ QUESTION_POOL = generate_question_pool(seed=2025)
170
+
171
+ # --------------------------- Helpers --------------------------- #
172
+ def sample_quiz(n=50) -> List[Dict]:
173
+ """Sample n questions and shuffle options per question"""
174
+ qs = random.sample(QUESTION_POOL, k=n)
175
+ # Shuffle options & keep correct as text
176
+ quiz = []
177
+ for item in qs:
178
+ opts = item["options"][:]
179
+ random.shuffle(opts)
180
+ quiz.append({
181
+ "q": item["q"],
182
+ "options": opts,
183
+ "answer": item["answer"]
184
+ })
185
+ return quiz
186
+
187
+ def build_pdf(name, father, roll, score, total):
188
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
189
  c = canvas.Canvas(temp_file.name, pagesize=letter)
190
+ c.setFont("Helvetica-Bold", 16)
191
+ c.drawString(140, 760, "5th Class Intelligence Test - Result Card")
192
  c.setFont("Helvetica", 12)
193
+ c.drawString(50, 720, f"Name: {name}")
194
+ c.drawString(50, 700, f"Father's Name: {father}")
195
+ c.drawString(50, 680, f"Roll Number: {roll}")
196
+ percentage = round((score/total)*100, 2)
197
+ c.drawString(50, 660, f"Score: {score} / {total}")
198
+ c.drawString(50, 640, f"Percentage: {percentage}%")
199
+ c.drawString(50, 620, f"Date: {datetime.date.today().isoformat()}")
200
+ c.line(50, 610, 560, 610)
201
+ c.setFont("Helvetica-Oblique", 10)
202
+ c.drawString(50, 595, "Well done! Keep practicing logical thinking, patterns, and basic math.")
203
  c.save()
204
+ return temp_file.name
205
+
206
+ # --------------------------- Gradio App Logic --------------------------- #
207
+ def start_test(name, father, roll):
208
+ if not name or not father or not roll:
209
+ return ([gr.update()] * 50) + [gr.update(visible=False), "Please fill all details to start the test.", None, None]
210
+
211
+ quiz = sample_quiz(50)
212
+ # Build updates for 50 Radio components
213
+ updates = []
214
+ for i in range(50):
215
+ q = quiz[i]
216
+ updates.append(
217
+ gr.update(label=f"Q{i+1}. {q['q']}", choices=q["options"], value=None)
218
+ )
219
+ # Show quiz box, store quiz in JSON string in state
220
+ return updates + [gr.update(visible=True), "Test loaded. Answer all questions and click Submit.", quiz, {"name": name, "father": father, "roll": roll}]
221
+
222
+ def submit_test(*args):
223
+ # args: 50 answers + quiz_state + info_state
224
+ answers = list(args[:50])
225
+ quiz = args[50] # list of dicts
226
+ info = args[51] # dict with name/father/roll
227
+
228
+ if not quiz or not isinstance(quiz, list):
229
+ return "Please start the test first.", None
230
+
231
+ score = 0
232
+ for i, sel in enumerate(answers):
233
+ correct = quiz[i]["answer"]
234
+ if sel == correct:
235
+ score += 1
236
+
237
+ result_text = f"Name: {info['name']}\nFather's Name: {info['father']}\nRoll No: {info['roll']}\nScore: {score}/50\nPercentage: {round(score/50*100,2)}%"
238
+ pdf_path = build_pdf(info['name'], info['father'], info['roll'], score, 50)
239
+ return result_text, pdf_path
240
+
241
+ # --------------------------- UI --------------------------- #
242
+ with gr.Blocks(title="5th Class Intelligence Test (Gradio)") as demo:
243
+ gr.Markdown("# 5th Class Intelligence Test\nAnswer 50 questions. Download your result as PDF.")
244
+
245
+ with gr.Group():
246
+ with gr.Row():
247
+ name = gr.Textbox(label="Student Name", placeholder="Enter full name")
248
+ father = gr.Textbox(label="Father's Name", placeholder="Enter father's name")
249
+ roll = gr.Textbox(label="Roll Number", placeholder="Enter roll number")
250
+ start = gr.Button("Start Test", variant="primary")
251
+
252
+ quiz_box = gr.Accordion("Questions (50)", open=True, visible=False)
253
+ radios = []
254
+ with quiz_box:
255
+ for i in range(50):
256
+ r = gr.Radio(label=f"Q{i+1}", choices=["A", "B", "C", "D"], show_label=True)
257
+ radios.append(r)
258
+ submit = gr.Button("Submit")
259
+
260
+ status = gr.Markdown("")
261
+ result_text = gr.Textbox(label="Result", interactive=False)
262
+ result_pdf = gr.File(label="Download Result PDF", interactive=False)
263
+
264
+ quiz_state = gr.State(value=None) # list of q dicts
265
+ info_state = gr.State(value=None) # dict
266
+
267
+ # Start button wires
268
+ start_outputs = radios + [quiz_box, status, quiz_state, info_state]
269
+ start.click(
270
+ start_test,
271
+ inputs=[name, father, roll],
272
+ outputs=start_outputs
273
+ )
274
 
275
+ # Submit button wires
276
+ submit.click(
277
+ submit_test,
278
+ inputs=radios + [quiz_state, info_state],
279
+ outputs=[result_text, result_pdf]
280
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
 
282
  if __name__ == "__main__":
283
  demo.launch()