KareemAlk commited on
Commit
4652f84
·
verified ·
1 Parent(s): 86fd544

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +506 -0
app.py ADDED
@@ -0,0 +1,506 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import gradio as gr
3
+
4
+ # ---------- Helper functions ----------
5
+
6
+ def generate_random_students(n_students: int):
7
+ """
8
+ Generate a random class with names 'Student 1', 'Student 2', ...
9
+ and random grades between 0 and 100.
10
+ """
11
+ students = []
12
+ for i in range(n_students):
13
+ name = f"Student {i + 1}"
14
+ grade = random.randint(0, 100)
15
+ students.append([name, grade])
16
+ return students
17
+
18
+
19
+ def format_students(students):
20
+ """
21
+ Turn a list like [["Student 1", 78], ["Student 2", 92]]
22
+ into: [Student 1 (78), Student 2 (92)]
23
+ """
24
+ parts = [f"{name} ({grade:g})" for name, grade in students]
25
+ return "[" + ", ".join(parts) + "]"
26
+
27
+
28
+ def is_sorted(students, ascending):
29
+ """
30
+ Check if the list is sorted by grade in the chosen order.
31
+ """
32
+ grades = [g for _, g in students]
33
+ if ascending:
34
+ return all(grades[i] <= grades[i + 1] for i in range(len(grades) - 1))
35
+ else:
36
+ return all(grades[i] >= grades[i + 1] for i in range(len(grades) - 1))
37
+
38
+
39
+ def bubble_rule_should_swap(left_grade, right_grade, ascending):
40
+ """
41
+ Bubble Sort rule: when do we swap?
42
+ """
43
+ if ascending:
44
+ return left_grade > right_grade
45
+ else:
46
+ return left_grade < right_grade
47
+
48
+
49
+ # ---------- Simulation functions ----------
50
+
51
+ def start_simulation(n_students, order_choice):
52
+ """
53
+ Create a random class and set up the first comparison.
54
+ """
55
+ try:
56
+ n_students = int(n_students)
57
+ except ValueError:
58
+ n_students = 5 # default
59
+
60
+ if n_students < 2:
61
+ n_students = 2
62
+
63
+ students = generate_random_students(n_students)
64
+ n = len(students)
65
+ ascending = (order_choice == "Lowest to highest")
66
+
67
+ # Initial pass and position
68
+ i = 0 # pass index
69
+ j = 0 # position index
70
+
71
+ list_text = format_students(students)
72
+
73
+ left_name, left_grade = students[j]
74
+ right_name, right_grade = students[j + 1]
75
+ should_swap = bubble_rule_should_swap(left_grade, right_grade, ascending)
76
+
77
+ comparison_text = (
78
+ f"Pass {i + 1} – Compare {left_name} ({left_grade:g}) "
79
+ f"and {right_name} ({right_grade:g})\n"
80
+ f"Bubble Sort rule for this comparison: "
81
+ f"{'Swap' if should_swap else 'Do NOT swap'}"
82
+ )
83
+
84
+ status = (
85
+ "New random class generated.\n"
86
+ "Use the buttons below to decide each step."
87
+ )
88
+
89
+ # At start: no swaps yet, no accuracy info yet
90
+ swap_count = 0
91
+ correct_decisions = 0
92
+ total_decisions = 0
93
+ finished = False
94
+
95
+ return (
96
+ list_text,
97
+ comparison_text,
98
+ status,
99
+ students,
100
+ i,
101
+ j,
102
+ swap_count,
103
+ finished,
104
+ order_choice,
105
+ correct_decisions,
106
+ total_decisions,
107
+ )
108
+
109
+
110
+ def step_simulation(
111
+ action,
112
+ students,
113
+ pass_index,
114
+ position_index,
115
+ swap_count,
116
+ finished,
117
+ order_choice,
118
+ correct_decisions,
119
+ total_decisions,
120
+ ):
121
+ """
122
+ One step after the teacher presses Swap / Don't swap.
123
+ """
124
+ if students is None:
125
+ return (
126
+ "Click 'Start Simulation' to generate a class.",
127
+ "",
128
+ "No active simulation.",
129
+ students,
130
+ pass_index,
131
+ position_index,
132
+ swap_count,
133
+ finished,
134
+ order_choice,
135
+ correct_decisions,
136
+ total_decisions,
137
+ )
138
+
139
+ if finished:
140
+ list_text = format_students(students)
141
+ status = "Simulation already finished. Start again with a new class."
142
+ return (
143
+ list_text,
144
+ "",
145
+ status,
146
+ students,
147
+ pass_index,
148
+ position_index,
149
+ swap_count,
150
+ finished,
151
+ order_choice,
152
+ correct_decisions,
153
+ total_decisions,
154
+ )
155
+
156
+ n = len(students)
157
+ ascending = (order_choice == "Lowest to highest")
158
+
159
+ i = pass_index
160
+ j = position_index
161
+
162
+ # Current pair before action
163
+ left_name, left_grade = students[j]
164
+ right_name, right_grade = students[j + 1]
165
+ rule_says_swap = bubble_rule_should_swap(left_grade, right_grade, ascending)
166
+
167
+ teacher_chose_swap = (action == "swap")
168
+
169
+ # Update accuracy counters
170
+ total_decisions += 1
171
+ if teacher_chose_swap == rule_says_swap:
172
+ correct_decisions += 1
173
+
174
+ # Apply teacher choice
175
+ if teacher_chose_swap:
176
+ students[j], students[j + 1] = students[j + 1], students[j]
177
+ swap_count += 1
178
+ teacher_action_text = "Teacher chose to SWAP these two students."
179
+ else:
180
+ teacher_action_text = "Teacher chose NOT to swap these two students."
181
+
182
+ # Move to next position
183
+ j += 1
184
+
185
+ status = teacher_action_text
186
+
187
+ # End of this pass?
188
+ if j >= n - 1 - i:
189
+ i += 1
190
+ j = 0
191
+ status += f"\nEnd of pass {i}. Moving to the next pass."
192
+
193
+ # All passes done?
194
+ if i >= n - 1:
195
+ finished = True
196
+ list_text = format_students(students)
197
+ correct_order = is_sorted(students, ascending)
198
+
199
+ # Accuracy
200
+ if total_decisions > 0:
201
+ accuracy = (correct_decisions / total_decisions) * 100
202
+ else:
203
+ accuracy = 0.0
204
+
205
+ comparison_text = ""
206
+ status += "\n\nSimulation finished. Final list locked."
207
+
208
+ if correct_order:
209
+ status += "\nThe final list IS correctly sorted according to the chosen order."
210
+ else:
211
+ status += "\nThe final list is NOT correctly sorted according to the chosen order."
212
+
213
+ status += f"\nTotal swaps you performed: {swap_count}"
214
+ status += f"\nYour decision accuracy: {accuracy:.1f}% "
215
+ status += f"({correct_decisions} out of {total_decisions} decisions matched the Bubble Sort rule.)"
216
+
217
+ return (
218
+ list_text,
219
+ comparison_text,
220
+ status,
221
+ students,
222
+ i,
223
+ j,
224
+ swap_count,
225
+ finished,
226
+ order_choice,
227
+ correct_decisions,
228
+ total_decisions,
229
+ )
230
+
231
+ # Not finished: compute next comparison
232
+ left_name, left_grade = students[j]
233
+ right_name, right_grade = students[j + 1]
234
+ should_swap = bubble_rule_should_swap(left_grade, right_grade, ascending)
235
+
236
+ list_text = format_students(students)
237
+ comparison_text = (
238
+ f"Pass {i + 1} – Compare {left_name} ({left_grade:g}) "
239
+ f"and {right_name} ({right_grade:g})\n"
240
+ f"Bubble Sort rule for this comparison: "
241
+ f"{'Swap' if should_swap else 'Do NOT swap'}"
242
+ )
243
+
244
+ return (
245
+ list_text,
246
+ comparison_text,
247
+ status,
248
+ students,
249
+ i,
250
+ j,
251
+ swap_count,
252
+ finished,
253
+ order_choice,
254
+ correct_decisions,
255
+ total_decisions,
256
+ )
257
+
258
+
259
+ def finish_now(
260
+ students,
261
+ pass_index,
262
+ position_index,
263
+ swap_count,
264
+ finished,
265
+ order_choice,
266
+ correct_decisions,
267
+ total_decisions,
268
+ ):
269
+ """
270
+ Teacher clicks 'Finish now' before all passes complete.
271
+ """
272
+ if students is None:
273
+ return (
274
+ "Click 'Start Simulation' to generate a class.",
275
+ "",
276
+ "No active simulation.",
277
+ students,
278
+ pass_index,
279
+ position_index,
280
+ swap_count,
281
+ finished,
282
+ order_choice,
283
+ correct_decisions,
284
+ total_decisions,
285
+ )
286
+
287
+ ascending = (order_choice == "Lowest to highest")
288
+ list_text = format_students(students)
289
+ correct_order = is_sorted(students, ascending)
290
+
291
+ status = "Teacher finished the list early.\n"
292
+
293
+ if correct_order:
294
+ status += "The final list IS correctly sorted according to the chosen order."
295
+ else:
296
+ status += "The final list is NOT correctly sorted according to the chosen order."
297
+
298
+ if total_decisions > 0:
299
+ accuracy = (correct_decisions / total_decisions) * 100
300
+ else:
301
+ accuracy = 0.0
302
+
303
+ status += f"\nTotal swaps you performed: {swap_count}"
304
+ status += f"\nYour decision accuracy: {accuracy:.1f}% "
305
+ status += f"({correct_decisions} out of {total_decisions} decisions matched the Bubble Sort rule.)"
306
+
307
+ finished = True
308
+
309
+ return (
310
+ list_text,
311
+ "",
312
+ status,
313
+ students,
314
+ pass_index,
315
+ position_index,
316
+ swap_count,
317
+ finished,
318
+ order_choice,
319
+ correct_decisions,
320
+ total_decisions,
321
+ )
322
+
323
+
324
+ # ---------- Gradio UI ----------
325
+
326
+ with gr.Blocks(title="Marking Day – Interactive Bubble Sort") as demo:
327
+ gr.Markdown(
328
+ """
329
+ # Marking Day – Interactive Bubble Sort Grade Ranker
330
+
331
+ You are the teacher on marking day.
332
+
333
+ 1. Choose how many students are in the class.
334
+ 2. Choose the sort order.
335
+ 3. Click **Start Simulation** to generate a random class.
336
+ 4. For each comparison, choose whether to **Swap** or **Don't swap**.
337
+ 5. Watch the list update after each choice.
338
+ 6. Continue until Bubble Sort finishes, or click **Finish now**.
339
+
340
+ At the end, you will see:
341
+ - Whether the final list is correctly sorted.
342
+ - How many swaps you performed.
343
+ - How accurate your decisions were compared to the Bubble Sort rule.
344
+ """
345
+ )
346
+
347
+ n_students_input = gr.Slider(
348
+ minimum=2,
349
+ maximum=20,
350
+ value=5,
351
+ step=1,
352
+ label="Number of students in the class",
353
+ )
354
+
355
+ order_choice = gr.Radio(
356
+ choices=["Highest to lowest", "Lowest to highest"],
357
+ value="Highest to lowest",
358
+ label="Sort order",
359
+ )
360
+
361
+ start_button = gr.Button("Start Simulation", variant="primary")
362
+
363
+ list_display = gr.Markdown(label="Current list")
364
+ comparison_display = gr.Markdown(label="Current comparison")
365
+ status_display = gr.Markdown(label="Status")
366
+
367
+ # State variables
368
+ students_state = gr.State()
369
+ pass_state = gr.State(0)
370
+ position_state = gr.State(0)
371
+ swap_count_state = gr.State(0)
372
+ finished_state = gr.State(False)
373
+ order_state = gr.State("Highest to lowest")
374
+ correct_decisions_state = gr.State(0)
375
+ total_decisions_state = gr.State(0)
376
+
377
+ # Start simulation
378
+ start_button.click(
379
+ fn=start_simulation,
380
+ inputs=[n_students_input, order_choice],
381
+ outputs=[
382
+ list_display,
383
+ comparison_display,
384
+ status_display,
385
+ students_state,
386
+ pass_state,
387
+ position_state,
388
+ swap_count_state,
389
+ finished_state,
390
+ order_state,
391
+ correct_decisions_state,
392
+ total_decisions_state,
393
+ ],
394
+ )
395
+
396
+ with gr.Row():
397
+ swap_button = gr.Button("Swap students")
398
+ dont_swap_button = gr.Button("Don't swap")
399
+ finish_button = gr.Button("Finish now")
400
+
401
+ # Step with Swap
402
+ swap_button.click(
403
+ fn=lambda students, p, pos, swaps, fin, ord_choice, correct_dec, total_dec: step_simulation(
404
+ "swap",
405
+ students,
406
+ p,
407
+ pos,
408
+ swaps,
409
+ fin,
410
+ ord_choice,
411
+ correct_dec,
412
+ total_dec,
413
+ ),
414
+ inputs=[
415
+ students_state,
416
+ pass_state,
417
+ position_state,
418
+ swap_count_state,
419
+ finished_state,
420
+ order_state,
421
+ correct_decisions_state,
422
+ total_decisions_state,
423
+ ],
424
+ outputs=[
425
+ list_display,
426
+ comparison_display,
427
+ status_display,
428
+ students_state,
429
+ pass_state,
430
+ position_state,
431
+ swap_count_state,
432
+ finished_state,
433
+ order_state,
434
+ correct_decisions_state,
435
+ total_decisions_state,
436
+ ],
437
+ )
438
+
439
+ # Step with Don't swap
440
+ dont_swap_button.click(
441
+ fn=lambda students, p, pos, swaps, fin, ord_choice, correct_dec, total_dec: step_simulation(
442
+ "no_swap",
443
+ students,
444
+ p,
445
+ pos,
446
+ swaps,
447
+ fin,
448
+ ord_choice,
449
+ correct_dec,
450
+ total_dec,
451
+ ),
452
+ inputs=[
453
+ students_state,
454
+ pass_state,
455
+ position_state,
456
+ swap_count_state,
457
+ finished_state,
458
+ order_state,
459
+ correct_decisions_state,
460
+ total_decisions_state,
461
+ ],
462
+ outputs=[
463
+ list_display,
464
+ comparison_display,
465
+ status_display,
466
+ students_state,
467
+ pass_state,
468
+ position_state,
469
+ swap_count_state,
470
+ finished_state,
471
+ order_state,
472
+ correct_decisions_state,
473
+ total_decisions_state,
474
+ ],
475
+ )
476
+
477
+ # Finish early
478
+ finish_button.click(
479
+ fn=finish_now,
480
+ inputs=[
481
+ students_state,
482
+ pass_state,
483
+ position_state,
484
+ swap_count_state,
485
+ finished_state,
486
+ order_state,
487
+ correct_decisions_state,
488
+ total_decisions_state,
489
+ ],
490
+ outputs=[
491
+ list_display,
492
+ comparison_display,
493
+ status_display,
494
+ students_state,
495
+ pass_state,
496
+ position_state,
497
+ swap_count_state,
498
+ finished_state,
499
+ order_state,
500
+ correct_decisions_state,
501
+ total_decisions_state,
502
+ ],
503
+ )
504
+
505
+ if __name__ == "__main__":
506
+ demo.launch()