Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import sqlite3 | |
| from utils import get_device_id, authenticate_user, get_user_role | |
| from database import ( | |
| init_db, add_student, get_sessions, toggle_session, | |
| get_open_session, has_marked_attendance, mark_attendance, get_attendance_summary | |
| ) | |
| # Initialize the database | |
| init_db() | |
| # Global user state | |
| current_user = {"email": None, "role": None, "device_id": None, "class_id": None} | |
| # Custom theme for the app | |
| theme = gr.themes.Soft( | |
| primary_hue="indigo", | |
| secondary_hue="blue", | |
| neutral_hue="slate", | |
| radius_size=gr.themes.sizes.radius_md, | |
| ).set( | |
| button_primary_background_fill="*primary_500", | |
| button_primary_background_fill_hover="*primary_600", | |
| button_primary_text_color="white", | |
| button_secondary_background_fill="*neutral_100", | |
| button_secondary_background_fill_hover="*neutral_200", | |
| button_secondary_text_color="*neutral_800", | |
| block_label_background_fill="*neutral_50", | |
| block_title_text_weight="600", | |
| ) | |
| # --- Login Logic --- | |
| def login(email, password): | |
| if not email or not password: | |
| return "Please fill in all fields.", gr.update(visible=False), gr.update(visible=True) | |
| device_id = get_device_id() | |
| success, message = authenticate_user(email, password, device_id) | |
| if success: | |
| current_user["email"] = email | |
| current_user["role"] = get_user_role(email) | |
| current_user["device_id"] = device_id | |
| # Get class_id and name | |
| conn = sqlite3.connect("attendance.db") | |
| cursor = conn.cursor() | |
| cursor.execute("SELECT class_id, name FROM users WHERE email=?", (email,)) | |
| row = cursor.fetchone() | |
| current_user["class_id"] = row[0] if row else None | |
| user_name = row[1] if row else email | |
| conn.close() | |
| # Set visibility based on user role | |
| if current_user["role"] == "teacher": | |
| return ( | |
| f"π Welcome, {user_name}!", | |
| gr.update(visible=False), | |
| gr.update(visible=True), | |
| gr.update(selected="teacher_tab" if current_user["role"] == "teacher" else "student_tab") | |
| ) | |
| else: | |
| return ( | |
| f"π Welcome, {user_name}!", | |
| gr.update(visible=False), | |
| gr.update(visible=True), | |
| gr.update(selected="student_tab") | |
| ) | |
| else: | |
| return message, gr.update(visible=True), gr.update(visible=False), gr.update() | |
| def logout(): | |
| # Reset user data | |
| current_user["email"] = None | |
| current_user["role"] = None | |
| current_user["class_id"] = None | |
| current_user["device_id"] = None | |
| # Show login form, hide dashboard | |
| return gr.update(visible=True), gr.update(visible=False) | |
| # --- Teacher Dashboard Functions --- | |
| def teacher_dashboard_view(): | |
| if current_user["role"] != "teacher": | |
| return "Access denied. You are not a teacher." | |
| sessions = get_sessions(current_user["class_id"]) | |
| # Format sessions as a list of dictionaries for the DataFrame | |
| session_data = [ | |
| {"ID": s[0], "Date & Time": s[1], "Status": "π’ Open" if bool(s[2]) else "π΄ Closed"} | |
| for s in sessions | |
| ] | |
| if not session_data: | |
| session_data = [{"ID": "-", "Date & Time": "-", "Status": "-"}] | |
| return session_data | |
| def handle_open_session(): | |
| if current_user["role"] != "teacher": | |
| return "Access denied.", teacher_dashboard_view() | |
| result = toggle_session(current_user["class_id"], True) | |
| return f"β {result}", teacher_dashboard_view() | |
| def handle_close_session(): | |
| if current_user["role"] != "teacher": | |
| return "Access denied.", teacher_dashboard_view() | |
| result = toggle_session(current_user["class_id"], False) | |
| return f"β {result}", teacher_dashboard_view() | |
| def handle_add_student(name, email, password): | |
| if not name or not email or not password: | |
| return "Please fill in all fields." | |
| if current_user["role"] != "teacher": | |
| return "Access denied. You are not authorized to add students." | |
| result = add_student(name, email, password, current_user["class_id"]) | |
| # Clear the form on success | |
| if "successfully" in result: | |
| return f"β {result}", "", "", "" | |
| else: | |
| return f"β {result}", gr.update(), gr.update(), gr.update() | |
| # --- Student Dashboard Functions --- | |
| def handle_mark_attendance(): | |
| if current_user["role"] != "student": | |
| return "Access denied. You are not a student." | |
| session_id = get_open_session(current_user["class_id"]) | |
| if not session_id: | |
| return "β No open attendance session right now.", get_student_attendance_data() | |
| if has_marked_attendance(current_user["email"], session_id): | |
| return "βΉοΈ You have already marked attendance for this session.", get_student_attendance_data() | |
| result = mark_attendance(current_user["email"], session_id) | |
| return f"β {result}", get_student_attendance_data() | |
| def get_student_attendance_data(): | |
| if current_user["role"] != "student": | |
| return [{"Date": "-", "Class": "-"}] | |
| conn = sqlite3.connect("attendance.db") | |
| cursor = conn.cursor() | |
| cursor.execute(""" | |
| SELECT s.date, s.class_id | |
| FROM attendance_logs a | |
| JOIN sessions s ON a.session_id = s.id | |
| WHERE a.student_email=? | |
| ORDER BY s.date DESC | |
| LIMIT 10 | |
| """, (current_user["email"],)) | |
| records = cursor.fetchall() | |
| conn.close() | |
| if not records: | |
| return [{"Date": "-", "Class": "-"}] | |
| return [{"Date": row[0], "Class": row[1]} for row in records] | |
| def get_student_stats(): | |
| if current_user["role"] != "student" or not current_user["email"]: | |
| return "0", "0%" | |
| conn = sqlite3.connect("attendance.db") | |
| cursor = conn.cursor() | |
| # Get total sessions for the student's class | |
| cursor.execute("SELECT COUNT(*) FROM sessions WHERE class_id=?", (current_user["class_id"],)) | |
| total_sessions = cursor.fetchone()[0] | |
| # Get student's attendance count | |
| cursor.execute(""" | |
| SELECT COUNT(*) FROM attendance_logs a | |
| JOIN sessions s ON a.session_id = s.id | |
| WHERE a.student_email=? AND s.class_id=? | |
| """, (current_user["email"], current_user["class_id"])) | |
| attended_sessions = cursor.fetchone()[0] | |
| conn.close() | |
| attendance_percentage = "0%" | |
| if total_sessions > 0: | |
| attendance_percentage = f"{(attended_sessions / total_sessions) * 100:.1f}%" | |
| return str(attended_sessions), attendance_percentage | |
| # --- UI Layout --- | |
| with gr.Blocks(theme=theme, title="Smart Attendance System") as demo: | |
| gr.Markdown( | |
| """ | |
| # π± Smart Attendance System | |
| ### Modern attendance tracking for educational institutions | |
| """ | |
| ) | |
| # Login Section | |
| login_container = gr.Column(visible=True) | |
| with login_container: | |
| with gr.Group(): | |
| gr.Markdown("### Log in to your account") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| email = gr.Textbox( | |
| label="Email Address", | |
| placeholder="Enter your email", | |
| scale=2 | |
| ) | |
| password = gr.Textbox( | |
| label="Password", | |
| placeholder="Enter your password", | |
| type="password", | |
| scale=2 | |
| ) | |
| login_msg = gr.Textbox( | |
| label="", | |
| interactive=False, | |
| visible=True | |
| ) | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Welcome!") | |
| gr.Markdown("Sign in to access the attendance system. Teachers can manage sessions and students, while students can mark their attendance.") | |
| with gr.Row(): | |
| login_btn = gr.Button( | |
| "π Login", | |
| variant="primary", | |
| scale=1 | |
| ) | |
| # Dashboard Section | |
| dashboard_container = gr.Column(visible=False) | |
| with dashboard_container: | |
| with gr.Row(): | |
| user_greeting = gr.Textbox( | |
| value="Welcome!", | |
| label="", | |
| interactive=False | |
| ) | |
| logout_btn = gr.Button( | |
| "πͺ Logout", | |
| variant="secondary", | |
| scale=0 | |
| ) | |
| # Tabs for different interfaces | |
| with gr.Tabs(selected=0) as dashboard_tabs: | |
| # Teacher Dashboard Tab | |
| with gr.TabItem("π¨βπ« Teacher Dashboard", id="teacher_tab"): | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gr.Markdown("### Class Sessions") | |
| with gr.Row(): | |
| open_btn = gr.Button("β Open Session", variant="primary") | |
| close_btn = gr.Button("β Close Session", variant="secondary") | |
| session_status = gr.Textbox(label="", interactive=False) | |
| gr.Markdown("### Session History") | |
| refresh_btn = gr.Button("π Refresh Sessions", variant="secondary") | |
| session_output = gr.DataFrame( | |
| headers=["ID", "Date & Time", "Status"], | |
| datatype=["number", "str", "str"], | |
| label="All Sessions" | |
| ) | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Add New Student") | |
| with gr.Group(): | |
| student_name = gr.Textbox( | |
| label="Student Name", | |
| placeholder="Full name" | |
| ) | |
| student_email = gr.Textbox( | |
| label="Student Email", | |
| placeholder="student@example.com" | |
| ) | |
| student_pass = gr.Textbox( | |
| label="Password", | |
| placeholder="Create password", | |
| type="password" | |
| ) | |
| add_btn = gr.Button("β Add Student", variant="primary") | |
| add_status = gr.Textbox(label="", interactive=False) | |
| # Student Dashboard Tab | |
| with gr.TabItem("π¨βπ Student Dashboard", id="student_tab"): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Quick Actions") | |
| mark_btn = gr.Button("π Mark Attendance", size="lg", variant="primary") | |
| mark_status = gr.Textbox(label="Status", interactive=False) | |
| gr.Markdown("### Your Statistics") | |
| with gr.Row(): | |
| with gr.Column(): | |
| total_attended = gr.Textbox(label="Sessions Attended", interactive=False) | |
| with gr.Column(): | |
| attendance_rate = gr.Textbox(label="Attendance Rate", interactive=False) | |
| with gr.Column(scale=2): | |
| gr.Markdown("### Attendance History") | |
| attendance_table = gr.DataFrame( | |
| headers=["Date", "Class"], | |
| datatype=["str", "str"], | |
| label="Your Recent Attendance", | |
| interactive=False | |
| ) | |
| # Event Bindings | |
| login_btn.click( | |
| fn=login, | |
| inputs=[email, password], | |
| outputs=[user_greeting, login_container, dashboard_container, dashboard_tabs] | |
| ) | |
| logout_btn.click( | |
| fn=logout, | |
| outputs=[login_container, dashboard_container] | |
| ) | |
| # Teacher functions | |
| refresh_btn.click( | |
| fn=teacher_dashboard_view, | |
| outputs=session_output | |
| ) | |
| open_btn.click( | |
| fn=handle_open_session, | |
| outputs=[session_status, session_output] | |
| ) | |
| close_btn.click( | |
| fn=handle_close_session, | |
| outputs=[session_status, session_output] | |
| ) | |
| add_btn.click( | |
| fn=handle_add_student, | |
| inputs=[student_name, student_email, student_pass], | |
| outputs=[add_status, student_name, student_email, student_pass] | |
| ) | |
| # Student functions | |
| mark_btn.click( | |
| fn=handle_mark_attendance, | |
| outputs=[mark_status, attendance_table] | |
| ) | |
| # Dynamic loading handlers | |
| dashboard_tabs.select( | |
| fn=lambda tab: teacher_dashboard_view() if tab == "teacher_tab" else get_student_attendance_data(), | |
| inputs=dashboard_tabs, | |
| outputs=session_output if dashboard_tabs.selected == "teacher_tab" else attendance_table | |
| ) | |
| dashboard_tabs.select( | |
| fn=lambda tab: get_student_stats() if tab == "student_tab" else (None, None), | |
| inputs=dashboard_tabs, | |
| outputs=[total_attended, attendance_rate] | |
| ) | |
| # Footer | |
| gr.Markdown( | |
| """ | |
| --- | |
| ### Smart Attendance - A modern attendance tracking solution | |
| Version 2.0 | Β© 2025 | |
| """ | |
| ) | |
| # Launch the app | |
| if __name__ == "__main__": | |
| demo.launch(share=True) |