85 lines
3.4 KiB
Python
85 lines
3.4 KiB
Python
"""Renders one timetable grid per instructor."""
|
|
|
|
import sys
|
|
import os
|
|
|
|
_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..")
|
|
sys.path.insert(0, os.path.join(_ROOT, "app"))
|
|
sys.path.insert(0, os.path.join(_ROOT, "controller"))
|
|
|
|
from xlsx_grid_helper import build_grid_sheet, new_workbook, save_workbook # noqa: E402
|
|
from template_controller import TIME_SLOTS as GRID_TIME_SLOTS # noqa: E402
|
|
from template_logger import log_error # noqa: E402
|
|
|
|
|
|
def generate_teacher_wise_templates(sessions, data, output_dir):
|
|
written = []
|
|
|
|
# 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)
|
|
# must resolve to exactly one session. If the schedule places more
|
|
# than one, that's a real scheduling error - log it instead of
|
|
# silently picking/overwriting one.
|
|
raw_by_day_slot = {}
|
|
for s in instr_sessions:
|
|
for slot_index in s["slot_indices"]:
|
|
raw_by_day_slot.setdefault((s["day"], slot_index), []).append(s)
|
|
|
|
by_day_slot = {}
|
|
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"]}) for batch(es) {"+".join(e["batch_codes"])} in {e["room_id"]}'
|
|
for e in entries
|
|
)
|
|
log_error(
|
|
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]
|
|
|
|
def cell_text(day, slot_index, raw_by_day_slot=raw_by_day_slot):
|
|
entries = raw_by_day_slot.get((day, slot_index))
|
|
if not entries:
|
|
return ""
|
|
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} [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_name} ({code})',
|
|
subtitle=subtitle,
|
|
time_slots=GRID_TIME_SLOTS,
|
|
sessions_by_day_slot=by_day_slot,
|
|
cell_text_fn=cell_text,
|
|
)
|
|
path = save_workbook(wb, output_dir, f"{code}.xlsx")
|
|
written.append(path)
|
|
return written
|