| import os |
| import pandas as pd |
| import numpy as np |
| import gradio as gr |
| from collections import defaultdict |
| from openpyxl import Workbook |
| from openpyxl.styles import PatternFill |
| import shutil |
| import zipfile |
|
|
| |
| days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] |
| time_slots = ["8-9", "9-10", "10-11", "11-12", "12-1", "1-2", "2-3", "3-4", "4-5", "5-6", "6-7"] |
| lunch_slot = "1-2" |
| no_class_3rd_4th = {"Tuesday": ["4-5"], "Friday": ["5-6"], "Monday":["6-7"],"Tuesday":["6-7"],"Thursday":["6-7"]} |
| seminar_hall_blocked_slots = ["3-4", "4-5", "5-6"] |
|
|
| block_3rd = {"Monday", "Wednesday", "Friday"} |
| block_3rd_slots = ["2-3", "3-4", "4-5"] |
| block_4th = {"Monday", "Wednesday", "Thursday"} |
| block_4th_slots = ["10-11", "11-12", "12-1"] |
|
|
| restricted_5_6_slots = { |
| 1: {"Monday", "Wednesday", "Friday"}, |
| 2: {"Monday", "Tuesday", "Wednesday"} |
| } |
|
|
| bcp_prefixes = ["phy", "msp", "chy", "msc", "bio", "msb", "i2c", "i2p", "i2b"] |
|
|
| def ordinal(n): |
| mapping = ["First", "Second", "Third", "Fourth", "Fifth"] |
| return mapping[n - 1] if 1 <= n <= 5 else f"Year{n or 'Unknown'}" |
|
|
| def extract_year_from_code(code): |
| code = code.lower() |
| numeric = ''.join(filter(str.isdigit, code)) |
| if not numeric: |
| return None |
| first_digit = int(numeric[0]) |
| if code.startswith(("msp", "msb", "msc", "msm")): |
| return 1 if first_digit == 3 else 2 if first_digit == 4 else None |
| if 1 <= first_digit <= 5: |
| return first_digit |
| return None |
|
|
| def is_bcp_major(code): |
| return any(code.startswith(prefix) for prefix in bcp_prefixes) |
|
|
| def is_bcp_blocked(code, day, slot): |
| if not is_bcp_major(code): |
| return False |
| year = extract_year_from_code(code) |
| if year == 3 and day in block_3rd and slot in block_3rd_slots: |
| return True |
| if year == 4 and day in block_4th and slot in block_4th_slots: |
| return True |
| return False |
|
|
| def parse_ltpc(ltpc): |
| ltpc = str(ltpc).strip() |
| if '-' in ltpc: |
| parts = list(map(int, ltpc.split('-'))) |
| while len(parts) < 4: |
| parts.append(0) |
| return tuple(parts[:4]) |
| digits = ''.join(filter(str.isdigit, ltpc)).zfill(4) |
| return int(digits[0]), int(digits[1]), int(digits[2]), int(digits[3]) |
|
|
| def find_valid_lecture_days(L, days_available): |
| if L == 1: |
| return [[d] for d in days_available] |
| elif L == 2: |
| return [[d1, d2] for d1 in days_available for d2 in days_available if abs(d1 - d2) >= 2 and d1 < d2] |
| elif L == 3: |
| options = [] |
| for d1 in days_available: |
| for d2 in days_available: |
| for d3 in days_available: |
| if d1 < d2 < d3: |
| if d2 - d1 == 1 and d3 - d2 >= 2: |
| options.append([d1, d2, d3]) |
| elif d3 - d2 == 1 and d2 - d1 >= 2: |
| options.append([d1, d2, d3]) |
| return options |
| return [] |
|
|
| def generate_central_timetable_excel(timetable, tutorial_groups=None, lab_groups=None, output_path="Central_Timetable.xlsx"): |
| from openpyxl import Workbook |
| from openpyxl.styles import Alignment |
|
|
| wb = Workbook() |
| ws = wb.active |
| ws.title = "Central Timetable" |
|
|
| |
| ws.append(["Day"] + time_slots) |
|
|
| for day in days: |
| row = [day] |
| for slot in time_slots: |
| entries = [] |
|
|
| |
| for year in sorted(timetable.keys()): |
| scheduled = timetable[year][day][slot] |
| entries.extend(scheduled) |
|
|
| |
| if tutorial_groups: |
| for lg, sessions in tutorial_groups.items(): |
| for d, s, r, cname in sessions: |
| if d == day and s == slot: |
| entries.append(f"{cname} (Tutorial) @ {r} ({lg})") |
|
|
| |
| if lab_groups: |
| for lg, sessions in lab_groups.items(): |
| for d, slot_block, r, cname in sessions: |
| if d == day and slot in slot_block: |
| entries.append(f"{cname} (Lab) @ {r} ({lg})") |
|
|
| row.append("\n".join(entries)) |
| ws.append(row) |
|
|
| |
| for col in ws.columns: |
| for cell in col: |
| cell.alignment = Alignment(wrapText=True) |
|
|
| |
| for i in range(2, len(time_slots) + 2): |
| ws.column_dimensions[chr(64 + i)].width = 30 |
|
|
| wb.save(output_path) |
|
|
|
|
|
|
| def generate_available_halls_report(room_slots, output_path="Available_Halls.xlsx"): |
| from openpyxl import Workbook |
|
|
| wb = Workbook() |
| ws = wb.active |
| ws.title = "Available Halls" |
|
|
| |
| ws.append(["Day"] + time_slots) |
|
|
| for day in days: |
| row_data = [day] |
| for slot in time_slots: |
| available_rooms = [] |
| for room in room_slots: |
| key = f"{day}-{slot}" |
| if room_slots[room][key]: |
| available_rooms.append(room) |
| row_data.append(", ".join(sorted(available_rooms))) |
| ws.append(row_data) |
|
|
| wb.save(output_path) |
|
|
|
|
| def schedule_bs_ms_first_year( |
| course_df, room_df, room_slots, time_slots, days, output_dir, |
| first_year_lectures_by_day_slot=None, |
| timetable=None |
| ): |
| from openpyxl import Workbook |
| from collections import defaultdict |
| import os |
|
|
| TUTORIAL_ROOMS = ["LHC 105", "LHC 106", "LHC 107", "LHC 108"] |
| LAB_SLOTS = ["2-3", "3-4", "4-5"] |
| LECTURE_SLOTS = ["8-9", "9-10", "10-11", "11-12", "12-1"] |
| LUNCH_SLOT = "1-2" |
| FORBIDDEN_5_6_DAYS = {"Monday", "Wednesday", "Friday"} |
|
|
| |
| tg_to_lg = { |
| "TG-1": "LG-1", "TG-2": "LG-1", |
| "TG-3": "LG-2", "TG-4": "LG-2", |
| "TG-5": "LG-3", "TG-6": "LG-3", |
| "TG-7": "LG-4", "TG-8": "LG-4", |
| "TG-9": "LG-5", "TG-10": "LG-5", |
| } |
|
|
| |
| lab_rooms = [] |
| for _, row in room_df.iterrows(): |
| for col in ["Computer Lab", "Class Room", "Seminar Halls"]: |
| room = row.get(col) |
| if pd.notna(room) and ("lab" in str(room).lower() or "computer" in str(room).lower()): |
| lab_rooms.append(str(room).strip()) |
|
|
| lab_groups = {f"LG-{i+1}": [] for i in range(5)} |
| tutorial_groups = {f"LG-{i+1}": [] for i in range(5)} |
| tutorial_slot_count = defaultdict(lambda: defaultdict(int)) |
| used_slots_by_lg = defaultdict(set) |
| unscheduled = [] |
|
|
| for _, row in course_df.iterrows(): |
| cname = row["Course Name"] |
| subfolder = row["Sub folder"] |
| ltpc = row["[LTPC]"] |
| students = int(row["Total Students"]) |
| L, T, P, C = parse_ltpc(ltpc) |
|
|
| codes = cname.split("-") |
| years = [extract_year_from_code(c) for c in codes if extract_year_from_code(c) is not None] |
| if not years or max(years) != 1: |
| continue |
|
|
| |
| if P > 0: |
| for lg_index in range(5): |
| lg = f"LG-{lg_index+1}" |
| assigned = False |
| for day in days: |
| for room in lab_rooms: |
| if all(room_slots[room][f"{day}-{slot}"] for slot in LAB_SLOTS): |
| for slot in LAB_SLOTS: |
| room_slots[room][f"{day}-{slot}"] = False |
| used_slots_by_lg[lg].add(f"{day}-{slot}") |
| |
| if timetable: |
| timetable[1][day][slot].append(f"{cname} (Lab) @ {room} ({lg})") |
| lab_groups[lg].append((day, LAB_SLOTS, room, cname)) |
| assigned = True |
| break |
| if assigned: |
| break |
| if not assigned: |
| unscheduled.append({ |
| "Course Name": cname, |
| "LTPC": ltpc, |
| "Sub folder": subfolder, |
| "Reason": f"No available 2β5PM block for {lg}" |
| }) |
|
|
| |
| if T == 1: |
| for lg_index in range(5): |
| lg = f"LG-{lg_index+1}" |
| tg1 = f"TG-{2*lg_index+1}" |
| tg2 = f"TG-{2*lg_index+2}" |
| assigned = 0 |
|
|
| for day in days: |
| for slot in time_slots: |
| if slot == LUNCH_SLOT or slot in LAB_SLOTS or (slot == "5-6" and day in FORBIDDEN_5_6_DAYS): |
| continue |
| if slot in used_slots_by_lg[lg]: |
| continue |
| if tutorial_slot_count[day][slot] >= 3: |
| continue |
|
|
| available_rooms = [r for r in TUTORIAL_ROOMS if room_slots[r][f"{day}-{slot}"]] |
| if not available_rooms: |
| continue |
|
|
| room = available_rooms[0] |
| room_slots[room][f"{day}-{slot}"] = False |
| tutorial_slot_count[day][slot] += 1 |
| tutorial_groups[lg].append((day, slot, room, cname)) |
| used_slots_by_lg[lg].add(f"{day}-{slot}") |
| assigned += 1 |
|
|
| |
| if timetable: |
| timetable[1][day][slot].append(f"{cname} (Tutorial) @ {room} ({lg})") |
|
|
| if assigned == 2: |
| break |
| if assigned == 2: |
| break |
|
|
| if assigned < 2: |
| unscheduled.append({ |
| "Course Name": cname, |
| "LTPC": ltpc, |
| "Sub folder": subfolder, |
| "Reason": f"Tutorials for {tg1}, {tg2} (under {lg}) not scheduled" |
| }) |
|
|
| |
| year_folder = os.path.join(output_dir, "1st Year") |
| os.makedirs(year_folder, exist_ok=True) |
|
|
| for lg in lab_groups: |
| wb = Workbook() |
| ws = wb.active |
| ws.title = lg |
| ws.append(["Day"] + time_slots) |
|
|
| for day in days: |
| row = [day] |
| for slot in time_slots: |
| val = "" |
|
|
| |
| for d, s, r, c in tutorial_groups[lg]: |
| if d == day and s == slot: |
| val += f"{c} (Tutorial) @ {r}\n" |
|
|
| |
| for d, slot_block, r, c in lab_groups[lg]: |
| if d == day and slot in slot_block: |
| val += f"{c} (Lab) @ {r}\n" |
|
|
| |
| if first_year_lectures_by_day_slot: |
| for entry in first_year_lectures_by_day_slot[day][slot]: |
| if f"{day}-{slot}" not in used_slots_by_lg[lg]: |
| val += f"{entry} (Lecture)\n" |
| used_slots_by_lg[lg].add(f"{day}-{slot}") |
|
|
| row.append(val.strip()) |
| ws.append(row) |
|
|
| |
| lg_index = int(lg.split("-")[1]) |
| tg1 = f"TG-{2 * (lg_index - 1) + 1}" |
| tg2 = f"TG-{2 * (lg_index - 1) + 2}" |
| filename = f"{lg} ({tg1} & {tg2}).xlsx" |
|
|
| wb.save(os.path.join(year_folder, filename)) |
|
|
| |
| if unscheduled: |
| pd.DataFrame(unscheduled).to_excel(os.path.join(output_dir, "Unscheduled_LTP_Report.xlsx"), index=False) |
|
|
| return tutorial_groups, lab_groups, unscheduled |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| def is_slot_allowed_for_year(year, day, slot): |
| if slot == lunch_slot: |
| return False |
| if year == 1: |
| |
| if slot not in ["8-9", "9-10", "10-11", "11-12", "12-1"]: |
| return False |
| if slot == "5-6" and day in ["Monday", "Wednesday", "Friday"]: |
| return False |
| if year in restricted_5_6_slots and slot == "5-6" and day in restricted_5_6_slots[year]: |
| return False |
| if year in [3, 4] and slot in no_class_3rd_4th.get(day, []): |
| return False |
| return True |
|
|
|
|
| def generate_timetable(course_df, room_df, output_dir, room_slots, timetable): |
| from openpyxl import Workbook |
| from openpyxl.styles import PatternFill |
| from collections import defaultdict |
|
|
| course_colors = {} |
| course_color_palette = ["FFCCCC", "CCFFCC", "CCCCFF", "FFFFCC", "CCFFFF", "FFCCFF", "F0E68C", "E6E6FA"] |
| color_index = 0 |
| first_year_lectures_by_day_slot = defaultdict(lambda: defaultdict(list)) |
| unscheduled_courses = [] |
|
|
| |
| room_data = [] |
| for i in range(len(room_df)): |
| for hall_type, room_col, cap_col in [ |
| ("Class Room", "Class Room", "Class Capacity"), |
| ("Computer Lab", "Computer Lab", "Computer Capacity"), |
| ("Seminar Halls", "Seminar Halls", "Seminar Capacity") |
| ]: |
| room = room_df[room_col].iloc[i] |
| cap = room_df[cap_col].iloc[i] |
| if pd.notna(room) and pd.notna(cap): |
| room_data.append((str(room).strip(), int(cap), hall_type)) |
|
|
| grouped = course_df.groupby("Sub folder") |
|
|
| for subfolder, group in grouped: |
| sorted_courses = group.sort_values(by="Total Students", ascending=False) |
|
|
| for _, row in sorted_courses.iterrows(): |
| cname = row["Course Name"] |
| course_group = cname.split("-") |
| ltpc = row["[LTPC]"] |
| students = int(row["Total Students"]) |
| years_in_group = [extract_year_from_code(part) for part in course_group] |
| valid_years = list(filter(None, years_in_group)) |
| if not valid_years or max(valid_years) > 5: |
| unscheduled_courses.append({"Course Name": cname, "LTPC": ltpc, "Sub folder": subfolder, "Reason": "Invalid or unsupported year in course code"}) |
| continue |
| year = max(valid_years) |
| L, T, P, C = parse_ltpc(ltpc) |
|
|
| |
| if cname not in course_colors: |
| course_colors[cname] = course_color_palette[color_index % len(course_color_palette)] |
| color_index += 1 |
|
|
| |
| assigned_room = None |
| if L > 0: |
| |
| for room, cap, hall_type in sorted(room_data, key=lambda x: abs(x[1] - students)): |
| if cap >= students and hall_type in ["Class Room", "Seminar Halls"]: |
| assigned_room = (room, hall_type) |
| break |
| else: |
| |
| for room, cap, hall_type in sorted(room_data, key=lambda x: abs(x[1] - students)): |
| if cap >= students: |
| assigned_room = (room, hall_type) |
| break |
|
|
| if not assigned_room: |
| unscheduled_courses.append({"Course Name": cname, "LTPC": ltpc, "Sub folder": subfolder, "Reason": "No suitable room"}) |
| continue |
|
|
| room_name, hall_type = assigned_room |
|
|
| |
| lecture_day_options = find_valid_lecture_days(L, list(range(len(days)))) |
| best_assigned = [] |
| max_assigned = 0 |
|
|
| for day_indices in lecture_day_options: |
| temp_assigned = [] |
| for idx in day_indices: |
| day = days[idx] |
| for slot in time_slots: |
| if not is_slot_allowed_for_year(year, day, slot): |
| continue |
| if hall_type == "Seminar Halls" and slot in seminar_hall_blocked_slots: |
| continue |
| if not room_slots[room_name][f"{day}-{slot}"]: |
| continue |
| if any(is_bcp_blocked(part, day, slot) for part in course_group): |
| continue |
|
|
| entry = f"{cname} @ {room_name}" |
| timetable[year][day][slot].append(entry) |
| room_slots[room_name][f"{day}-{slot}"] = False |
|
|
| if year == 1: |
| first_year_lectures_by_day_slot[day][slot].append(entry) |
|
|
| temp_assigned.append(idx) |
| break |
| if len(temp_assigned) > max_assigned: |
| best_assigned = temp_assigned |
| max_assigned = len(temp_assigned) |
| if max_assigned == L: |
| break |
|
|
| if max_assigned < L: |
| unscheduled_courses.append({ |
| "Course Name": cname, |
| "LTPC": ltpc, |
| "Sub folder": subfolder, |
| "Reason": f"Only {max_assigned} of {L} lectures scheduled" |
| }) |
|
|
| |
| if T == 1 and year != 1: |
| tutorial_scheduled = False |
| for i, day in enumerate(days): |
| if i in best_assigned: |
| continue |
| for slot in time_slots: |
| if not is_slot_allowed_for_year(year, day, slot): |
| continue |
| if not room_slots[room_name][f"{day}-{slot}"]: |
| continue |
| if any(is_bcp_blocked(part, day, slot) for part in course_group): |
| continue |
|
|
| timetable[year][day][slot].append(f"{cname} Tutorial @ {room_name}") |
| room_slots[room_name][f"{day}-{slot}"] = False |
| tutorial_scheduled = True |
| break |
| if tutorial_scheduled: |
| break |
| if not tutorial_scheduled: |
| unscheduled_courses.append({ |
| "Course Name": cname, |
| "LTPC": ltpc, |
| "Sub folder": subfolder, |
| "Reason": "Tutorial slot unavailable due to conflicts/constraints" |
| }) |
|
|
| |
| year_major_courses = defaultdict(lambda: defaultdict(list)) |
| for _, row in course_df.iterrows(): |
| cname = row["Course Name"] |
| years = [extract_year_from_code(code) for code in cname.split("-")] |
| valid_years = list(filter(None, years)) |
| if not valid_years or max(valid_years) > 5: |
| continue |
| year = max(valid_years) |
| for part in cname.split("-"): |
| code = part.lower() |
| for prefix, major in { |
| "mat": "Math", "msm": "Math", "phy": "Physics", "msp": "Physics", "chy": "Chemistry", "msc": "Chemistry", |
| "bio": "Biology", "msb": "Biology", "ees": "Earth Science", "dsc": "Data Science", "i2m": "Math", |
| "i2p": "Physics", "i2c": "Chemistry", "i2b": "Biology" |
| }.items(): |
| if code.startswith(prefix): |
| year_major_courses[year][major].append(cname) |
|
|
| for year in timetable: |
| if year == 1: |
| continue |
|
|
| year_folder = os.path.join(output_dir, f"{ordinal(year)} Year") |
| os.makedirs(year_folder, exist_ok=True) |
|
|
| for major in year_major_courses[year]: |
| fname = f"{ordinal(year)} Year, {major} Major.xlsx" |
| fpath = os.path.join(year_folder, fname) |
| wb = Workbook() |
| ws = wb.active |
| ws.append(["Day"] + time_slots) |
|
|
| for row_idx, day in enumerate(days, start=2): |
| row_data = [day] |
| for col_idx, slot in enumerate(time_slots, start=2): |
| entries = timetable[year][day][slot] |
| val = ", ".join(e for e in entries if any(c in e for c in year_major_courses[year][major])) |
| row_data.append(val) |
| ws.append(row_data) |
|
|
| for col_idx, slot in enumerate(time_slots, start=2): |
| cell = ws.cell(row=row_idx, column=col_idx) |
| entries = timetable[year][day][slot] |
| for cname in year_major_courses[year][major]: |
| if any(cname in e for e in entries): |
| cell.fill = PatternFill(start_color=course_colors[cname], end_color=course_colors[cname], fill_type="solid") |
| break |
| wb.save(fpath) |
|
|
| |
| if unscheduled_courses: |
| pd.DataFrame(unscheduled_courses).to_excel("Unscheduled_LTP_Report.xlsx", index=False) |
|
|
| return first_year_lectures_by_day_slot |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
| |
|
|
| |
| def process(course_file, room_file): |
| import shutil |
| import zipfile |
| from collections import defaultdict |
| import pandas as pd |
| import os |
|
|
| |
| course_df = pd.read_excel(course_file.name) |
| room_df = pd.read_excel(room_file.name) |
|
|
| |
| room_df.columns = [ |
| "Class Room", "Class Capacity", |
| "Computer Lab", "Computer Capacity", |
| "Seminar Halls", "Seminar Capacity" |
| ] |
|
|
| |
| output_dir = "generated_routines" |
| output_1st_year_dir = "generated_1st_year_LT" |
|
|
| for path in [output_dir, output_1st_year_dir]: |
| if os.path.exists(path): |
| shutil.rmtree(path) |
| os.makedirs(path) |
|
|
| |
| room_slots = defaultdict(lambda: defaultdict(lambda: True)) |
|
|
| |
| for day in days: |
| for room in room_slots: |
| for slot in ["2-3", "3-4", "4-5"]: |
| room_slots[room][f"{day}-{slot}"] = False |
|
|
| |
| room_data = [] |
| for i in range(len(room_df)): |
| |
| if pd.notna(room_df["Class Room"].iloc[i]) and pd.notna(room_df["Class Capacity"].iloc[i]): |
| room_data.append(( |
| str(room_df["Class Room"].iloc[i]).strip(), |
| int(room_df["Class Capacity"].iloc[i]), |
| "Class Room" |
| )) |
|
|
| |
| if pd.notna(room_df["Computer Lab"].iloc[i]) and pd.notna(room_df["Computer Capacity"].iloc[i]): |
| room_data.append(( |
| str(room_df["Computer Lab"].iloc[i]).strip(), |
| int(room_df["Computer Capacity"].iloc[i]), |
| "Computer Lab" |
| )) |
|
|
| |
| if pd.notna(room_df["Seminar Halls"].iloc[i]) and pd.notna(room_df["Seminar Capacity"].iloc[i]): |
| room_data.append(( |
| str(room_df["Seminar Halls"].iloc[i]).strip(), |
| int(room_df["Seminar Capacity"].iloc[i]), |
| "Seminar Halls" |
| )) |
|
|
| |
| timetable = defaultdict(lambda: defaultdict(lambda: defaultdict(list))) |
|
|
| |
| first_year_lectures = generate_timetable(course_df, room_df, output_dir, room_slots, timetable) |
|
|
| |
| tg, lg, unscheduled_1st = schedule_bs_ms_first_year( |
| course_df, |
| room_df, |
| room_slots, |
| time_slots, |
| days, |
| output_1st_year_dir, |
| first_year_lectures_by_day_slot=first_year_lectures, |
| timetable=timetable |
| ) |
|
|
| |
| generate_available_halls_report(room_slots) |
| generate_central_timetable_excel(timetable) |
|
|
| |
| zip_path_main = "Final_Timetable.zip" |
| with zipfile.ZipFile(zip_path_main, 'w') as zipf: |
| for foldername, _, filenames in os.walk(output_dir): |
| for filename in filenames: |
| filepath = os.path.join(foldername, filename) |
| arcname = os.path.relpath(filepath, output_dir) |
| zipf.write(filepath, arcname) |
|
|
| |
| zip_path_1st = "1st_Year_Labs_Tutorials.zip" |
| with zipfile.ZipFile(zip_path_1st, 'w') as zipf: |
| for foldername, _, filenames in os.walk(output_1st_year_dir): |
| for filename in filenames: |
| filepath = os.path.join(foldername, filename) |
| arcname = os.path.relpath(filepath, output_1st_year_dir) |
| zipf.write(filepath, arcname) |
|
|
| |
| return ( |
| zip_path_main, |
| zip_path_1st, |
| ("Unscheduled_LTP_Report.xlsx" if os.path.exists("Unscheduled_LTP_Report.xlsx") else None), |
| ("Available_Halls.xlsx" if os.path.exists("Available_Halls.xlsx") else None), |
| ("Central_Timetable.xlsx" if os.path.exists("Central_Timetable.xlsx") else None) |
| ) |
|
|
|
|
|
|
|
|
|
|
|
|
| |
| import gradio as gr |
|
|
| def launch_ui(): |
| with gr.Blocks(css=""" |
| .centered-title { |
| text-align: center; |
| font-size: 2.2em; |
| font-weight: bold; |
| color: #2b3d4f; |
| background: #e0f7fa; |
| padding: 10px; |
| border-radius: 12px; |
| margin-bottom: 20px; |
| } |
| .highlight-button { |
| background-color: #1976D2 !important; |
| color: white !important; |
| border-radius: 8px; |
| padding: 10px 20px; |
| } |
| """) as iface: |
|
|
| |
| gr.Image( |
| value="https://drive.google.com/uc?id=1UDlI15QVKy0JSkfFCG7yMAR7esv35O-v", |
| height=400, |
| width=8000, |
| show_label=False, |
| container=False |
| ) |
|
|
| |
| gr.HTML('<div class="centered-title">IISER TVM Course Timetable Scheduler</div>') |
|
|
| |
| with gr.Row(): |
| course_input = gr.File(label="π Upload Course Excel File (.xlsx)") |
| room_input = gr.File(label="π¬ Upload Room Details Excel File (.xlsx)") |
|
|
| |
| run_button = gr.Button("Generate Timetable", elem_classes="highlight-button") |
|
|
| |
| with gr.Row(): |
| timetable_output = gr.File(label="π Download Timetable ZIP") |
| first_year_output = gr.File(label="π§ͺ 1st Year Labs & Tutorials ZIP") |
|
|
| with gr.Row(): |
| unscheduled_output = gr.File(label="β οΈ Unscheduled L/T/P Report (if any)") |
| available_halls_output = gr.File(label="π© Available Halls Report") |
| central_output = gr.File(label="π Central Timetable (All Courses)") |
|
|
| |
| run_button.click( |
| fn=process, |
| inputs=[course_input, room_input], |
| outputs=[ |
| timetable_output, |
| first_year_output, |
| unscheduled_output, |
| available_halls_output, |
| central_output |
| ] |
| ) |
|
|
| iface.launch(share=True, debug=True) |
|
|
|
|
| if __name__ == "__main__": |
| launch_ui() |