64 lines
2.2 KiB
Python
64 lines
2.2 KiB
Python
"""
|
|
Verifies that no batch (and therefore no student in that batch) is booked
|
|
into two courses — lecture or lab — at the same day+slot.
|
|
|
|
This is an independent verification pass over the scheduler's output: it
|
|
does not trust the scheduler's own busy-maps, it re-derives every batch's
|
|
occupied (day, slot_index) cells directly from the session list and flags
|
|
any cell used by more than one session.
|
|
"""
|
|
|
|
from collections import defaultdict
|
|
|
|
|
|
def check_student_conflicts(sessions):
|
|
"""
|
|
Returns a list of conflict dicts, one per (batch, day, slot) where two
|
|
or more sessions overlap for that batch:
|
|
{
|
|
"batch_code": str,
|
|
"day": str,
|
|
"slot_index": int,
|
|
"sessions": [session, session, ...],
|
|
}
|
|
Sessions that span multiple slot_indices (labs) occupy every one of
|
|
their slot_indices, so a lab overlapping a lecture in either of its two
|
|
slots is caught too.
|
|
"""
|
|
# (batch_code, day, slot_index) -> list of sessions occupying that cell
|
|
occupied = defaultdict(list)
|
|
|
|
for session in sessions:
|
|
day = session["day"]
|
|
for slot_index in session["slot_indices"]:
|
|
for batch_code in session["batch_codes"]:
|
|
occupied[(batch_code, day, slot_index)].append(session)
|
|
|
|
conflicts = []
|
|
for (batch_code, day, slot_index), cell_sessions in occupied.items():
|
|
if len(cell_sessions) > 1:
|
|
conflicts.append({
|
|
"batch_code": batch_code,
|
|
"day": day,
|
|
"slot_index": slot_index,
|
|
"sessions": cell_sessions,
|
|
})
|
|
|
|
conflicts.sort(key=lambda c: (c["batch_code"], c["day"], c["slot_index"]))
|
|
return conflicts
|
|
|
|
|
|
def format_student_conflicts(conflicts):
|
|
"""Human-readable lines describing each student/batch conflict."""
|
|
lines = []
|
|
for c in conflicts:
|
|
course_list = ", ".join(
|
|
f'{s["course_id"]} ({s["course_type"]}) with {s["instructor_name"]} in {s["room_id"]}'
|
|
for s in c["sessions"]
|
|
)
|
|
lines.append(
|
|
f'Batch {c["batch_code"]} has {len(c["sessions"])} overlapping courses on '
|
|
f'{c["day"]} slot {c["slot_index"]}: {course_list}'
|
|
)
|
|
return lines
|