diff --git a/controller/template_controller.py b/controller/template_controller.py index ca7def1..cd52175 100644 --- a/controller/template_controller.py +++ b/controller/template_controller.py @@ -5,12 +5,18 @@ five templates/*-wise template modules render from. Scheduling rules (dummy/demo scheduler, not a full CSP solver): 1. General courses (shared across every batch of a session) are placed first, only on Tuesday, so those slots are locked in before anything - else and never clash across the batches sharing them. - 2. Lab courses (180-minute / double-slot) are placed next, in lab rooms - only, on whichever day/slot-pair is free for the batch, instructor, - and room. - 3. Remaining theory courses fill the rest of the week (Mon/Tue/Wed/Thu/Fri), - one 90-minute slot at a time, avoiding room / instructor / batch clashes. + else and never clash across the batches sharing them. Each general + offering gets `lectures_per_week` sessions. + 2. Lab courses are placed next: each offering gets `labs_per_week` + 180-minute / double-slot sessions, in lab rooms only, on whichever + day/slot-pair is free for the batch, instructor, and room. + 3. Remaining theory sessions (`lectures_per_week` per non-general + offering) fill the rest of the week (Mon/Tue/Wed/Thu/Fri), one + 90-minute slot at a time, avoiding room / instructor / batch clashes. + +Room selection always tries a course's preferred lecture_room_id / +lab_room_id first, falling back to another same-type room only if the +preferred one is busy for that day/slot. """ import csv @@ -59,22 +65,32 @@ def load_data(): batches_26 = _read_csv(os.path.join("batch_codes", "26", "batches.csv")) batches = batches_25 + batches_26 - courses_by_id = {c["course_id"]: c for c in courses} - instructors_by_code = {i["instructor_code"]: i for i in instructors} + rooms_by_id = {r["room_id"]: r for r in rooms} + instructor_names_by_code = {i["instructor_code"]: i["instructor_name"] for i in instructors} batches_by_code = {b["batch_code"]: b for b in batches} - # normalize offerings: expand "batch1|batch2" general offerings into a - # list of batch codes on each offering record + # normalize courses: numeric fields + a lookup by id + courses_by_id = {} + for c in courses: + c["credit_hours"] = int(c["credit_hours"]) + c["lectures_per_week"] = int(c["lectures_per_week"]) + c["labs_per_week"] = int(c["labs_per_week"]) + c["is_general"] = c["is_general"].lower() == "yes" + courses_by_id[c["course_id"]] = c + + # normalize offerings: expand "batch1|batch2" section batch groups into + # a list of batch codes, and attach the course record for convenience for off in offerings: - off["batch_codes"] = off["batch_code"].split("|") - off["is_general"] = courses_by_id[off["course_id"]]["is_general"].lower() == "yes" - off["course_type"] = courses_by_id[off["course_id"]]["course_type"] + off["batch_codes"] = off["batch_codes"].split("|") + off["course"] = courses_by_id[off["course_id"]] + off["instructor_name"] = instructor_names_by_code.get(off["instructor_code"], off["instructor_code"]) return { "rooms": rooms, + "rooms_by_id": rooms_by_id, "blocks": blocks, "instructors": instructors, - "instructors_by_code": instructors_by_code, + "instructor_names_by_code": instructor_names_by_code, "courses": courses, "courses_by_id": courses_by_id, "offerings": offerings, @@ -115,10 +131,17 @@ class Scheduler: self.instructor_busy.setdefault(key, set()).add(instructor_code) self.batch_busy.setdefault(key, set()).update(batch_codes) + def _room_candidates(self, preferred_room_id, room_pool): + """Preferred room first, then the rest of the same-type pool.""" + preferred = self.data["rooms_by_id"].get(preferred_room_id) + rest = [r for r in room_pool if r["room_id"] != preferred_room_id] + return ([preferred] if preferred else []) + rest + def _add_session(self, offering, day, slot_indices, room): - course = self.data["courses_by_id"][offering["course_id"]] + course = offering["course"] start = TIME_SLOTS[slot_indices[0]]["start"] end = TIME_SLOTS[slot_indices[-1]]["end"] + session_type = "lab" if len(slot_indices) > 1 else "theory" self.sessions.append({ "day": day, "slot_indices": slot_indices, @@ -126,71 +149,88 @@ class Scheduler: "end": end, "course_id": offering["course_id"], "course_name": course["course_name"], - "course_type": course["course_type"], - "is_general": offering["is_general"], + "course_type": session_type, + "is_general": course["is_general"], + "section_id": offering["section_id"], "instructor_code": offering["instructor_code"], - "instructor_name": self.data["instructors_by_code"][offering["instructor_code"]]["instructor_name"], + "instructor_name": offering["instructor_name"], "room_id": room["room_id"], "room_name": room["room_name"], "batch_codes": offering["batch_codes"], }) def schedule_general_courses(self): - day = "Tuesday" - general = [o for o in self.data["offerings"] if o["is_general"]] + general = [o for o in self.data["offerings"] if o["course"]["is_general"]] for offering in general: - placed = False - for slot_index in TEACHING_SLOT_INDICES: - for room in self.classrooms: - if self._is_free(day, slot_index, room["room_id"], offering["instructor_code"], offering["batch_codes"]): - self._mark_busy(day, slot_index, room["room_id"], offering["instructor_code"], offering["batch_codes"]) - self._add_session(offering, day, [slot_index], room) - placed = True + course = offering["course"] + room_candidates = self._room_candidates(course["lecture_room_id"], self.classrooms) + + for _ in range(course["lectures_per_week"]): + placed = False + # Tuesday first, then spill to the rest of the week if a + # session can't fit there. + for day in ["Tuesday"] + [d for d in DAYS if d != "Tuesday"]: + for slot_index in TEACHING_SLOT_INDICES: + for room in room_candidates: + if self._is_free(day, slot_index, room["room_id"], offering["instructor_code"], offering["batch_codes"]): + self._mark_busy(day, slot_index, room["room_id"], offering["instructor_code"], offering["batch_codes"]) + self._add_session(offering, day, [slot_index], room) + placed = True + break + if placed: + break + if placed: break - if placed: - break - if not placed: - print(f"WARNING: could not place general course {offering['course_id']}") + if not placed: + print(f"WARNING: could not place a lecture for general course {offering['course_id']} section {offering['section_id']}") def schedule_lab_courses(self): - labs_offerings = [o for o in self.data["offerings"] if o["course_type"] == "lab" and not o["is_general"]] - for offering in labs_offerings: - placed = False - for day in DAYS: - for slot_a, slot_b in LAB_SLOT_PAIRS: - for room in self.labs: - if self._is_free(day, slot_a, room["room_id"], offering["instructor_code"], offering["batch_codes"]) and \ - self._is_free(day, slot_b, room["room_id"], offering["instructor_code"], offering["batch_codes"]): - self._mark_busy(day, slot_a, room["room_id"], offering["instructor_code"], offering["batch_codes"]) - self._mark_busy(day, slot_b, room["room_id"], offering["instructor_code"], offering["batch_codes"]) - self._add_session(offering, day, [slot_a, slot_b], room) - placed = True + offerings = [o for o in self.data["offerings"] if not o["course"]["is_general"] and o["course"]["labs_per_week"] > 0] + for offering in offerings: + course = offering["course"] + room_candidates = self._room_candidates(course["lab_room_id"], self.labs) + + for _ in range(course["labs_per_week"]): + placed = False + for day in DAYS: + for slot_a, slot_b in LAB_SLOT_PAIRS: + for room in room_candidates: + if self._is_free(day, slot_a, room["room_id"], offering["instructor_code"], offering["batch_codes"]) and \ + self._is_free(day, slot_b, room["room_id"], offering["instructor_code"], offering["batch_codes"]): + self._mark_busy(day, slot_a, room["room_id"], offering["instructor_code"], offering["batch_codes"]) + self._mark_busy(day, slot_b, room["room_id"], offering["instructor_code"], offering["batch_codes"]) + self._add_session(offering, day, [slot_a, slot_b], room) + placed = True + break + if placed: break if placed: break - if placed: - break - if not placed: - print(f"WARNING: could not place lab course {offering['course_id']} for {offering['batch_codes']}") + if not placed: + print(f"WARNING: could not place a lab for course {offering['course_id']} section {offering['section_id']} ({offering['batch_codes']})") def schedule_theory_courses(self): - theory_offerings = [o for o in self.data["offerings"] if o["course_type"] == "theory" and not o["is_general"]] - for offering in theory_offerings: - placed = False - for day in DAYS: - for slot_index in TEACHING_SLOT_INDICES: - for room in self.classrooms: - if self._is_free(day, slot_index, room["room_id"], offering["instructor_code"], offering["batch_codes"]): - self._mark_busy(day, slot_index, room["room_id"], offering["instructor_code"], offering["batch_codes"]) - self._add_session(offering, day, [slot_index], room) - placed = True + offerings = [o for o in self.data["offerings"] if not o["course"]["is_general"]] + for offering in offerings: + course = offering["course"] + room_candidates = self._room_candidates(course["lecture_room_id"], self.classrooms) + + for _ in range(course["lectures_per_week"]): + placed = False + for day in DAYS: + for slot_index in TEACHING_SLOT_INDICES: + for room in room_candidates: + if self._is_free(day, slot_index, room["room_id"], offering["instructor_code"], offering["batch_codes"]): + self._mark_busy(day, slot_index, room["room_id"], offering["instructor_code"], offering["batch_codes"]) + self._add_session(offering, day, [slot_index], room) + placed = True + break + if placed: break if placed: break - if placed: - break - if not placed: - print(f"WARNING: could not place theory course {offering['course_id']} for {offering['batch_codes']}") + if not placed: + print(f"WARNING: could not place a lecture for course {offering['course_id']} section {offering['section_id']} ({offering['batch_codes']})") def run(self): self.schedule_general_courses() diff --git a/data/dummy/courses/course_offering.csv b/data/dummy/courses/course_offering.csv index bb12df3..39cfde3 100644 --- a/data/dummy/courses/course_offering.csv +++ b/data/dummy/courses/course_offering.csv @@ -1,25 +1,15 @@ -course_id,batch_code,instructor_code -CS101,25021519,1001 -CS102,25021519,1001 -CS201,25021519,1002 -CS202,25021519,1002 -CS101,25021520,1001 -CS102,25021520,1001 -CS201,25021520,1002 -CS202,25021520,1002 -EE101,25031519,1004 -EE102,25031519,1004 -CS101,26021519,1001 -CS102,26021519,1001 -CS201,26021519,1002 -CS202,26021519,1002 -CS101,26021520,1001 -CS102,26021520,1001 -CS201,26021520,1002 -CS202,26021520,1002 -EE101,26031519,1004 -EE102,26031519,1004 -GEN101,25021519|25021520|25031519,1002 -GEN102,25021519|25021520|25031519,1006 -GEN101,26021519|26021520|26031519,1002 -GEN102,26021519|26021520|26031519,1006 +course_id,section_id,batch_codes,instructor_code +CS101,A,25021519,1001 +CS201,A,25021519,1002 +CS101,B,25021520,1001 +CS201,B,25021520,1002 +EE101,A,25031519,1004 +CS101,C,26021519,1001 +CS201,C,26021519,1002 +CS101,D,26021520,1001 +CS201,D,26021520,1002 +EE101,B,26031519,1004 +GEN101,A,25021519|25021520|25031519,1002 +GEN102,A,25021519|25021520|25031519,1006 +GEN101,B,26021519|26021520|26031519,1002 +GEN102,B,26021519|26021520|26031519,1006 diff --git a/data/dummy/courses/courses.csv b/data/dummy/courses/courses.csv index a88d24b..e4a2e1e 100644 --- a/data/dummy/courses/courses.csv +++ b/data/dummy/courses/courses.csv @@ -1,9 +1,6 @@ -course_id,course_name,course_type,credit_hours,is_general -CS101,Programming Fundamentals,theory,3,no -CS102,Programming Fundamentals Lab,lab,1,no -CS201,Data Structures,theory,3,no -CS202,Data Structures Lab,lab,1,no -EE101,Basic Electronics,theory,3,no -EE102,Basic Electronics Lab,lab,1,no -GEN101,Functional English,theory,3,yes -GEN102,Islamic Studies,theory,2,yes +course_id,course_name,credit_hours,lectures_per_week,labs_per_week,is_general,lecture_room_id,lab_room_id +CS101,Programming Fundamentals,4,2,1,no,A-101,A-L01 +CS201,Data Structures,4,2,1,no,A-102,A-L01 +EE101,Basic Electronics,4,2,1,no,A-201,A-L02 +GEN101,Functional English,3,2,0,yes,A-103, +GEN102,Islamic Studies,2,1,0,yes,A-103, diff --git a/data/dummy/generate_dummy_data.py b/data/dummy/generate_dummy_data.py index 3ca3cd7..7a0de23 100644 --- a/data/dummy/generate_dummy_data.py +++ b/data/dummy/generate_dummy_data.py @@ -69,77 +69,91 @@ def main(): all_batches = batches_25 + batches_26 - # ---- instructors (4-digit employee codes) ---- - instructors = [ - ["1001", "Dr. Ahsan Raza", "CS"], - ["1002", "Ms. Sana Tariq", "CS"], - ["1003", "Mr. Bilal Khan", "CS"], - ["1004", "Dr. Farah Iqbal", "EE"], - ["1005", "Mr. Usman Ali", "EE"], - ["1006", "Ms. Hira Shah", "CS"], - ] - write_csv( - "instructor/instructors.csv", - ["instructor_code", "instructor_name", "department"], - instructors, - ) - # ---- courses ---- - # is_general courses are shared across all batches of the same session - # (so they can be scheduled together on Tuesday with no clash). + # One row per subject: lectures/labs per week are counted separately so + # a single course can carry both, and each course names its preferred + # lecture/lab room (the scheduler falls back to another same-type room + # only if the preferred one is busy). + # is_general courses are shared across every batch of the same session + # (so they can be scheduled together on Tuesday first, with no clash). courses = [ - ["CS101", "Programming Fundamentals", "theory", 3, "no"], - ["CS102", "Programming Fundamentals Lab", "lab", 1, "no"], - ["CS201", "Data Structures", "theory", 3, "no"], - ["CS202", "Data Structures Lab", "lab", 1, "no"], - ["EE101", "Basic Electronics", "theory", 3, "no"], - ["EE102", "Basic Electronics Lab", "lab", 1, "no"], - ["GEN101", "Functional English", "theory", 3, "yes"], - ["GEN102", "Islamic Studies", "theory", 2, "yes"], + # course_id, course_name, credit_hours, lectures_per_week, labs_per_week, is_general, lecture_room_id, lab_room_id + ["CS101", "Programming Fundamentals", 4, 2, 1, "no", "A-101", "A-L01"], + ["CS201", "Data Structures", 4, 2, 1, "no", "A-102", "A-L01"], + ["EE101", "Basic Electronics", 4, 2, 1, "no", "A-201", "A-L02"], + ["GEN101", "Functional English", 3, 2, 0, "yes", "A-103", ""], + ["GEN102", "Islamic Studies", 2, 1, 0, "yes", "A-103", ""], ] write_csv( "courses/courses.csv", - ["course_id", "course_name", "course_type", "credit_hours", "is_general"], + [ + "course_id", "course_name", "credit_hours", "lectures_per_week", + "labs_per_week", "is_general", "lecture_room_id", "lab_room_id", + ], courses, ) - # ---- course offerings: which course is taught to which batch, by whom ---- - # General courses (GEN101/GEN102) are offered once per session (25/26) - # and shared across every batch in that session. + # ---- course offerings: course + section (a section groups the batches + # taking that section together) + who teaches it ---- + # A course can have many sections; here each CS/EE batch of a session is + # its own section (Section A/B/...), while general courses have a single + # section "ALL" grouping every batch of that session. offerings = [] - dept_theory_lab = { - "CS": [("CS101", "CS102"), ("CS201", "CS202")], - "EE": [("EE101", "EE102")], - } + dept_course = {"CS": ["CS101", "CS201"], "EE": ["EE101"]} dept_instructors = { "CS": ["1001", "1002", "1003", "1006"], "EE": ["1004", "1005"], } - for batch_code, department, program, session in all_batches: - pairs = dept_theory_lab[department] - instr_pool = dept_instructors[department] - for i, (theory_id, lab_id) in enumerate(pairs): - instr = instr_pool[i % len(instr_pool)] - offerings.append([theory_id, batch_code, instr]) - offerings.append([lab_id, batch_code, instr]) + section_letters = ["A", "B", "C", "D"] + dept_section_counter = {"CS": 0, "EE": 0} - # general courses: one offering per (course, session), batch_group lists - # every batch code sharing that session, separated by "|" + for batch_code, department, program, session in all_batches: + section_id = section_letters[dept_section_counter[department] % len(section_letters)] + dept_section_counter[department] += 1 + + for i, course_id in enumerate(dept_course[department]): + instr_pool = dept_instructors[department] + instr = instr_pool[i % len(instr_pool)] + offerings.append([course_id, section_id, batch_code, instr]) + + # general courses: one offering per (course, session), section "ALL" + # grouping every batch code sharing that session, separated by "|" for session, batch_group in ( ("25", [b[0] for b in batches_25]), ("26", [b[0] for b in batches_26]), ): - offerings.append(["GEN101", "|".join(batch_group), "1002"]) - offerings.append(["GEN102", "|".join(batch_group), "1006"]) + offerings.append(["GEN101", "ALL", "|".join(batch_group), "1002"]) + offerings.append(["GEN102", "ALL", "|".join(batch_group), "1006"]) write_csv( "courses/course_offering.csv", - ["course_id", "batch_code", "instructor_code"], + ["course_id", "section_id", "batch_codes", "instructor_code"], offerings, ) + # ---- instructors (4-digit employee codes) ---- + # Derived from the offerings above so it can never drift: one row per + # unique (instructor, course, section) teaching assignment. + instructor_names = { + "1001": "Dr. Ahsan Raza", + "1002": "Ms. Sana Tariq", + "1003": "Mr. Bilal Khan", + "1004": "Dr. Farah Iqbal", + "1005": "Mr. Usman Ali", + "1006": "Ms. Hira Shah", + } + instructors = [] + for course_id, section_id, batch_codes, instr_code in offerings: + instructors.append([instr_code, instructor_names[instr_code], course_id, section_id]) + + write_csv( + "instructor/instructors.csv", + ["instructor_code", "instructor_name", "course_id", "section_id"], + instructors, + ) + # ---- students (roll_no = batch_code-seq) ---- students = [] for batch_code, department, program, session in all_batches: diff --git a/data/dummy/instructor/instructors.csv b/data/dummy/instructor/instructors.csv index 0ff9b94..bf73e17 100644 --- a/data/dummy/instructor/instructors.csv +++ b/data/dummy/instructor/instructors.csv @@ -1,7 +1,15 @@ -instructor_code,instructor_name,department -1001,Dr. Ahsan Raza,CS -1002,Ms. Sana Tariq,CS -1003,Mr. Bilal Khan,CS -1004,Dr. Farah Iqbal,EE -1005,Mr. Usman Ali,EE -1006,Ms. Hira Shah,CS +instructor_code,instructor_name,course_id,section_id +1001,Dr. Ahsan Raza,CS101,A +1002,Ms. Sana Tariq,CS201,A +1001,Dr. Ahsan Raza,CS101,B +1002,Ms. Sana Tariq,CS201,B +1004,Dr. Farah Iqbal,EE101,A +1001,Dr. Ahsan Raza,CS101,C +1002,Ms. Sana Tariq,CS201,C +1001,Dr. Ahsan Raza,CS101,D +1002,Ms. Sana Tariq,CS201,D +1004,Dr. Farah Iqbal,EE101,B +1002,Ms. Sana Tariq,GEN101,ALL +1006,Ms. Hira Shah,GEN102,ALL +1002,Ms. Sana Tariq,GEN101,ALL +1006,Ms. Hira Shah,GEN102,ALL diff --git a/logs/template_generator.log b/logs/template_generator.log index a616b34..bf62da0 100644 --- a/logs/template_generator.log +++ b/logs/template_generator.log @@ -1,4 +1,4 @@ -Timetable generation log - started 2026-09-09T17:07:54.250382 +Timetable generation log - started 2026-09-09T17:48:16.013663 ================================================================================ -[17:07:54] INFO: Running conflict checks (student/batch and teacher)... -[17:07:54] INFO: No conflicts found — schedule is clash-free for students and teachers. +[17:48:16] INFO: Running conflict checks (student/batch and teacher)... +[17:48:16] INFO: No conflicts found — schedule is clash-free for students and teachers. diff --git a/result/templates/batch-wise/25021519.xlsx b/result/templates/batch-wise/25021519.xlsx index 7769396..42ef45d 100644 Binary files a/result/templates/batch-wise/25021519.xlsx and b/result/templates/batch-wise/25021519.xlsx differ diff --git a/result/templates/batch-wise/25021520.xlsx b/result/templates/batch-wise/25021520.xlsx index 644dc93..c3af9ff 100644 Binary files a/result/templates/batch-wise/25021520.xlsx and b/result/templates/batch-wise/25021520.xlsx differ diff --git a/result/templates/batch-wise/25031519.xlsx b/result/templates/batch-wise/25031519.xlsx index f518f0f..2a2788c 100644 Binary files a/result/templates/batch-wise/25031519.xlsx and b/result/templates/batch-wise/25031519.xlsx differ diff --git a/result/templates/batch-wise/26021519.xlsx b/result/templates/batch-wise/26021519.xlsx index 39624ce..7d69467 100644 Binary files a/result/templates/batch-wise/26021519.xlsx and b/result/templates/batch-wise/26021519.xlsx differ diff --git a/result/templates/batch-wise/26021520.xlsx b/result/templates/batch-wise/26021520.xlsx index babf013..405eecb 100644 Binary files a/result/templates/batch-wise/26021520.xlsx and b/result/templates/batch-wise/26021520.xlsx differ diff --git a/result/templates/batch-wise/26031519.xlsx b/result/templates/batch-wise/26031519.xlsx index 9050dbc..7280a84 100644 Binary files a/result/templates/batch-wise/26031519.xlsx and b/result/templates/batch-wise/26031519.xlsx differ diff --git a/result/templates/department-wise/CS.xlsx b/result/templates/department-wise/CS.xlsx index 5386d80..48bc5be 100644 Binary files a/result/templates/department-wise/CS.xlsx and b/result/templates/department-wise/CS.xlsx differ diff --git a/result/templates/department-wise/EE.xlsx b/result/templates/department-wise/EE.xlsx index 0830a3a..ba5f4d0 100644 Binary files a/result/templates/department-wise/EE.xlsx and b/result/templates/department-wise/EE.xlsx differ diff --git a/result/templates/room-wise/A-101.xlsx b/result/templates/room-wise/A-101.xlsx index c500a97..4848058 100644 Binary files a/result/templates/room-wise/A-101.xlsx and b/result/templates/room-wise/A-101.xlsx differ diff --git a/result/templates/room-wise/A-102.xlsx b/result/templates/room-wise/A-102.xlsx index b189add..e5b5fc9 100644 Binary files a/result/templates/room-wise/A-102.xlsx and b/result/templates/room-wise/A-102.xlsx differ diff --git a/result/templates/room-wise/A-103.xlsx b/result/templates/room-wise/A-103.xlsx index 768ecab..fa4c8b9 100644 Binary files a/result/templates/room-wise/A-103.xlsx and b/result/templates/room-wise/A-103.xlsx differ diff --git a/result/templates/room-wise/A-201.xlsx b/result/templates/room-wise/A-201.xlsx index 9515cd1..a095eed 100644 Binary files a/result/templates/room-wise/A-201.xlsx and b/result/templates/room-wise/A-201.xlsx differ diff --git a/result/templates/room-wise/A-L01.xlsx b/result/templates/room-wise/A-L01.xlsx index e80bea5..6d42bcc 100644 Binary files a/result/templates/room-wise/A-L01.xlsx and b/result/templates/room-wise/A-L01.xlsx differ diff --git a/result/templates/room-wise/A-L02.xlsx b/result/templates/room-wise/A-L02.xlsx index 80cce9a..d5e69be 100644 Binary files a/result/templates/room-wise/A-L02.xlsx and b/result/templates/room-wise/A-L02.xlsx differ diff --git a/result/templates/student-wise/25021519-001.xlsx b/result/templates/student-wise/25021519-001.xlsx index e2b128f..90d7de1 100644 Binary files a/result/templates/student-wise/25021519-001.xlsx and b/result/templates/student-wise/25021519-001.xlsx differ diff --git a/result/templates/student-wise/25021519-002.xlsx b/result/templates/student-wise/25021519-002.xlsx index 5d8400e..c33b5fb 100644 Binary files a/result/templates/student-wise/25021519-002.xlsx and b/result/templates/student-wise/25021519-002.xlsx differ diff --git a/result/templates/student-wise/25021519-003.xlsx b/result/templates/student-wise/25021519-003.xlsx index 33dd25a..3d40004 100644 Binary files a/result/templates/student-wise/25021519-003.xlsx and b/result/templates/student-wise/25021519-003.xlsx differ diff --git a/result/templates/student-wise/25021519-004.xlsx b/result/templates/student-wise/25021519-004.xlsx index 6b67e12..6127ced 100644 Binary files a/result/templates/student-wise/25021519-004.xlsx and b/result/templates/student-wise/25021519-004.xlsx differ diff --git a/result/templates/student-wise/25021519-005.xlsx b/result/templates/student-wise/25021519-005.xlsx index 563aed0..26b9ee0 100644 Binary files a/result/templates/student-wise/25021519-005.xlsx and b/result/templates/student-wise/25021519-005.xlsx differ diff --git a/result/templates/student-wise/25021520-001.xlsx b/result/templates/student-wise/25021520-001.xlsx index d67bb89..ebbf9ee 100644 Binary files a/result/templates/student-wise/25021520-001.xlsx and b/result/templates/student-wise/25021520-001.xlsx differ diff --git a/result/templates/student-wise/25021520-002.xlsx b/result/templates/student-wise/25021520-002.xlsx index c78ab93..7f29d96 100644 Binary files a/result/templates/student-wise/25021520-002.xlsx and b/result/templates/student-wise/25021520-002.xlsx differ diff --git a/result/templates/student-wise/25021520-003.xlsx b/result/templates/student-wise/25021520-003.xlsx index ab162d8..4d5cc64 100644 Binary files a/result/templates/student-wise/25021520-003.xlsx and b/result/templates/student-wise/25021520-003.xlsx differ diff --git a/result/templates/student-wise/25021520-004.xlsx b/result/templates/student-wise/25021520-004.xlsx index 03fbf1d..e0e7b08 100644 Binary files a/result/templates/student-wise/25021520-004.xlsx and b/result/templates/student-wise/25021520-004.xlsx differ diff --git a/result/templates/student-wise/25021520-005.xlsx b/result/templates/student-wise/25021520-005.xlsx index 8eb25e0..b5ff5a0 100644 Binary files a/result/templates/student-wise/25021520-005.xlsx and b/result/templates/student-wise/25021520-005.xlsx differ diff --git a/result/templates/student-wise/25031519-001.xlsx b/result/templates/student-wise/25031519-001.xlsx index 6aae24b..70a5265 100644 Binary files a/result/templates/student-wise/25031519-001.xlsx and b/result/templates/student-wise/25031519-001.xlsx differ diff --git a/result/templates/student-wise/25031519-002.xlsx b/result/templates/student-wise/25031519-002.xlsx index 26132d0..d82ac87 100644 Binary files a/result/templates/student-wise/25031519-002.xlsx and b/result/templates/student-wise/25031519-002.xlsx differ diff --git a/result/templates/student-wise/25031519-003.xlsx b/result/templates/student-wise/25031519-003.xlsx index 68b2161..deba11f 100644 Binary files a/result/templates/student-wise/25031519-003.xlsx and b/result/templates/student-wise/25031519-003.xlsx differ diff --git a/result/templates/student-wise/25031519-004.xlsx b/result/templates/student-wise/25031519-004.xlsx index 6cf4a31..45c0077 100644 Binary files a/result/templates/student-wise/25031519-004.xlsx and b/result/templates/student-wise/25031519-004.xlsx differ diff --git a/result/templates/student-wise/25031519-005.xlsx b/result/templates/student-wise/25031519-005.xlsx index 571a693..d505112 100644 Binary files a/result/templates/student-wise/25031519-005.xlsx and b/result/templates/student-wise/25031519-005.xlsx differ diff --git a/result/templates/student-wise/26021519-001.xlsx b/result/templates/student-wise/26021519-001.xlsx index 62cd746..6c94f2b 100644 Binary files a/result/templates/student-wise/26021519-001.xlsx and b/result/templates/student-wise/26021519-001.xlsx differ diff --git a/result/templates/student-wise/26021519-002.xlsx b/result/templates/student-wise/26021519-002.xlsx index f1472a3..1245cc9 100644 Binary files a/result/templates/student-wise/26021519-002.xlsx and b/result/templates/student-wise/26021519-002.xlsx differ diff --git a/result/templates/student-wise/26021519-003.xlsx b/result/templates/student-wise/26021519-003.xlsx index ac6a67a..35cc3a4 100644 Binary files a/result/templates/student-wise/26021519-003.xlsx and b/result/templates/student-wise/26021519-003.xlsx differ diff --git a/result/templates/student-wise/26021519-004.xlsx b/result/templates/student-wise/26021519-004.xlsx index 6a11e7c..f6a70e4 100644 Binary files a/result/templates/student-wise/26021519-004.xlsx and b/result/templates/student-wise/26021519-004.xlsx differ diff --git a/result/templates/student-wise/26021519-005.xlsx b/result/templates/student-wise/26021519-005.xlsx index c88bf74..134b34f 100644 Binary files a/result/templates/student-wise/26021519-005.xlsx and b/result/templates/student-wise/26021519-005.xlsx differ diff --git a/result/templates/student-wise/26021520-001.xlsx b/result/templates/student-wise/26021520-001.xlsx index e0ed758..9414d2b 100644 Binary files a/result/templates/student-wise/26021520-001.xlsx and b/result/templates/student-wise/26021520-001.xlsx differ diff --git a/result/templates/student-wise/26021520-002.xlsx b/result/templates/student-wise/26021520-002.xlsx index 172bd48..6f03ad4 100644 Binary files a/result/templates/student-wise/26021520-002.xlsx and b/result/templates/student-wise/26021520-002.xlsx differ diff --git a/result/templates/student-wise/26021520-003.xlsx b/result/templates/student-wise/26021520-003.xlsx index 6be6062..3e8bca1 100644 Binary files a/result/templates/student-wise/26021520-003.xlsx and b/result/templates/student-wise/26021520-003.xlsx differ diff --git a/result/templates/student-wise/26021520-004.xlsx b/result/templates/student-wise/26021520-004.xlsx index a5e0f37..6b03795 100644 Binary files a/result/templates/student-wise/26021520-004.xlsx and b/result/templates/student-wise/26021520-004.xlsx differ diff --git a/result/templates/student-wise/26021520-005.xlsx b/result/templates/student-wise/26021520-005.xlsx index 29d5aa3..f3aa85c 100644 Binary files a/result/templates/student-wise/26021520-005.xlsx and b/result/templates/student-wise/26021520-005.xlsx differ diff --git a/result/templates/student-wise/26031519-001.xlsx b/result/templates/student-wise/26031519-001.xlsx index 968dfe8..9184c91 100644 Binary files a/result/templates/student-wise/26031519-001.xlsx and b/result/templates/student-wise/26031519-001.xlsx differ diff --git a/result/templates/student-wise/26031519-002.xlsx b/result/templates/student-wise/26031519-002.xlsx index 4d22656..734c43e 100644 Binary files a/result/templates/student-wise/26031519-002.xlsx and b/result/templates/student-wise/26031519-002.xlsx differ diff --git a/result/templates/student-wise/26031519-003.xlsx b/result/templates/student-wise/26031519-003.xlsx index 76dbcf6..a5f6e91 100644 Binary files a/result/templates/student-wise/26031519-003.xlsx and b/result/templates/student-wise/26031519-003.xlsx differ diff --git a/result/templates/student-wise/26031519-004.xlsx b/result/templates/student-wise/26031519-004.xlsx index 5dc5666..1e104e7 100644 Binary files a/result/templates/student-wise/26031519-004.xlsx and b/result/templates/student-wise/26031519-004.xlsx differ diff --git a/result/templates/student-wise/26031519-005.xlsx b/result/templates/student-wise/26031519-005.xlsx index 1ae46a3..24e5e9d 100644 Binary files a/result/templates/student-wise/26031519-005.xlsx and b/result/templates/student-wise/26031519-005.xlsx differ diff --git a/result/templates/teacher-wise/1001.xlsx b/result/templates/teacher-wise/1001.xlsx index 2249258..fb9d462 100644 Binary files a/result/templates/teacher-wise/1001.xlsx and b/result/templates/teacher-wise/1001.xlsx differ diff --git a/result/templates/teacher-wise/1002.xlsx b/result/templates/teacher-wise/1002.xlsx index 5a50ba1..2099a22 100644 Binary files a/result/templates/teacher-wise/1002.xlsx and b/result/templates/teacher-wise/1002.xlsx differ diff --git a/result/templates/teacher-wise/1003.xlsx b/result/templates/teacher-wise/1003.xlsx deleted file mode 100644 index 1978e3d..0000000 Binary files a/result/templates/teacher-wise/1003.xlsx and /dev/null differ diff --git a/result/templates/teacher-wise/1004.xlsx b/result/templates/teacher-wise/1004.xlsx index 0449c55..6f3b279 100644 Binary files a/result/templates/teacher-wise/1004.xlsx and b/result/templates/teacher-wise/1004.xlsx differ diff --git a/result/templates/teacher-wise/1005.xlsx b/result/templates/teacher-wise/1005.xlsx deleted file mode 100644 index ccee4ca..0000000 Binary files a/result/templates/teacher-wise/1005.xlsx and /dev/null differ diff --git a/result/templates/teacher-wise/1006.xlsx b/result/templates/teacher-wise/1006.xlsx index 36683b2..2b03a6b 100644 Binary files a/result/templates/teacher-wise/1006.xlsx and b/result/templates/teacher-wise/1006.xlsx differ diff --git a/templates/batch-wise/batch_wise_template.py b/templates/batch-wise/batch_wise_template.py index aaa617c..9ca736b 100644 --- a/templates/batch-wise/batch_wise_template.py +++ b/templates/batch-wise/batch_wise_template.py @@ -37,7 +37,7 @@ def generate_batch_wise_templates(sessions, data, output_dir): lines = [] for s in entries: tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "") - lines.append(f'{s["course_name"]}{tag}\n{s["instructor_name"]}\n{s["room_id"]}') + lines.append(f'{s["course_name"]}{tag} [Sec {s["section_id"]}]\n{s["instructor_name"]}\n{s["room_id"]}') return "\n---\n".join(lines) first_by_day_slot = {k: v[0] for k, v in by_day_slot.items()} diff --git a/templates/department-wise/department_wise_template.py b/templates/department-wise/department_wise_template.py index f8d679b..3aff6ad 100644 --- a/templates/department-wise/department_wise_template.py +++ b/templates/department-wise/department_wise_template.py @@ -32,7 +32,7 @@ def generate_department_wise_templates(sessions, data, output_dir): for s in entries: tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "") batches = "+".join(s["batch_codes"]) - lines.append(f'{s["course_name"]}{tag} [{batches}]\n{s["instructor_name"]} @ {s["room_id"]}') + lines.append(f'{s["course_name"]}{tag} [Sec {s["section_id"]}] [{batches}]\n{s["instructor_name"]} @ {s["room_id"]}') return "\n---\n".join(lines) # for fill-color purposes just use the first session in each cell diff --git a/templates/room-wise/room_wise_template.py b/templates/room-wise/room_wise_template.py index 31c398b..0620630 100644 --- a/templates/room-wise/room_wise_template.py +++ b/templates/room-wise/room_wise_template.py @@ -33,7 +33,7 @@ def generate_room_wise_templates(sessions, data, output_dir): for s in entries: tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "") batches = "+".join(s["batch_codes"]) - lines.append(f'{s["course_name"]}{tag}\n{batches}\n{s["instructor_name"]}') + lines.append(f'{s["course_name"]}{tag} [Sec {s["section_id"]}]\n{batches}\n{s["instructor_name"]}') return "\n---\n".join(lines) first_by_day_slot = {k: v[0] for k, v in by_day_slot.items()} diff --git a/templates/student-wise/student_wise_template.py b/templates/student-wise/student_wise_template.py index 07f9c0b..f6edb77 100644 --- a/templates/student-wise/student_wise_template.py +++ b/templates/student-wise/student_wise_template.py @@ -37,7 +37,7 @@ def generate_student_wise_templates(sessions, data, output_dir): for (day, slot_index), entries in raw_by_day_slot.items(): if len(entries) > 1: course_list = ", ".join( - f'{e["course_id"]} ({e["course_type"]}) with {e["instructor_name"]} in {e["room_id"]}' + f'{e["course_id"]} Sec {e["section_id"]} ({e["course_type"]}) with {e["instructor_name"]} in {e["room_id"]}' for e in entries ) log_error( @@ -52,7 +52,7 @@ def generate_student_wise_templates(sessions, data, output_dir): return "" s = entries[0] tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "") - text = f'{s["course_name"]}{tag}\n{s["instructor_name"]}\n{s["room_id"]}' + text = f'{s["course_name"]}{tag} [Sec {s["section_id"]}]\n{s["instructor_name"]}\n{s["room_id"]}' if len(entries) > 1: text = "CONFLICT!\n" + text return text diff --git a/templates/teacher-wise/teacher_wise_template.py b/templates/teacher-wise/teacher_wise_template.py index 76e6732..069f419 100644 --- a/templates/teacher-wise/teacher_wise_template.py +++ b/templates/teacher-wise/teacher_wise_template.py @@ -14,8 +14,23 @@ from template_logger import log_error # noqa: E402 def generate_teacher_wise_templates(sessions, data, output_dir): written = [] - for instructor in data["instructors"]: - code = instructor["instructor_code"] + + # instructors.csv has one row per (instructor, course, section) teaching + # assignment, so the same instructor_code can repeat; render one sheet + # per unique instructor, listing all of their assignments in the subtitle. + seen_codes = [] + assignments_by_code = {} + name_by_code = {} + for row in data["instructors"]: + code = row["instructor_code"] + if code not in assignments_by_code: + seen_codes.append(code) + assignments_by_code[code] = [] + name_by_code[code] = row["instructor_name"] + assignments_by_code[code].append(f'{row["course_id"]} (Sec {row["section_id"]})') + + for code in seen_codes: + instructor_name = name_by_code[code] instr_sessions = [s for s in sessions if s["instructor_code"] == code] # A teacher can only be in one place at a time: each (day, slot) @@ -35,7 +50,7 @@ def generate_teacher_wise_templates(sessions, data, output_dir): for e in entries ) log_error( - f"Teacher conflict: {instructor['instructor_name']} ({code}) is double-booked on " + f"Teacher conflict: {instructor_name} ({code}) is double-booked on " f"{day} slot {slot_index}: {course_list}" ) by_day_slot[(day, slot_index)] = entries[0] @@ -47,17 +62,19 @@ def generate_teacher_wise_templates(sessions, data, output_dir): s = entries[0] tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "") batches = "+".join(s["batch_codes"]) - text = f'{s["course_name"]}{tag}\n{batches}\n{s["room_id"]}' + text = f'{s["course_name"]}{tag} [Sec {s["section_id"]}]\n{batches}\n{s["room_id"]}' if len(entries) > 1: text = "CONFLICT!\n" + text return text + subtitle = "Teaches: " + ", ".join(assignments_by_code[code]) + wb = new_workbook() ws = wb.active build_grid_sheet( ws, - title=f'{instructor["instructor_name"]} ({code})', - subtitle=f'Department: {instructor["department"]}', + title=f'{instructor_name} ({code})', + subtitle=subtitle, time_slots=GRID_TIME_SLOTS, sessions_by_day_slot=by_day_slot, cell_text_fn=cell_text,