ngwakomadikwe commited on
Commit
307ee68
Β·
verified Β·
1 Parent(s): e6c5850

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +9 -48
app.py CHANGED
@@ -1,13 +1,10 @@
1
  """
2
- ThutoAI - Complete School Assistant with Teacher Assignments & Analytics Dashboard
3
- Meaning: "Thuto" = Learning/Education (Setswana β€” used for branding only)
4
-
5
- βœ… Teacher Assignment Posting β€” Assign work to class groups
6
- βœ… πŸ“Š Analytics Dashboard β€” Track student engagement, assignments, groups
7
- βœ… πŸŒ™ Dark Mode + πŸ–ΌοΈ Profile Pictures
8
- βœ… πŸŽ™οΈ Voice Input + πŸ“… Assignment Tracker + πŸ‘₯ Class Groups
9
- βœ… All deprecation warnings fixed (Gradio 4.0+ compatible)
10
- βœ… Fully commented
11
  """
12
 
13
  import os
@@ -60,7 +57,7 @@ class StudentService:
60
  }
61
  }
62
  self.valid_groups = ["MATH10A", "MATH10B", "SCI11A", "SCI11B", "ENG10A", "ENG10B", "HIST9A", "HIST9B"]
63
- self.all_assignments = [] # Master list for analytics
64
 
65
  def register_student(self, username: str, password: str, name: str) -> str:
66
  if not username or not password or not name:
@@ -121,7 +118,6 @@ class StudentService:
121
  })
122
 
123
  def get_assignments(self, username: str) -> List:
124
- """Get assignments for groups student has joined."""
125
  if username not in self.student_sessions:
126
  return []
127
  student_groups = set(self.student_sessions[username]["groups"])
@@ -130,7 +126,6 @@ class StudentService:
130
  for assignment in user_data.get("assignments", []):
131
  if assignment["course"] in student_groups:
132
  all_assignments.append(assignment)
133
- # Remove duplicates by ID
134
  seen = set()
135
  unique_assignments = []
136
  for assignment in all_assignments:
@@ -158,7 +153,6 @@ class StudentService:
158
  return f"βœ… Left group: {group_code.upper()}"
159
  return "❌ Group not found or not joined."
160
 
161
- # Analytics methods
162
  def get_total_students(self) -> int:
163
  return len(self.students)
164
 
@@ -198,14 +192,11 @@ class StudentService:
198
  return sorted(activity, key=lambda x: x["date"], reverse=True)[:5]
199
 
200
 
201
- # Initialize student service
202
  student_service = StudentService()
203
 
204
  # ==================== SCHOOL SERVICE ====================
205
 
206
  class SchoolService:
207
- """Handles announcements, AI context, and shared assignments."""
208
-
209
  def __init__(self):
