initial project

This commit is contained in:
Talhadeveloperr
2026-09-09 17:00:54 +05:00
commit a7bb2f3dae
72 changed files with 1051 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
from .student_conflict_checker import check_student_conflicts
from .teacher_conflict_checker import check_teacher_conflicts
__all__ = ["check_student_conflicts", "check_teacher_conflicts", "check_all_conflicts"]
def check_all_conflicts(sessions):
"""
Runs every conflict check against the given list of scheduled sessions
and returns a combined report:
{"student_conflicts": [...], "teacher_conflicts": [...], "has_conflicts": bool}
"""
student_conflicts = check_student_conflicts(sessions)
teacher_conflicts = check_teacher_conflicts(sessions)
return {
"student_conflicts": student_conflicts,
"teacher_conflicts": teacher_conflicts,
"has_conflicts": bool(student_conflicts or teacher_conflicts),
}

View File

@@ -0,0 +1,63 @@
"""
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

View File

@@ -0,0 +1,59 @@
"""
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