Spaces:
Build error
Build error
Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from transformers import pipeline
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import math
|
| 6 |
+
import gradio as gr
|
| 7 |
+
|
| 8 |
+
# Step 1: Load Excel Files (Not needed for Gradio, files are loaded in run_allocation)
|
| 9 |
+
|
| 10 |
+
# Step 2: Calculate Total Hours and Average Duty (n)
|
| 11 |
+
def calculate_average_duty(exams_df, teachers_df):
|
| 12 |
+
grouped = exams_df.groupby(['Day', 'Slot', 'Room Number'])['Student Number'].sum().reset_index(name='TotalStudents')
|
| 13 |
+
|
| 14 |
+
def compute_invigilators(x):
|
| 15 |
+
n = math.ceil(x / 20)
|
| 16 |
+
while (20 * n + 10) < x:
|
| 17 |
+
n += 1
|
| 18 |
+
return n
|
| 19 |
+
|
| 20 |
+
grouped['RequiredInvigilators'] = grouped['TotalStudents'].apply(compute_invigilators)
|
| 21 |
+
|
| 22 |
+
exams_df = pd.merge(
|
| 23 |
+
exams_df,
|
| 24 |
+
grouped[['Day', 'Slot', 'Room Number', 'RequiredInvigilators']],
|
| 25 |
+
on=['Day', 'Slot', 'Room Number'],
|
| 26 |
+
how='left'
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
exams_df['Duration'] = exams_df['Duration'].astype(int)
|
| 30 |
+
total_invigilator_hours = sum(exams_df['RequiredInvigilators'] * exams_df['Duration'])
|
| 31 |
+
n = total_invigilator_hours / len(teachers_df)
|
| 32 |
+
return n, total_invigilator_hours, exams_df
|
| 33 |
+
|
| 34 |
+
# Step 3: Assign Initial Duty Limits Based on Designation
|
| 35 |
+
def assign_initial_duties(teachers_df, avg_duty):
|
| 36 |
+
teachers_df['DESIG'] = teachers_df['DESIG'].str.strip().str.upper()
|
| 37 |
+
|
| 38 |
+
def get_duty_limit(desig, avg):
|
| 39 |
+
if desig == "SACT":
|
| 40 |
+
return avg - 2
|
| 41 |
+
elif desig in ["LAB", "HOD", "BURSAR", "AQAC"]:
|
| 42 |
+
return avg - 1
|
| 43 |
+
else:
|
| 44 |
+
return avg # Default for A.P. and others
|
| 45 |
+
|
| 46 |
+
teachers_df['DutyLimit'] = teachers_df['DESIG'].apply(lambda d: get_duty_limit(d, math.ceil(avg_duty)))
|
| 47 |
+
teachers_df['AssignedHours'] = 0
|
| 48 |
+
teachers_df['AssignedDays'] = [[] for _ in range(len(teachers_df))]
|
| 49 |
+
return teachers_df
|
| 50 |
+
|
| 51 |
+
# Step 4: Assign Duties
|
| 52 |
+
def assign_duties(teachers_df, exams_df, buffer=0.5):
|
| 53 |
+
assignments = []
|
| 54 |
+
unassigned = []
|
| 55 |
+
|
| 56 |
+
unique_sessions = exams_df[['Day', 'Slot', 'Room Number', 'Duration', 'RequiredInvigilators']].drop_duplicates()
|
| 57 |
+
|
| 58 |
+
for _, exam in unique_sessions.iterrows():
|
| 59 |
+
date = exam['Day']
|
| 60 |
+
time = exam['Slot']
|
| 61 |
+
duration = int(exam['Duration'])
|
| 62 |
+
room = exam['Room Number']
|
| 63 |
+
required_invigilators = int(exam['RequiredInvigilators'])
|
| 64 |
+
|
| 65 |
+
for _ in range(required_invigilators):
|
| 66 |
+
available_teachers = teachers_df[
|
| 67 |
+
(teachers_df['AssignedHours'] + duration <= teachers_df['DutyLimit'] + buffer) &
|
| 68 |
+
(~teachers_df['AssignedDays'].apply(lambda x: (date, time) in x))
|
| 69 |
+
]
|
| 70 |
+
|
| 71 |
+
if available_teachers.empty:
|
| 72 |
+
unassigned.append({
|
| 73 |
+
'Day': date, 'Slot': time, 'Room': room,
|
| 74 |
+
'Duration': duration, 'Reason': 'No available teacher'
|
| 75 |
+
})
|
| 76 |
+
continue
|
| 77 |
+
|
| 78 |
+
available_teachers = available_teachers.sort_values(by='AssignedHours')
|
| 79 |
+
selected_teacher = available_teachers.head(1)
|
| 80 |
+
idx = selected_teacher.index[0]
|
| 81 |
+
|
| 82 |
+
teachers_df.at[idx, 'AssignedHours'] += duration
|
| 83 |
+
teachers_df.at[idx, 'AssignedDays'].append((date, time))
|
| 84 |
+
|
| 85 |
+
assignments.append({
|
| 86 |
+
'Day': date,
|
| 87 |
+
'Slot': time,
|
| 88 |
+
'Room': room,
|
| 89 |
+
'Duration': duration,
|
| 90 |
+
'Teacher': teachers_df.at[idx, 'NAME'],
|
| 91 |
+
'Designation': teachers_df.at[idx, 'DESIG'],
|
| 92 |
+
'AssignedHoursAfter': teachers_df.at[idx, 'AssignedHours']
|
| 93 |
+
})
|
| 94 |
+
|
| 95 |
+
return pd.DataFrame(assignments), pd.DataFrame(unassigned)
|
| 96 |
+
|
| 97 |
+
# Function to run the allocation process
|
| 98 |
+
def run_allocation(teachers_file, exams_file, buffer=0.5):
|
| 99 |
+
try:
|
| 100 |
+
teachers_df = pd.read_excel(teachers_file.name)
|
| 101 |
+
exams_df = pd.read_excel(exams_file.name)
|
| 102 |
+
|
| 103 |
+
n, total_hours, exams_df = calculate_average_duty(exams_df, teachers_df)
|
| 104 |
+
|
| 105 |
+
teachers_df = assign_initial_duties(teachers_df, n)
|
| 106 |
+
assignments_df, unassigned_df = assign_duties(teachers_df, exams_df, buffer=buffer)
|
| 107 |
+
|
| 108 |
+
# Save assignments to Excel for download
|
| 109 |
+
assignments_df.to_excel("duty_allocation_summary.xlsx", index=False)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
# Return DataFrames, summary, and file path for display and download
|
| 113 |
+
return assignments_df, unassigned_df, teachers_df[['NAME', 'DESIG', 'AssignedHours', 'DutyLimit']], total_hours, n, "duty_allocation_summary.xlsx"
|
| 114 |
+
|
| 115 |
+
except Exception as e:
|
| 116 |
+
return None, None, None, None, str(e), None # Return None for file path on error
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
# Gradio Interface
|
| 120 |
+
iface = gr.Interface(
|
| 121 |
+
fn=run_allocation,
|
| 122 |
+
inputs=[
|
| 123 |
+
gr.File(label="Teachers Excel File"),
|
| 124 |
+
gr.File(label="Exam Summary Excel File"),
|
| 125 |
+
gr.Slider(minimum=0, maximum=2, step=0.1, label="Duty Buffer", value=0.5),
|
| 126 |
+
],
|
| 127 |
+
outputs=[
|
| 128 |
+
gr.Dataframe(label="Duty Assignments"),
|
| 129 |
+
gr.Dataframe(label="Unassigned Duties"),
|
| 130 |
+
gr.Dataframe(label="Teacher Summary"),
|
| 131 |
+
gr.Textbox(label="Total Invigilator Hours"),
|
| 132 |
+
gr.Textbox(label="Average Duty per Teacher"),
|
| 133 |
+
gr.File(label="Download Duty Allocation Summary") # Add File component for download
|
| 134 |
+
],
|
| 135 |
+
title="🧑🏫 Duty Allocation System",
|
| 136 |
+
description="Upload your Excel files and click 'Submit' to generate duty assignments."
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
iface.launch(inline = False)
|
| 140 |
+
|