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):
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()

View File

@@ -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
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
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,
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
# ---- 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:

View File

@@ -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
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: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.

View File

@@ -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()}

View File

@@ -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

View File

@@ -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()}

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():
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

View File

@@ -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,