data templates corrected

This commit is contained in:
Talhadeveloperr
2026-09-09 17:50:37 +05:00
parent 3840d2104c
commit aa3faf674f
61 changed files with 227 additions and 161 deletions

View File

@@ -5,12 +5,18 @@ five templates/*-wise template modules render from.
Scheduling rules (dummy/demo scheduler, not a full CSP solver): Scheduling rules (dummy/demo scheduler, not a full CSP solver):
1. General courses (shared across every batch of a session) are placed 1. General courses (shared across every batch of a session) are placed
first, only on Tuesday, so those slots are locked in before anything first, only on Tuesday, so those slots are locked in before anything
else and never clash across the batches sharing them. else and never clash across the batches sharing them. Each general
2. Lab courses (180-minute / double-slot) are placed next, in lab rooms offering gets `lectures_per_week` sessions.
only, on whichever day/slot-pair is free for the batch, instructor, 2. Lab courses are placed next: each offering gets `labs_per_week`
and room. 180-minute / double-slot sessions, in lab rooms only, on whichever
3. Remaining theory courses fill the rest of the week (Mon/Tue/Wed/Thu/Fri), day/slot-pair is free for the batch, instructor, and room.
one 90-minute slot at a time, avoiding room / instructor / batch clashes. 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 import csv
@@ -59,22 +65,32 @@ def load_data():
batches_26 = _read_csv(os.path.join("batch_codes", "26", "batches.csv")) batches_26 = _read_csv(os.path.join("batch_codes", "26", "batches.csv"))
batches = batches_25 + batches_26 batches = batches_25 + batches_26
courses_by_id = {c["course_id"]: c for c in courses} rooms_by_id = {r["room_id"]: r for r in rooms}
instructors_by_code = {i["instructor_code"]: i for i in instructors} 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} batches_by_code = {b["batch_code"]: b for b in batches}
# normalize offerings: expand "batch1|batch2" general offerings into a # normalize courses: numeric fields + a lookup by id
# list of batch codes on each offering record 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: for off in offerings:
off["batch_codes"] = off["batch_code"].split("|") off["batch_codes"] = off["batch_codes"].split("|")
off["is_general"] = courses_by_id[off["course_id"]]["is_general"].lower() == "yes" off["course"] = courses_by_id[off["course_id"]]
off["course_type"] = courses_by_id[off["course_id"]]["course_type"] off["instructor_name"] = instructor_names_by_code.get(off["instructor_code"], off["instructor_code"])
return { return {
"rooms": rooms, "rooms": rooms,
"rooms_by_id": rooms_by_id,
"blocks": blocks, "blocks": blocks,
"instructors": instructors, "instructors": instructors,
"instructors_by_code": instructors_by_code, "instructor_names_by_code": instructor_names_by_code,
"courses": courses, "courses": courses,
"courses_by_id": courses_by_id, "courses_by_id": courses_by_id,
"offerings": offerings, "offerings": offerings,
@@ -115,10 +131,17 @@ class Scheduler:
self.instructor_busy.setdefault(key, set()).add(instructor_code) self.instructor_busy.setdefault(key, set()).add(instructor_code)
self.batch_busy.setdefault(key, set()).update(batch_codes) 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): 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"] start = TIME_SLOTS[slot_indices[0]]["start"]
end = TIME_SLOTS[slot_indices[-1]]["end"] end = TIME_SLOTS[slot_indices[-1]]["end"]
session_type = "lab" if len(slot_indices) > 1 else "theory"
self.sessions.append({ self.sessions.append({
"day": day, "day": day,
"slot_indices": slot_indices, "slot_indices": slot_indices,
@@ -126,22 +149,29 @@ class Scheduler:
"end": end, "end": end,
"course_id": offering["course_id"], "course_id": offering["course_id"],
"course_name": course["course_name"], "course_name": course["course_name"],
"course_type": course["course_type"], "course_type": session_type,
"is_general": offering["is_general"], "is_general": course["is_general"],
"section_id": offering["section_id"],
"instructor_code": offering["instructor_code"], "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_id": room["room_id"],
"room_name": room["room_name"], "room_name": room["room_name"],
"batch_codes": offering["batch_codes"], "batch_codes": offering["batch_codes"],
}) })
def schedule_general_courses(self): def schedule_general_courses(self):
day = "Tuesday" general = [o for o in self.data["offerings"] if o["course"]["is_general"]]
general = [o for o in self.data["offerings"] if o["is_general"]]
for offering in general: for offering in general:
course = offering["course"]
room_candidates = self._room_candidates(course["lecture_room_id"], self.classrooms)
for _ in range(course["lectures_per_week"]):
placed = False 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 slot_index in TEACHING_SLOT_INDICES:
for room in self.classrooms: for room in room_candidates:
if self._is_free(day, slot_index, room["room_id"], offering["instructor_code"], offering["batch_codes"]): 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._mark_busy(day, slot_index, room["room_id"], offering["instructor_code"], offering["batch_codes"])
self._add_session(offering, day, [slot_index], room) self._add_session(offering, day, [slot_index], room)
@@ -149,16 +179,22 @@ class Scheduler:
break break
if placed: if placed:
break break
if placed:
break
if not placed: if not placed:
print(f"WARNING: could not place general course {offering['course_id']}") print(f"WARNING: could not place a lecture for general course {offering['course_id']} section {offering['section_id']}")
def schedule_lab_courses(self): def schedule_lab_courses(self):
labs_offerings = [o for o in self.data["offerings"] if o["course_type"] == "lab" and not o["is_general"]] offerings = [o for o in self.data["offerings"] if not o["course"]["is_general"] and o["course"]["labs_per_week"] > 0]
for offering in labs_offerings: 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 placed = False
for day in DAYS: for day in DAYS:
for slot_a, slot_b in LAB_SLOT_PAIRS: for slot_a, slot_b in LAB_SLOT_PAIRS:
for room in self.labs: for room in room_candidates:
if self._is_free(day, slot_a, room["room_id"], offering["instructor_code"], offering["batch_codes"]) and \ 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._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_a, room["room_id"], offering["instructor_code"], offering["batch_codes"])
@@ -171,15 +207,19 @@ class Scheduler:
if placed: if placed:
break break
if not placed: if not placed:
print(f"WARNING: could not place lab course {offering['course_id']} for {offering['batch_codes']}") 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): def schedule_theory_courses(self):
theory_offerings = [o for o in self.data["offerings"] if o["course_type"] == "theory" and not o["is_general"]] offerings = [o for o in self.data["offerings"] if not o["course"]["is_general"]]
for offering in theory_offerings: 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 placed = False
for day in DAYS: for day in DAYS:
for slot_index in TEACHING_SLOT_INDICES: for slot_index in TEACHING_SLOT_INDICES:
for room in self.classrooms: for room in room_candidates:
if self._is_free(day, slot_index, room["room_id"], offering["instructor_code"], offering["batch_codes"]): 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._mark_busy(day, slot_index, room["room_id"], offering["instructor_code"], offering["batch_codes"])
self._add_session(offering, day, [slot_index], room) self._add_session(offering, day, [slot_index], room)
@@ -190,7 +230,7 @@ class Scheduler:
if placed: if placed:
break break
if not placed: if not placed:
print(f"WARNING: could not place theory course {offering['course_id']} for {offering['batch_codes']}") print(f"WARNING: could not place a lecture for course {offering['course_id']} section {offering['section_id']} ({offering['batch_codes']})")
def run(self): def run(self):
self.schedule_general_courses() self.schedule_general_courses()

View File

@@ -1,25 +1,15 @@
course_id,batch_code,instructor_code course_id,section_id,batch_codes,instructor_code
CS101,25021519,1001 CS101,A,25021519,1001
CS102,25021519,1001 CS201,A,25021519,1002
CS201,25021519,1002 CS101,B,25021520,1001
CS202,25021519,1002 CS201,B,25021520,1002
CS101,25021520,1001 EE101,A,25031519,1004
CS102,25021520,1001 CS101,C,26021519,1001
CS201,25021520,1002 CS201,C,26021519,1002
CS202,25021520,1002 CS101,D,26021520,1001
EE101,25031519,1004 CS201,D,26021520,1002
EE102,25031519,1004 EE101,B,26031519,1004
CS101,26021519,1001 GEN101,A,25021519|25021520|25031519,1002
CS102,26021519,1001 GEN102,A,25021519|25021520|25031519,1006
CS201,26021519,1002 GEN101,B,26021519|26021520|26031519,1002
CS202,26021519,1002 GEN102,B,26021519|26021520|26031519,1006
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
1 course_id batch_code section_id batch_codes instructor_code
2 CS101 25021519 A 25021519 1001
3 CS102 CS201 25021519 A 25021519 1001 1002
4 CS201 CS101 25021519 B 25021520 1002 1001
5 CS202 CS201 25021519 B 25021520 1002
6 CS101 EE101 25021520 A 25031519 1001 1004
7 CS102 CS101 25021520 C 26021519 1001
8 CS201 25021520 C 26021519 1002
9 CS202 CS101 25021520 D 26021520 1002 1001
10 EE101 CS201 25031519 D 26021520 1004 1002
11 EE102 EE101 25031519 B 26031519 1004
12 CS101 GEN101 26021519 A 25021519|25021520|25031519 1001 1002
13 CS102 GEN102 26021519 A 25021519|25021520|25031519 1001 1006
14 CS201 GEN101 26021519 B 26021519|26021520|26031519 1002
15 CS202 GEN102 26021519 B 26021519|26021520|26031519 1002 1006
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

View File

@@ -1,9 +1,6 @@
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
CS101,Programming Fundamentals,theory,3,no CS101,Programming Fundamentals,4,2,1,no,A-101,A-L01
CS102,Programming Fundamentals Lab,lab,1,no CS201,Data Structures,4,2,1,no,A-102,A-L01
CS201,Data Structures,theory,3,no EE101,Basic Electronics,4,2,1,no,A-201,A-L02
CS202,Data Structures Lab,lab,1,no GEN101,Functional English,3,2,0,yes,A-103,
EE101,Basic Electronics,theory,3,no GEN102,Islamic Studies,2,1,0,yes,A-103,
EE102,Basic Electronics Lab,lab,1,no
GEN101,Functional English,theory,3,yes
GEN102,Islamic Studies,theory,2,yes
1 course_id course_name course_type credit_hours lectures_per_week labs_per_week is_general lecture_room_id lab_room_id
2 CS101 Programming Fundamentals theory 3 4 2 1 no A-101 A-L01
3 CS102 CS201 Programming Fundamentals Lab Data Structures lab 1 4 2 1 no A-102 A-L01
4 CS201 EE101 Data Structures Basic Electronics theory 3 4 2 1 no A-201 A-L02
5 CS202 GEN101 Data Structures Lab Functional English lab 1 3 2 0 no yes A-103
6 EE101 GEN102 Basic Electronics Islamic Studies theory 3 2 1 0 no yes A-103
EE102 Basic Electronics Lab lab 1 no
GEN101 Functional English theory 3 yes
GEN102 Islamic Studies theory 2 yes

View File

@@ -69,77 +69,91 @@ def main():
all_batches = batches_25 + batches_26 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 ---- # ---- courses ----
# is_general courses are shared across all batches of the same session # One row per subject: lectures/labs per week are counted separately so
# (so they can be scheduled together on Tuesday with no clash). # 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 = [ courses = [
["CS101", "Programming Fundamentals", "theory", 3, "no"], # course_id, course_name, credit_hours, lectures_per_week, labs_per_week, is_general, lecture_room_id, lab_room_id
["CS102", "Programming Fundamentals Lab", "lab", 1, "no"], ["CS101", "Programming Fundamentals", 4, 2, 1, "no", "A-101", "A-L01"],
["CS201", "Data Structures", "theory", 3, "no"], ["CS201", "Data Structures", 4, 2, 1, "no", "A-102", "A-L01"],
["CS202", "Data Structures Lab", "lab", 1, "no"], ["EE101", "Basic Electronics", 4, 2, 1, "no", "A-201", "A-L02"],
["EE101", "Basic Electronics", "theory", 3, "no"], ["GEN101", "Functional English", 3, 2, 0, "yes", "A-103", ""],
["EE102", "Basic Electronics Lab", "lab", 1, "no"], ["GEN102", "Islamic Studies", 2, 1, 0, "yes", "A-103", ""],
["GEN101", "Functional English", "theory", 3, "yes"],
["GEN102", "Islamic Studies", "theory", 2, "yes"],
] ]
write_csv( write_csv(
"courses/courses.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, courses,
) )
# ---- course offerings: which course is taught to which batch, by whom ---- # ---- course offerings: course + section (a section groups the batches
# General courses (GEN101/GEN102) are offered once per session (25/26) # taking that section together) + who teaches it ----
# and shared across every batch in that session. # 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 = [] offerings = []
dept_theory_lab = { dept_course = {"CS": ["CS101", "CS201"], "EE": ["EE101"]}
"CS": [("CS101", "CS102"), ("CS201", "CS202")],
"EE": [("EE101", "EE102")],
}
dept_instructors = { dept_instructors = {
"CS": ["1001", "1002", "1003", "1006"], "CS": ["1001", "1002", "1003", "1006"],
"EE": ["1004", "1005"], "EE": ["1004", "1005"],
} }
for batch_code, department, program, session in all_batches: section_letters = ["A", "B", "C", "D"]
pairs = dept_theory_lab[department] dept_section_counter = {"CS": 0, "EE": 0}
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])
# general courses: one offering per (course, session), batch_group lists for batch_code, department, program, session in all_batches:
# every batch code sharing that session, separated by "|" 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 ( for session, batch_group in (
("25", [b[0] for b in batches_25]), ("25", [b[0] for b in batches_25]),
("26", [b[0] for b in batches_26]), ("26", [b[0] for b in batches_26]),
): ):
offerings.append(["GEN101", "|".join(batch_group), "1002"]) offerings.append(["GEN101", "ALL", "|".join(batch_group), "1002"])
offerings.append(["GEN102", "|".join(batch_group), "1006"]) offerings.append(["GEN102", "ALL", "|".join(batch_group), "1006"])
write_csv( write_csv(
"courses/course_offering.csv", "courses/course_offering.csv",
["course_id", "batch_code", "instructor_code"], ["course_id", "section_id", "batch_codes", "instructor_code"],
offerings, 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 (roll_no = batch_code-seq) ----
students = [] students = []
for batch_code, department, program, session in all_batches: for batch_code, department, program, session in all_batches:

View File

@@ -1,7 +1,15 @@
instructor_code,instructor_name,department instructor_code,instructor_name,course_id,section_id
1001,Dr. Ahsan Raza,CS 1001,Dr. Ahsan Raza,CS101,A
1002,Ms. Sana Tariq,CS 1002,Ms. Sana Tariq,CS201,A
1003,Mr. Bilal Khan,CS 1001,Dr. Ahsan Raza,CS101,B
1004,Dr. Farah Iqbal,EE 1002,Ms. Sana Tariq,CS201,B
1005,Mr. Usman Ali,EE 1004,Dr. Farah Iqbal,EE101,A
1006,Ms. Hira Shah,CS 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
1 instructor_code instructor_name department course_id section_id
2 1001 Dr. Ahsan Raza CS CS101 A
3 1002 Ms. Sana Tariq CS CS201 A
4 1003 1001 Mr. Bilal Khan Dr. Ahsan Raza CS CS101 B
5 1004 1002 Dr. Farah Iqbal Ms. Sana Tariq EE CS201 B
6 1005 1004 Mr. Usman Ali Dr. Farah Iqbal EE EE101 A
7 1006 1001 Ms. Hira Shah Dr. Ahsan Raza CS CS101 C
8 1002 Ms. Sana Tariq CS201 C
9 1001 Dr. Ahsan Raza CS101 D
10 1002 Ms. Sana Tariq CS201 D
11 1004 Dr. Farah Iqbal EE101 B
12 1002 Ms. Sana Tariq GEN101 ALL
13 1006 Ms. Hira Shah GEN102 ALL
14 1002 Ms. Sana Tariq GEN101 ALL
15 1006 Ms. Hira Shah GEN102 ALL

View File

@@ -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:48:16] 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: No conflicts found — schedule is clash-free for students and teachers.

View File

@@ -37,7 +37,7 @@ def generate_batch_wise_templates(sessions, data, output_dir):
lines = [] lines = []
for s in entries: for s in entries:
tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "") 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) return "\n---\n".join(lines)
first_by_day_slot = {k: v[0] for k, v in by_day_slot.items()} first_by_day_slot = {k: v[0] for k, v in by_day_slot.items()}

View File

@@ -32,7 +32,7 @@ def generate_department_wise_templates(sessions, data, output_dir):
for s in entries: for s in entries:
tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "") tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "")
batches = "+".join(s["batch_codes"]) 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) return "\n---\n".join(lines)
# for fill-color purposes just use the first session in each cell # for fill-color purposes just use the first session in each cell

View File

@@ -33,7 +33,7 @@ def generate_room_wise_templates(sessions, data, output_dir):
for s in entries: for s in entries:
tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "") tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "")
batches = "+".join(s["batch_codes"]) 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) return "\n---\n".join(lines)
first_by_day_slot = {k: v[0] for k, v in by_day_slot.items()} first_by_day_slot = {k: v[0] for k, v in by_day_slot.items()}

View File

@@ -37,7 +37,7 @@ def generate_student_wise_templates(sessions, data, output_dir):
for (day, slot_index), entries in raw_by_day_slot.items(): for (day, slot_index), entries in raw_by_day_slot.items():
if len(entries) > 1: if len(entries) > 1:
course_list = ", ".join( 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 for e in entries
) )
log_error( log_error(
@@ -52,7 +52,7 @@ def generate_student_wise_templates(sessions, data, output_dir):
return "" return ""
s = entries[0] s = entries[0]
tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "") 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: if len(entries) > 1:
text = "CONFLICT!\n" + text text = "CONFLICT!\n" + text
return text return text

View File

@@ -14,8 +14,23 @@ from template_logger import log_error # noqa: E402
def generate_teacher_wise_templates(sessions, data, output_dir): def generate_teacher_wise_templates(sessions, data, output_dir):
written = [] 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] 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) # 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 for e in entries
) )
log_error( 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}" f"{day} slot {slot_index}: {course_list}"
) )
by_day_slot[(day, slot_index)] = entries[0] by_day_slot[(day, slot_index)] = entries[0]
@@ -47,17 +62,19 @@ def generate_teacher_wise_templates(sessions, data, output_dir):
s = entries[0] s = entries[0]
tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "") tag = " (Lab)" if s["course_type"] == "lab" else (" (General)" if s["is_general"] else "")
batches = "+".join(s["batch_codes"]) 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: if len(entries) > 1:
text = "CONFLICT!\n" + text text = "CONFLICT!\n" + text
return text return text
subtitle = "Teaches: " + ", ".join(assignments_by_code[code])
wb = new_workbook() wb = new_workbook()
ws = wb.active ws = wb.active
build_grid_sheet( build_grid_sheet(
ws, ws,
title=f'{instructor["instructor_name"]} ({code})', title=f'{instructor_name} ({code})',
subtitle=f'Department: {instructor["department"]}', subtitle=subtitle,
time_slots=GRID_TIME_SLOTS, time_slots=GRID_TIME_SLOTS,
sessions_by_day_slot=by_day_slot, sessions_by_day_slot=by_day_slot,
cell_text_fn=cell_text, cell_text_fn=cell_text,