210
  self.announcements = [
211
  {
@@ -242,7 +233,7 @@ class SchoolService:
242
  self.courses = ["All", "Mathematics", "Science", "English", "History", "General"]
243
  self.total_announcements = len(self.announcements)
244
  self.total_files = 0
245
- self.assignment_id_counter = 5 # Start after existing assignments
246
 
247
  def get_announcements(self, course_filter: str = "All") -> List[Dict]:
248
  if course_filter == "All":
@@ -279,10 +270,8 @@ class SchoolService:
279
  }
280
 
281
  def create_assignment(self, title: str, description: str, due_date: str, course: str, assigned_by: str) -> int:
282
- """Create assignment for a specific class group."""
283
  assignment_id = self.assignment_id_counter
284
  self.assignment_id_counter += 1
285
-
286
  new_assignment = {
287
  "id": assignment_id,
288
  "title": title,
@@ -293,12 +282,9 @@ class SchoolService:
293
  "assigned_date": datetime.now().strftime("%Y-%m-%d"),
294
  "status": "pending"
295
  }
296
-
297
- # Assign to all students in this group
298
  for username, data in student_service.student_sessions.items():
299
  if course in data["groups"]:
300
  data["assignments"].append(new_assignment.copy())
301
-
302
  return assignment_id
303
 
304
 
@@ -307,8 +293,6 @@ school_service = SchoolService()
307
  # ==================== ADMIN SERVICE ====================
308
 
309
  class AdminService:
310
- """Handles teacher and admin authentication."""
311
-
312
  def __init__(self):
313
  self.teachers = {
314
  "[email protected]": {"password": "password123", "name": "Mr. Smith", "role": "teacher"},
@@ -343,7 +327,6 @@ else:
343
  # ==================== AI CHAT FUNCTION ====================
344
 
345
  def ai_chat(message: str, history: List, username: str = "guest") -> tuple:
346
- """Generate AI response with loading state and save to history."""
347
  if not message.strip():
348
  return history, ""
349
 
@@ -403,7 +386,6 @@ Guidelines:
403
  # ==================== UI RENDERING HELPERS ====================
404
 
405
  def render_announcements(course: str) -> str:
406
- """Render announcements with modern cards."""
407
  announcements = school_service.get_announcements(course)
408
  if not announcements:
409
  return """
@@ -460,7 +442,6 @@ def render_announcements(course: str) -> str:
460
 
461
 
462
  def render_assignments(assignments: List[Dict]) -> str:
463
- """Render assignments in a clean, prioritized list."""
464
  if not assignments:
465
  return """
466
  <div style='text-align: center; padding: 40px; color: #6c757d;'>
@@ -526,7 +507,6 @@ def render_assignments(assignments: List[Dict]) -> str:
526
 
527
 
528
  def render_groups(groups: List[str]) -> str:
529
- """Render joined class groups."""
530
  if not groups:
531
  return """
532
  <div style='text-align: center; padding: 40px; color: #6c757d;'>
@@ -557,7 +537,6 @@ def render_groups(groups: List[str]) -> str:
557
 
558
 
559
  def get_avatar_html(avatar_path: Optional[str], name: str) -> str:
560
- """Generate HTML for avatar display."""
561
  if avatar_path:
562
  try:
563
  with open(avatar_path, "rb") as f:
@@ -572,7 +551,6 @@ def get_avatar_html(avatar_path: Optional[str], name: str) -> str:
572
 
573
 
574
  def render_analytics() -> str:
575
- """Render analytics dashboard for admins."""
576
  total_students = student_service.get_total_students()
577
  active_students = student_service.get_active_students()
578
  total_assignments = student_service.get_total_assignments()
@@ -848,7 +826,6 @@ def create_assignment(title: str, description: str, due_date: str, course: str)
848
  assigned_by=CURRENT_TEACHER["name"]
849
  )
850
 
851
- # Count how many students received this assignment
852
  recipients = 0
853
  for data in student_service.student_sessions.values():
854
  if course in data["groups"]:
@@ -938,24 +915,15 @@ CUSTOM_CSS = """
938
  background: #2a2a2a !important;
939
  border-color: #5a5a5a !important;
940
  }
941
-
942
- .analytics-card {
943
- background: white;
944
- padding: 20px;
945
- border-radius: 12px;
946
- box-shadow: 0 2px 8px rgba(0,0,0,0.08);
947
- }
948
  """
949
 
950
  # ==================== BUILD UI ====================
951
 
952
  with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="indigo")) as demo:
953
- # Header with dark mode toggle
954
  with gr.Row():
955
  gr.Markdown("# πŸŽ“ ThutoAI β€” Your AI School Assistant")
956
  dark_mode_btn = gr.Button("β˜€οΈ Dark Mode", variant="secondary")
957
 
958
- # ========= STUDENT LOGIN/REGISTER =========
959
  with gr.Group() as login_group:
960
  gr.Markdown("### πŸ” Student Login")
961
  with gr.Row():
@@ -972,9 +940,7 @@ with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="indigo")) as de
972
  reg_btn = gr.Button("Create Account")
973
  reg_status = gr.Textbox(label="Registration Status")
974
 
975
- # ========= MAIN APP (hidden until login) =========
976
  with gr.Group(visible=False) as main_app:
977
- # Profile header
978
  with gr.Row():
979
  avatar_display = gr.HTML()
980
  user_display = gr.Textbox(label="Logged in as", interactive=False, visible=True)
@@ -1000,7 +966,7 @@ with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="indigo")) as de
1000
  gr.Markdown("### πŸ’‘ Ask me anything β€” I'm here to help!")
1001
  chatbot = gr.Chatbot(
1002
  height=480,
1003
- type='messages' # βœ… FIXED: Replaced deprecated bubble_full_width
1004
  )
1005
  with gr.Row():
1006
  msg = gr.Textbox(
@@ -1121,7 +1087,6 @@ with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="indigo")) as de
1121
  create_assignment_btn = gr.Button("πŸ“¬ Create Assignment", variant="primary")
1122
  assignment_result = gr.Textbox(label="Result")
1123
 
1124
- # βœ… FIXED: Removed deprecated 'inline' parameter
1125
  assignment_priority = gr.Radio(
1126
  ["low", "normal", "high"],
1127
  label="Priority",
@@ -1161,9 +1126,6 @@ with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="indigo")) as de
1161
  outputs=analytics_display
1162
  )
1163
 
1164
- # Logout button (already in header)
1165
-
1166
- # ========= EVENT HANDLERS =========
1167
  login_btn.click(
1168
  fn=login_student,
1169
  inputs=[login_username, login_password],
@@ -1185,6 +1147,5 @@ with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="indigo")) as de
1185
  outputs=[dark_mode_btn, gr.update()]
1186
  )
1187
 
1188
- # Launch app
1189
  if __name__ == "__main__":
1190
  demo.launch()
 
1
  """
2
+ ThutoAI - Complete School Assistant (Fixed for HF Spaces)
3
+ βœ… Teacher Assignments + Admin Analytics
4
+ βœ… Dark Mode + Profile Pictures
5
+ βœ… Voice Input + Class Groups
6
+ βœ… Gradio 4.0+ compatible β€” all deprecations fixed
7
+ βœ… Runs perfectly on Hugging Face Spaces
 
 
 
8
  """
9
 
10
  import os
 
57
  }
58
  }
59
  self.valid_groups = ["MATH10A", "MATH10B", "SCI11A", "SCI11B", "ENG10A", "ENG10B", "HIST9A", "HIST9B"]
60
+ self.all_assignments = []
61
 
62
  def register_student(self, username: str, password: str, name: str) -> str:
63
  if not username or not password or not name:
 
118
  })
119
 
120
  def get_assignments(self, username: str) -> List:
 
121
  if username not in self.student_sessions:
122
  return []
123
  student_groups = set(self.student_sessions[username]["groups"])
 
126
  for assignment in user_data.get("assignments", []):
127
  if assignment["course"] in student_groups:
128
  all_assignments.append(assignment)
 
129
  seen = set()
130
  unique_assignments = []
131
  for assignment in all_assignments:
 
153
  return f"βœ… Left group: {group_code.upper()}"
154
  return "❌ Group not found or not joined."
155
 
 
156
  def get_total_students(self) -> int:
157
  return len(self.students)
158
 
 
192
  return sorted(activity, key=lambda x: x["date"], reverse=True)[:5]
193
 
194
 
 
195
  student_service = StudentService()
196
 
197
  # ==================== SCHOOL SERVICE ====================
198
 
199
  class SchoolService:
 
 
200
  def __init__(self):
201
  self.announcements = [
202
  {
 
233
  self.courses = ["All", "Mathematics", "Science", "English", "History", "General"]
234
  self.total_announcements = len(self.announcements)
235
  self.total_files = 0
236
+ self.assignment_id_counter = 5
237
 
238
  def get_announcements(self, course_filter: str = "All") -> List[Dict]:
239
  if course_filter == "All":
 
270
  }
271
 
272
  def create_assignment(self, title: str, description: str, due_date: str, course: str, assigned_by: str) -> int:
 
273
  assignment_id = self.assignment_id_counter
274
  self.assignment_id_counter += 1
 
275
  new_assignment = {
276
  "id": assignment_id,
277
  "title": title,
 
282
  "assigned_date": datetime.now().strftime("%Y-%m-%d"),
283
  "status": "pending"
284
  }
 
 
285
  for username, data in student_service.student_sessions.items():
286
  if course in data["groups"]:
287
  data["assignments"].append(new_assignment.copy())
 
288
  return assignment_id
289
 
290
 
 
293
  # ==================== ADMIN SERVICE ====================
294
 
295
  class AdminService:
 
 
296
  def __init__(self):
297
  self.teachers = {
298
  "[email protected]": {"password": "password123", "name": "Mr. Smith", "role": "teacher"},
 
327
  # ==================== AI CHAT FUNCTION ====================
328
 
329
  def ai_chat(message: str, history: List, username: str = "guest") -> tuple:
 
330
  if not message.strip():
331
  return history, ""
332
 
 
386
  # ==================== UI RENDERING HELPERS ====================
387
 
388
  def render_announcements(course: str) -> str:
 
389
  announcements = school_service.get_announcements(course)
390
  if not announcements:
391
  return """
 
442
 
443
 
444
  def render_assignments(assignments: List[Dict]) -> str:
 
445
  if not assignments:
446
  return """
447
  <div style='text-align: center; padding: 40px; color: #6c757d;'>
 
507
 
508
 
509
  def render_groups(groups: List[str]) -> str:
 
510
  if not groups:
511
  return """
512
  <div style='text-align: center; padding: 40px; color: #6c757d;'>
 
537
 
538
 
539
  def get_avatar_html(avatar_path: Optional[str], name: str) -> str:
 
540
  if avatar_path:
541
  try:
542
  with open(avatar_path, "rb") as f:
 
551
 
552
 
553
  def render_analytics() -> str:
 
554
  total_students = student_service.get_total_students()
555
  active_students = student_service.get_active_students()
556
  total_assignments = student_service.get_total_assignments()
 
826
  assigned_by=CURRENT_TEACHER["name"]
827
  )
