|
|
import gradio as gr |
|
|
import os |
|
|
import pandas as pd |
|
|
from datetime import datetime |
|
|
import calendar |
|
|
from db import Database |
|
|
from UserDetail import UserDetail |
|
|
import image |
|
|
import encdec |
|
|
import plotly.graph_objects as go |
|
|
import plotly.express as px |
|
|
|
|
|
|
|
|
db = Database() |
|
|
|
|
|
|
|
|
custom_css = """ |
|
|
.gradio-container { |
|
|
font-family: 'Arial', sans-serif; |
|
|
} |
|
|
.header { |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
padding: 2rem; |
|
|
border-radius: 15px; |
|
|
color: white; |
|
|
text-align: center; |
|
|
margin-bottom: 2rem; |
|
|
} |
|
|
.success-box { |
|
|
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); |
|
|
padding: 1rem; |
|
|
border-radius: 10px; |
|
|
color: white; |
|
|
text-align: center; |
|
|
} |
|
|
.error-box { |
|
|
background: #ff6b6b; |
|
|
padding: 1rem; |
|
|
border-radius: 10px; |
|
|
color: white; |
|
|
} |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def show_home(): |
|
|
"""Display home page with statistics""" |
|
|
stats = db.get_statistics() |
|
|
|
|
|
html = f""" |
|
|
<div style='background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
padding: 2rem; border-radius: 15px; color: white; text-align: center; margin-bottom: 2rem;'> |
|
|
<h1>π Suthukeny Government High School</h1> |
|
|
<p style='font-size: 1.2rem;'>AI-Powered Student Management & Performance Analysis System</p> |
|
|
</div> |
|
|
|
|
|
<div style='display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin: 2rem 0;'> |
|
|
<div style='background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); |
|
|
padding: 1.5rem; border-radius: 10px; color: white; text-align: center;'> |
|
|
<h2 style='margin: 0; font-size: 3rem;'>{stats['total_students']}</h2> |
|
|
<p style='margin: 0.5rem 0 0 0;'>Total Students</p> |
|
|
</div> |
|
|
<div style='background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); |
|
|
padding: 1.5rem; border-radius: 10px; color: white; text-align: center;'> |
|
|
<h2 style='margin: 0; font-size: 3rem;'>{len(stats['class_wise'])}</h2> |
|
|
<p style='margin: 0.5rem 0 0 0;'>Active Classes</p> |
|
|
</div> |
|
|
<div style='background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); |
|
|
padding: 1.5rem; border-radius: 10px; color: white; text-align: center;'> |
|
|
<h2 style='margin: 0; font-size: 3rem;'>{stats['students_with_marks']}</h2> |
|
|
<p style='margin: 0.5rem 0 0 0;'>Students with Records</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<h2>β¨ Key Features</h2> |
|
|
<ul style='font-size: 1.1rem; line-height: 1.8;'> |
|
|
<li>π Face Recognition Login - Secure AI authentication</li> |
|
|
<li>π Complete Student Profiles - Photo ID with full details</li> |
|
|
<li>π Monthly Performance Tracking - 5 subjects with analytics</li> |
|
|
<li>π Visual Analytics - Beautiful interactive charts</li> |
|
|
<li>π Class Rankings - Top performers identification</li> |
|
|
<li>πΎ Persistent Storage - Data never lost</li> |
|
|
</ul> |
|
|
|
|
|
<div style='background: #f0f2f6; padding: 1.5rem; border-radius: 10px; margin-top: 2rem;'> |
|
|
<h3>π How to Use</h3> |
|
|
<ol style='line-height: 1.8;'> |
|
|
<li><strong>Register Student:</strong> Upload photo and enter details</li> |
|
|
<li><strong>Student Login:</strong> Use face recognition to access profile</li> |
|
|
<li><strong>Update Marks:</strong> Teachers enter monthly exam scores</li> |
|
|
<li><strong>View Reports:</strong> Analyze performance with charts</li> |
|
|
<li><strong>Manage Students:</strong> Edit or update student information</li> |
|
|
</ol> |
|
|
</div> |
|
|
|
|
|
<div style='text-align: center; margin-top: 3rem; padding: 2rem; |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
border-radius: 15px; color: white;'> |
|
|
<h3>Suthukeny Government High School</h3> |
|
|
<p>Empowering Education Through Technology π</p> |
|
|
</div> |
|
|
""" |
|
|
return html |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def register_student(photo, name, dob, class_std, section, father_name, mother_name, |
|
|
address, phone, email, blood_group): |
|
|
"""Register a new student with face recognition""" |
|
|
try: |
|
|
|
|
|
if photo is None: |
|
|
return "β Please upload a student photo", None, None |
|
|
|
|
|
if not name or not dob or not class_std or not section: |
|
|
return "β Please fill all required fields (Name, DOB, Class, Section)", None, None |
|
|
|
|
|
|
|
|
student_id = db.generate_student_id(class_std) |
|
|
|
|
|
|
|
|
success, message = image.save_image_from_path(photo, student_id) |
|
|
|
|
|
if not success: |
|
|
return f"β {message}", None, None |
|
|
|
|
|
|
|
|
user_detail = UserDetail( |
|
|
student_id, name, str(dob), class_std, section, |
|
|
father_name or "", mother_name or "", address or "", |
|
|
phone or "", email or "", blood_group or "" |
|
|
) |
|
|
|
|
|
|
|
|
success, db_message = db.insert_student(user_detail) |
|
|
|
|
|
if success: |
|
|
|
|
|
card_html = f""" |
|
|
<div style='background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); |
|
|
padding: 2rem; border-radius: 15px; color: white; text-align: center;'> |
|
|
<h2>β
Registration Successful!</h2> |
|
|
<h1 style='font-size: 2.5rem; margin: 1rem 0;'>Student ID: {student_id}</h1> |
|
|
<p style='font-size: 1.2rem;'>Student registered successfully with face recognition!</p> |
|
|
</div> |
|
|
<div style='background: white; padding: 1.5rem; border-radius: 10px; margin-top: 1rem; border: 2px solid #667eea;'> |
|
|
<h3>Student Information</h3> |
|
|
<p><strong>Name:</strong> {name}</p> |
|
|
<p><strong>Class:</strong> {class_std}-{section}</p> |
|
|
<p><strong>DOB:</strong> {dob}</p> |
|
|
<p><strong>Father:</strong> {father_name}</p> |
|
|
<p><strong>Mother:</strong> {mother_name}</p> |
|
|
<p><strong>Blood Group:</strong> {blood_group}</p> |
|
|
</div> |
|
|
""" |
|
|
return card_html, photo, f"Student ID: {student_id}" |
|
|
else: |
|
|
image.delete_image(student_id) |
|
|
return f"β {db_message}", None, None |
|
|
|
|
|
except Exception as e: |
|
|
return f"β Error: {str(e)}", None, None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def login_with_face(photo): |
|
|
"""Login student using face recognition""" |
|
|
try: |
|
|
if photo is None: |
|
|
return "β Please capture your photo", None |
|
|
|
|
|
|
|
|
temp_path = image.save_temp_image_from_path(photo) |
|
|
|
|
|
if not temp_path: |
|
|
return "β Failed to process image", None |
|
|
|
|
|
|
|
|
is_match, student_id, confidence, message = image.recognize_face(temp_path) |
|
|
|
|
|
|
|
|
image.delete_temp_image() |
|
|
|
|
|
if is_match: |
|
|
|
|
|
student = db.get_student(student_id) |
|
|
|
|
|
if student: |
|
|
|
|
|
profile_html = f""" |
|
|
<div style='background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); |
|
|
padding: 1.5rem; border-radius: 10px; color: white; margin-bottom: 1rem;'> |
|
|
<h2>π Welcome, {student.student_name}!</h2> |
|
|
<p><strong>Student ID:</strong> {student.student_id} | <strong>Class:</strong> {student.class_std}-{student.section}</p> |
|
|
<p><strong>Match Confidence:</strong> {confidence:.1f}%</p> |
|
|
</div> |
|
|
|
|
|
<div style='background: white; padding: 1.5rem; border-radius: 10px; border: 2px solid #667eea;'> |
|
|
<h3>π Personal Information</h3> |
|
|
<p><strong>Name:</strong> {student.student_name}</p> |
|
|
<p><strong>Date of Birth:</strong> {student.dob}</p> |
|
|
<p><strong>Class:</strong> {student.class_std}-{student.section}</p> |
|
|
<p><strong>Father's Name:</strong> {student.father_name or 'Not specified'}</p> |
|
|
<p><strong>Mother's Name:</strong> {student.mother_name or 'Not specified'}</p> |
|
|
<p><strong>Address:</strong> {student.address or 'Not specified'}</p> |
|
|
<p><strong>Phone:</strong> {student.phone or 'Not specified'}</p> |
|
|
<p><strong>Email:</strong> {student.email or 'Not specified'}</p> |
|
|
<p><strong>Blood Group:</strong> {student.blood_group or 'Not specified'}</p> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
|
|
|
marks_records = db.get_student_marks(student_id) |
|
|
|
|
|
if marks_records: |
|
|
marks_html = "<div style='margin-top: 1rem;'><h3>π Recent Performance</h3>" |
|
|
for record in marks_records[:3]: |
|
|
marks_html += f""" |
|
|
<div style='background: #f0f2f6; padding: 1rem; border-radius: 8px; margin: 0.5rem 0;'> |
|
|
<h4>{record[0]} {record[1]}</h4> |
|
|
<p><strong>Total:</strong> {record[12]}/500 | <strong>Percentage:</strong> {record[13]:.1f}%</p> |
|
|
<p><strong>{record[2]}:</strong> {record[3]} | <strong>{record[4]}:</strong> {record[5]} | |
|
|
<strong>{record[6]}:</strong> {record[7]} | <strong>{record[8]}:</strong> {record[9]} | |
|
|
<strong>{record[10]}:</strong> {record[11]}</p> |
|
|
</div> |
|
|
""" |
|
|
marks_html += "</div>" |
|
|
profile_html += marks_html |
|
|
|
|
|
return profile_html, student_id |
|
|
else: |
|
|
return "β Student record not found", None |
|
|
else: |
|
|
return f"β {message}\n\nπ‘ Tip: Ensure good lighting and face the camera directly", None |
|
|
|
|
|
except Exception as e: |
|
|
return f"β Error: {str(e)}", None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_students_in_class(class_std, section): |
|
|
"""Get list of students in a class""" |
|
|
students = db.get_all_students(class_filter=class_std) |
|
|
class_students = [s for s in students if s[4] == section] |
|
|
|
|
|
if class_students: |
|
|
return gr.Dropdown(choices=[f"{s[1]} ({s[0]})" for s in class_students], label="Select Student") |
|
|
else: |
|
|
return gr.Dropdown(choices=[], label="No students in this class") |
|
|
|
|
|
def save_marks(class_std, section, student_display, exam_month, exam_year, |
|
|
subj1_name, subj1_marks, subj2_name, subj2_marks, |
|
|
subj3_name, subj3_marks, subj4_name, subj4_marks, |
|
|
subj5_name, subj5_marks): |
|
|
"""Save student marks""" |
|
|
try: |
|
|
if not student_display: |
|
|
return "β Please select a student" |
|
|
|
|
|
|
|
|
student_id = student_display.split("(")[-1].strip(")") |
|
|
|
|
|
|
|
|
subject_names = [subj1_name, subj2_name, subj3_name, subj4_name, subj5_name] |
|
|
marks_list = [subj1_marks, subj2_marks, subj3_marks, subj4_marks, subj5_marks] |
|
|
|
|
|
if any(not s.strip() for s in subject_names): |
|
|
return "β Please enter names for all subjects" |
|
|
|
|
|
|
|
|
success, message = db.insert_marks( |
|
|
student_id, exam_month, exam_year, |
|
|
subject_names, marks_list |
|
|
) |
|
|
|
|
|
if success: |
|
|
total = sum(marks_list) |
|
|
percentage = (total / 500) * 100 |
|
|
return f""" |
|
|
<div style='background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); |
|
|
padding: 1.5rem; border-radius: 10px; color: white; text-align: center;'> |
|
|
<h2>β
Marks Saved Successfully!</h2> |
|
|
<h3>Total: {total}/500 | Percentage: {percentage:.2f}%</h3> |
|
|
</div> |
|
|
""" |
|
|
else: |
|
|
return f"β {message}" |
|
|
|
|
|
except Exception as e: |
|
|
return f"β Error: {str(e)}" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_class_report(class_std, exam_year): |
|
|
"""Generate class performance report""" |
|
|
try: |
|
|
performance = db.get_class_performance( |
|
|
class_std, |
|
|
exam_year=exam_year if exam_year != "All" else None |
|
|
) |
|
|
|
|
|
if not performance: |
|
|
return "π No performance data available for selected filters", None |
|
|
|
|
|
|
|
|
df = pd.DataFrame(performance, columns=[ |
|
|
'Student ID', 'Name', 'Month', 'Year', 'Total', 'Percentage' |
|
|
]) |
|
|
|
|
|
|
|
|
avg_percentage = df['Percentage'].mean() |
|
|
|
|
|
|
|
|
report_html = f""" |
|
|
<div style='background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); |
|
|
padding: 1.5rem; border-radius: 10px; color: white; text-align: center; margin-bottom: 1rem;'> |
|
|
<h2>π Class {class_std} Performance Report</h2> |
|
|
<h3>Average: {avg_percentage:.1f}%</h3> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
|
|
|
fig = go.Figure(data=[ |
|
|
go.Bar( |
|
|
x=df['Name'], |
|
|
y=df['Percentage'], |
|
|
marker=dict( |
|
|
color=df['Percentage'], |
|
|
colorscale='RdYlGn', |
|
|
showscale=True |
|
|
), |
|
|
text=df['Percentage'].round(1), |
|
|
textposition='auto', |
|
|
) |
|
|
]) |
|
|
|
|
|
fig.update_layout( |
|
|
title=f"Class {class_std} Student Performance", |
|
|
xaxis_title="Student", |
|
|
yaxis_title="Percentage", |
|
|
height=500 |
|
|
) |
|
|
|
|
|
return report_html, fig |
|
|
|
|
|
except Exception as e: |
|
|
return f"β Error: {str(e)}", None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def admin_login(password): |
|
|
"""Verify admin password""" |
|
|
if password == encdec.get_admin_password(): |
|
|
stats = db.get_statistics() |
|
|
|
|
|
html = f""" |
|
|
<div style='background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); |
|
|
padding: 1.5rem; border-radius: 10px; color: white; text-align: center;'> |
|
|
<h2>β
Admin Access Granted</h2> |
|
|
</div> |
|
|
|
|
|
<div style='margin-top: 1rem; padding: 1.5rem; background: #f0f2f6; border-radius: 10px;'> |
|
|
<h3>π System Statistics</h3> |
|
|
<p><strong>Total Students:</strong> {stats['total_students']}</p> |
|
|
<p><strong>Active Classes:</strong> {len(stats['class_wise'])}</p> |
|
|
<p><strong>Students with Marks:</strong> {stats['students_with_marks']}</p> |
|
|
|
|
|
<h4>Class Distribution:</h4> |
|
|
<ul> |
|
|
""" |
|
|
|
|
|
for class_name, count in stats['class_wise']: |
|
|
html += f"<li>Class {class_name}: {count} students</li>" |
|
|
|
|
|
html += """ |
|
|
</ul> |
|
|
</div> |
|
|
|
|
|
<div style='margin-top: 1rem; padding: 1.5rem; background: #fff3cd; border-radius: 10px; border: 2px solid #ffc107;'> |
|
|
<h3>β οΈ Data Management</h3> |
|
|
<p>Use with caution! Data operations are permanent.</p> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
return html |
|
|
else: |
|
|
return """ |
|
|
<div style='background: #ff6b6b; padding: 1.5rem; border-radius: 10px; color: white; text-align: center;'> |
|
|
<h2>β Incorrect Password</h2> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
with gr.Blocks(css=custom_css, title="Suthukeny Govt High School", theme=gr.themes.Soft()) as app: |
|
|
|
|
|
gr.Markdown(""" |
|
|
# π Suthukeny Government High School |
|
|
## AI-Powered Student Management & Performance Analysis System |
|
|
""") |
|
|
|
|
|
with gr.Tabs(): |
|
|
|
|
|
with gr.Tab("π Home"): |
|
|
home_output = gr.HTML(show_home()) |
|
|
gr.Button("π Refresh Statistics").click( |
|
|
fn=show_home, |
|
|
outputs=home_output |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("π Register Student"): |
|
|
gr.Markdown("### Register New Student") |
|
|
gr.Markdown("Upload a clear, front-facing photo of the student") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
reg_photo = gr.Image(type="filepath", label="Student Photo", sources=["upload", "webcam"]) |
|
|
|
|
|
with gr.Column(scale=2): |
|
|
with gr.Group(): |
|
|
gr.Markdown("#### Required Information") |
|
|
reg_name = gr.Textbox(label="Student Name *", placeholder="Enter full name") |
|
|
|
|
|
with gr.Row(): |
|
|
reg_dob = gr.Textbox(label="Date of Birth * (YYYY-MM-DD)", placeholder="2010-01-01") |
|
|
reg_class = gr.Dropdown(choices=["6", "7", "8", "9", "10", "11", "12"], label="Class *") |
|
|
reg_section = gr.Dropdown(choices=["A", "B", "C"], label="Section *") |
|
|
|
|
|
with gr.Group(): |
|
|
gr.Markdown("#### Family Information") |
|
|
with gr.Row(): |
|
|
reg_father = gr.Textbox(label="Father's Name", placeholder="Optional") |
|
|
reg_mother = gr.Textbox(label="Mother's Name", placeholder="Optional") |
|
|
|
|
|
with gr.Group(): |
|
|
gr.Markdown("#### Contact Information") |
|
|
reg_address = gr.Textbox(label="Address", placeholder="Optional", lines=2) |
|
|
with gr.Row(): |
|
|
reg_phone = gr.Textbox(label="Phone", placeholder="Optional") |
|
|
reg_email = gr.Textbox(label="Email", placeholder="Optional") |
|
|
reg_blood = gr.Dropdown(choices=["", "A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"], |
|
|
label="Blood Group") |
|
|
|
|
|
reg_button = gr.Button("β
Register Student", variant="primary", size="lg") |
|
|
|
|
|
reg_output = gr.HTML() |
|
|
reg_photo_display = gr.Image(label="Registered Student", visible=False) |
|
|
reg_id_display = gr.Textbox(label="Student ID", visible=False) |
|
|
|
|
|
reg_button.click( |
|
|
fn=register_student, |
|
|
inputs=[reg_photo, reg_name, reg_dob, reg_class, reg_section, |
|
|
reg_father, reg_mother, reg_address, reg_phone, reg_email, reg_blood], |
|
|
outputs=[reg_output, reg_photo_display, reg_id_display] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("π Student Login"): |
|
|
gr.Markdown("### Face Recognition Login") |
|
|
gr.Markdown("πΈ Position your face in the center and click capture") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
login_photo = gr.Image(type="filepath", label="Capture Your Face", sources=["webcam"]) |
|
|
login_button = gr.Button("π Login with Face", variant="primary", size="lg") |
|
|
|
|
|
with gr.Column(scale=2): |
|
|
login_output = gr.HTML() |
|
|
login_student_id = gr.Textbox(label="Logged in Student ID", visible=False) |
|
|
|
|
|
login_button.click( |
|
|
fn=login_with_face, |
|
|
inputs=login_photo, |
|
|
outputs=[login_output, login_student_id] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("π Marks Entry"): |
|
|
gr.Markdown("### Enter Student Marks") |
|
|
|
|
|
with gr.Row(): |
|
|
marks_class = gr.Dropdown(choices=["6", "7", "8", "9", "10", "11", "12"], label="Class") |
|
|
marks_section = gr.Dropdown(choices=["A", "B", "C"], label="Section") |
|
|
marks_student = gr.Dropdown(label="Select Student", choices=[]) |
|
|
|
|
|
|
|
|
marks_class.change( |
|
|
fn=get_students_in_class, |
|
|
inputs=[marks_class, marks_section], |
|
|
outputs=marks_student |
|
|
) |
|
|
marks_section.change( |
|
|
fn=get_students_in_class, |
|
|
inputs=[marks_class, marks_section], |
|
|
outputs=marks_student |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
marks_month = gr.Dropdown( |
|
|
choices=list(calendar.month_name)[1:], |
|
|
label="Exam Month", |
|
|
value=calendar.month_name[datetime.now().month] |
|
|
) |
|
|
marks_year = gr.Dropdown( |
|
|
choices=[str(y) for y in range(datetime.now().year - 1, datetime.now().year + 2)], |
|
|
label="Exam Year", |
|
|
value=str(datetime.now().year) |
|
|
) |
|
|
|
|
|
gr.Markdown("#### Enter Marks (Out of 100)") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
subj1_name = gr.Textbox(label="Subject 1 Name", value="Tamil") |
|
|
subj1_marks = gr.Number(label="Marks", value=0, minimum=0, maximum=100) |
|
|
|
|
|
with gr.Column(): |
|
|
subj2_name = gr.Textbox(label="Subject 2 Name", value="English") |
|
|
subj2_marks = gr.Number(label="Marks", value=0, minimum=0, maximum=100) |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
subj3_name = gr.Textbox(label="Subject 3 Name", value="Mathematics") |
|
|
subj3_marks = gr.Number(label="Marks", value=0, minimum=0, maximum=100) |
|
|
|
|
|
with gr.Column(): |
|
|
subj4_name = gr.Textbox(label="Subject 4 Name", value="Science") |
|
|
subj4_marks = gr.Number(label="Marks", value=0, minimum=0, maximum=100) |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
subj5_name = gr.Textbox(label="Subject 5 Name", value="Social Science") |
|
|
subj5_marks = gr.Number(label="Marks", value=0, minimum=0, maximum=100) |
|
|
|
|
|
with gr.Column(): |
|
|
save_marks_btn = gr.Button("πΎ Save Marks", variant="primary", size="lg") |
|
|
|
|
|
marks_output = gr.HTML() |
|
|
|
|
|
save_marks_btn.click( |
|
|
fn=save_marks, |
|
|
inputs=[marks_class, marks_section, marks_student, marks_month, marks_year, |
|
|
subj1_name, subj1_marks, subj2_name, subj2_marks, |
|
|
subj3_name, subj3_marks, subj4_name, subj4_marks, |
|
|
subj5_name, subj5_marks], |
|
|
outputs=marks_output |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("π Reports"): |
|
|
gr.Markdown("### Performance Reports") |
|
|
|
|
|
with gr.Row(): |
|
|
report_class = gr.Dropdown(choices=["6", "7", "8", "9", "10", "11", "12"], |
|
|
label="Select Class", value="6") |
|
|
report_year = gr.Dropdown(choices=["All"] + [str(y) for y in range(datetime.now().year - 2, datetime.now().year + 1)], |
|
|
label="Exam Year", value=str(datetime.now().year)) |
|
|
report_btn = gr.Button("π Generate Report", variant="primary") |
|
|
|
|
|
report_output = gr.HTML() |
|
|
report_chart = gr.Plot() |
|
|
|
|
|
report_btn.click( |
|
|
fn=generate_class_report, |
|
|
inputs=[report_class, report_year], |
|
|
outputs=[report_output, report_chart] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("π§ Admin Panel"): |
|
|
gr.Markdown("### Admin Access") |
|
|
gr.Markdown("β οΈ Authorized personnel only") |
|
|
|
|
|
admin_password = gr.Textbox(label="Admin Password", type="password") |
|
|
admin_login_btn = gr.Button("π Login", variant="primary") |
|
|
admin_output = gr.HTML() |
|
|
|
|
|
admin_login_btn.click( |
|
|
fn=admin_login, |
|
|
inputs=admin_password, |
|
|
outputs=admin_output |
|
|
) |
|
|
|
|
|
gr.Markdown(""" |
|
|
--- |
|
|
<div style='text-align: center; padding: 1rem; color: #666;'> |
|
|
<p><strong>Suthukeny Government High School</strong></p> |
|
|
<p>Empowering Education Through Technology π</p> |
|
|
</div> |
|
|
""") |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
app.launch(server_name="0.0.0.0", server_port=7860) |
|
|
|