Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,13 +1,10 @@
|
|
| 1 |
"""
|
| 2 |
-
ThutoAI - Complete School Assistant
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
β
|
| 6 |
-
β
|
| 7 |
-
β
|
| 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 = []
|
| 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
|
| 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'
|
| 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()
|