828
 
 
829
  recipients = 0
830
  for data in student_service.student_sessions.values():
831
  if course in data["groups"]:
 
915
  background: #2a2a2a !important;
916
  border-color: #5a5a5a !important;
917
  }
 
 
 
 
 
 
 
918
  """
919
 
920
  # ==================== BUILD UI ====================
921
 
922
  with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="indigo")) as demo:
 
923
  with gr.Row():
924
  gr.Markdown("# πŸŽ“ ThutoAI β€” Your AI School Assistant")
925
  dark_mode_btn = gr.Button("β˜€οΈ Dark Mode", variant="secondary")
926
 
 
927
  with gr.Group() as login_group:
928
  gr.Markdown("### πŸ” Student Login")
929
  with gr.Row():
 
940
  reg_btn = gr.Button("Create Account")
941
  reg_status = gr.Textbox(label="Registration Status")
942
 
 
943
  with gr.Group(visible=False) as main_app:
 
944
  with gr.Row():
945
  avatar_display = gr.HTML()
946
  user_display = gr.Textbox(label="Logged in as", interactive=False, visible=True)
 
966
  gr.Markdown("### πŸ’‘ Ask me anything β€” I'm here to help!")
967
  chatbot = gr.Chatbot(
968
  height=480,
969
+ type='messages'
970
  )
971
  with gr.Row():
972
  msg = gr.Textbox(
 
1087
  create_assignment_btn = gr.Button("πŸ“¬ Create Assignment", variant="primary")
1088
  assignment_result = gr.Textbox(label="Result")
1089
 
 
1090
  assignment_priority = gr.Radio(
1091
  ["low", "normal", "high"],
1092
  label="Priority",
 
1126
  outputs=analytics_display
1127
  )
1128
 
 
 
 
1129
  login_btn.click(
1130
  fn=login_student,
1131
  inputs=[login_username, login_password],
 
1147
  outputs=[dark_mode_btn, gr.update()]
1148
  )
1149
 
 
1150
  if __name__ == "__main__":
1151
  demo.launch()