Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import random
|
3 |
+
import string
|
4 |
+
import time
|
5 |
+
from collections import defaultdict
|
6 |
+
|
7 |
+
# Global variables
|
8 |
+
students = {}
|
9 |
+
class_data = defaultdict(lambda: {"students": set(), "questions": []})
|
10 |
+
|
11 |
+
def generate_student_id():
|
12 |
+
return ''.join(random.choices(string.ascii_letters + string.digits, k=12))
|
13 |
+
|
14 |
+
def update_student_state(student_id, class_id, color):
|
15 |
+
if student_id not in students:
|
16 |
+
students[student_id] = {"class_id": class_id, "color": "inactive"}
|
17 |
+
students[student_id]["color"] = color
|
18 |
+
class_data[class_id]["students"].add(student_id)
|
19 |
+
return f"Status updated to {color}"
|
20 |
+
|
21 |
+
def submit_question(student_id, class_id, question):
|
22 |
+
class_data[class_id]["questions"].append({"student_id": student_id, "question": question})
|
23 |
+
return "Question submitted successfully"
|
24 |
+
|
25 |
+
def get_class_stats(class_id):
|
26 |
+
class_students = [s for s in students.values() if s["class_id"] == class_id]
|
27 |
+
total_students = len(class_students)
|
28 |
+
active_students = sum(1 for s in class_students if s["color"] != "inactive")
|
29 |
+
color_counts = {"green": 0, "yellow": 0, "red": 0}
|
30 |
+
for student in class_students:
|
31 |
+
if student["color"] in color_counts:
|
32 |
+
color_counts[student["color"]] += 1
|
33 |
+
|
34 |
+
color_fractions = {color: count / (active_students or 1) for color, count in color_counts.items()}
|
35 |
+
|
36 |
+
return {
|
37 |
+
"total_students": total_students,
|
38 |
+
"active_students": active_students,
|
39 |
+
"color_fractions": color_fractions,
|
40 |
+
"questions": class_data[class_id]["questions"]
|
41 |
+
}
|
42 |
+
|
43 |
+
def create_student_interface(class_id):
|
44 |
+
student_id = gr.State(generate_student_id())
|
45 |
+
|
46 |
+
with gr.Row():
|
47 |
+
with gr.Column(scale=2):
|
48 |
+
color_buttons = [
|
49 |
+
gr.Button("🟢 I'm following along", variant="primary"),
|
50 |
+
gr.Button("🟡 I need clarification", variant="secondary"),
|
51 |
+
gr.Button("🔴 I'm lost, please stop", variant="stop")
|
52 |
+
]
|
53 |
+
with gr.Column(scale=1):
|
54 |
+
status = gr.Textbox(label="Current Status", interactive=False)
|
55 |
+
|
56 |
+
question_input = gr.Textbox(label="Ask a question")
|
57 |
+
submit_button = gr.Button("Submit Question")
|
58 |
+
question_status = gr.Textbox(label="Question Status", interactive=False)
|
59 |
+
|
60 |
+
for button, color in zip(color_buttons, ["green", "yellow", "red"]):
|
61 |
+
button.click(
|
62 |
+
update_student_state,
|
63 |
+
inputs=[student_id, gr.State(class_id), gr.State(color)],
|
64 |
+
outputs=status
|
65 |
+
)
|
66 |
+
|
67 |
+
submit_button.click(
|
68 |
+
submit_question,
|
69 |
+
inputs=[student_id, gr.State(class_id), question_input],
|
70 |
+
outputs=question_status
|
71 |
+
)
|
72 |
+
|
73 |
+
return gr.Interface(
|
74 |
+
fn=lambda: None,
|
75 |
+
inputs=[],
|
76 |
+
outputs=[],
|
77 |
+
title=f"Student Interface - Class {class_id}",
|
78 |
+
description="Use the buttons to indicate your understanding and ask questions.",
|
79 |
+
theme="default"
|
80 |
+
)
|
81 |
+
|
82 |
+
def create_teacher_interface(class_id):
|
83 |
+
def render_stats():
|
84 |
+
stats = get_class_stats(class_id)
|
85 |
+
color_chart = f"""
|
86 |
+
<div style="width: 100%; height: 60px; display: flex;">
|
87 |
+
<div style="width: {stats['color_fractions']['red']*100}%; background-color: #ff6b6b;"></div>
|
88 |
+
<div style="width: {stats['color_fractions']['yellow']*100}%; background-color: #feca57;"></div>
|
89 |
+
<div style="width: {stats['color_fractions']['green']*100}%; background-color: #5cd85c;"></div>
|
90 |
+
</div>
|
91 |
+
"""
|
92 |
+
stats_text = f"""
|
93 |
+
<h3>Class Statistics</h3>
|
94 |
+
<p>Total Students: {stats['total_students']}</p>
|
95 |
+
<p>Active Students: {stats['active_students']}</p>
|
96 |
+
"""
|
97 |
+
questions_text = "<h3>Student Questions</h3>"
|
98 |
+
for q in stats['questions']:
|
99 |
+
questions_text += f"<p><strong>Student {q['student_id'][:4]}...</strong>: {q['question']}</p>"
|
100 |
+
|
101 |
+
return f"{color_chart}<br>{stats_text}<br>{questions_text}"
|
102 |
+
|
103 |
+
stats_html = gr.HTML()
|
104 |
+
refresh_button = gr.Button("Refresh Stats")
|
105 |
+
|
106 |
+
refresh_button.click(render_stats, inputs=[], outputs=[stats_html])
|
107 |
+
|
108 |
+
return gr.Interface(
|
109 |
+
fn=lambda: None,
|
110 |
+
inputs=[],
|
111 |
+
outputs=[],
|
112 |
+
title=f"Teacher Interface - Class {class_id}",
|
113 |
+
description="Monitor student understanding and view questions.",
|
114 |
+
theme="default"
|
115 |
+
)
|
116 |
+
|
117 |
+
def launch_app(class_id):
|
118 |
+
student_interface = create_student_interface(class_id)
|
119 |
+
teacher_interface = create_teacher_interface(class_id)
|
120 |
+
|
121 |
+
app = gr.TabbedInterface(
|
122 |
+
[student_interface, teacher_interface],
|
123 |
+
["Student", "Teacher"],
|
124 |
+
title=f"Fastcups - Class {class_id}",
|
125 |
+
theme=gr.themes.Soft()
|
126 |
+
)
|
127 |
+
app.launch()
|
128 |
+
|
129 |
+
if __name__ == "__main__":
|
130 |
+
class_id = input("Enter class ID: ")
|
131 |
+
launch_app(class_id)
|