data templates corrected
This commit is contained in:
@@ -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,22 +149,29 @@ 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:
|
||||
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 self.classrooms:
|
||||
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)
|
||||
@@ -149,16 +179,22 @@ class Scheduler:
|
||||
break
|
||||
if placed:
|
||||
break
|
||||
if placed:
|
||||
break
|
||||
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):
|
||||
labs_offerings = [o for o in self.data["offerings"] if o["course_type"] == "lab" and not o["is_general"]]
|
||||
for offering in labs_offerings:
|
||||
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 self.labs:
|
||||
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"])
|
||||
@@ -171,15 +207,19 @@ class Scheduler:
|
||||
if placed:
|
||||
break
|
||||
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):
|
||||
theory_offerings = [o for o in self.data["offerings"] if o["course_type"] == "theory" and not o["is_general"]]
|
||||
for offering in theory_offerings:
|
||||
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 self.classrooms:
|
||||
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)
|
||||
@@ -190,7 +230,7 @@ class Scheduler:
|
||||
if placed:
|
||||
break
|
||||
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):
|
||||
self.schedule_general_courses()
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
|
@@ -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:
|
||||
|
||||
@@ -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,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.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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()}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user