saptak21 commited on
Commit
ecf0b39
·
verified ·
1 Parent(s): 2ce0e71

Create app.py

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