""" Verifies that no instructor is booked into two sessions at the same day+slot (whether both are lectures, both labs, or a mix of either). Like student_conflict_checker, this re-derives occupancy directly from the session list rather than trusting the scheduler's internal busy-maps. """ from collections import defaultdict def check_teacher_conflicts(sessions): """ Returns a list of conflict dicts, one per (instructor_code, day, slot) where two or more sessions overlap for that instructor: { "instructor_code": str, "day": str, "slot_index": int, "sessions": [session, session, ...], } """ # (instructor_code, day, slot_index) -> list of sessions occupying that cell occupied = defaultdict(list) for session in sessions: day = session["day"] instructor_code = session["instructor_code"] for slot_index in session["slot_indices"]: occupied[(instructor_code, day, slot_index)].append(session) conflicts = [] for (instructor_code, day, slot_index), cell_sessions in occupied.items(): if len(cell_sessions) > 1: conflicts.append({ "instructor_code": instructor_code, "day": day, "slot_index": slot_index, "sessions": cell_sessions, }) conflicts.sort(key=lambda c: (c["instructor_code"], c["day"], c["slot_index"])) return conflicts def format_teacher_conflicts(conflicts): """Human-readable lines describing each teacher conflict.""" lines = [] for c in conflicts: course_list = ", ".join( f'{s["course_id"]} ({s["course_type"]}) for batch(es) {"+".join(s["batch_codes"])} in {s["room_id"]}' for s in c["sessions"] ) instructor_name = c["sessions"][0]["instructor_name"] lines.append( f'Instructor {instructor_name} ({c["instructor_code"]}) is double-booked on ' f'{c["day"]} slot {c["slot_index"]}: {course_list}' ) return lines