data templates corrected

This commit is contained in:
Talhadeveloperr
2026-09-09 17:50:37 +05:00
parent 3840d2104c
commit aa3faf674f
61 changed files with 227 additions and 161 deletions

View File

@@ -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,71 +149,88 @@ 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:
placed = False
for slot_index in TEACHING_SLOT_INDICES:
for room in self.classrooms:
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)
placed = True
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 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)
placed = True
break
if placed:
break
if placed:
break
if placed:
break
if not placed:
print(f"WARNING: could not place general course {offering['course_id']}")
if not placed:
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:
placed = False
for day in DAYS:
for slot_a, slot_b in LAB_SLOT_PAIRS:
for room in self.labs:
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"])
self._mark_busy(day, slot_b, room["room_id"], offering["instructor_code"], offering["batch_codes"])
self._add_session(offering, day, [slot_a, slot_b], room)
placed = True
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 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"])
self._mark_busy(day, slot_b, room["room_id"], offering["instructor_code"], offering["batch_codes"])
self._add_session(offering, day, [slot_a, slot_b], room)
placed = True
break
if placed:
break
if placed:
break
if placed:
break
if not placed:
print(f"WARNING: could not place lab course {offering['course_id']} for {offering['batch_codes']}")
if not placed:
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:
placed = False
for day in DAYS:
for slot_index in TEACHING_SLOT_INDICES:
for room in self.classrooms:
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)
placed = True
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 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)
placed = True
break
if placed:
break
if placed:
break
if placed:
break
if not placed:
print(f"WARNING: could not place theory course {offering['course_id']} for {offering['batch_codes']}")
if not placed:
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()