""" Generates a larger, deliberately harder dummy CSV dataset under data/dummy/ so the timetable template generator has something non-trivial to schedule and render — more departments/batches/sections than the previous small dataset, tighter room and instructor contention (fewer rooms/instructors relative to demand), a second block, and mixed lecture durations (90 vs 120 minutes) so weaknesses in the scheduler surface as WARNINGs/conflicts instead of everything trivially fitting. Run once: python data/dummy/generate_dummy_data.py """ import csv import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) def write_csv(relative_path, header, rows): path = os.path.join(BASE_DIR, relative_path) os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow(header) writer.writerows(rows) print(f"wrote {len(rows)} rows -> {path}") def main(): # ---- blocks: two blocks now, Block A (bigger) and Block B (smaller) ---- write_csv( "blocks/blocks.csv", ["block_id", "block_name"], [ ["A", "Block A"], ["B", "Block B"], ], ) # ---- rooms: tight enough to keep real contention, but with enough # total capacity that a correct solver can always find a fit (room # capacity/student-count is not a constraint here) ---- write_csv( "blocks/rooms.csv", ["room_id", "room_name", "block_id", "room_type", "capacity"], [ ["A-101", "Room 101", "A", "classroom", 60], ["A-102", "Room 102", "A", "classroom", 60], ["A-103", "Room 103", "A", "classroom", 50], ["A-201", "Room 201", "A", "classroom", 60], ["A-202", "Room 202", "A", "classroom", 60], ["A-L01", "Computer Lab 1", "A", "lab", 35], ["A-L02", "Computer Lab 2", "A", "lab", 35], ["A-L03", "Computer Lab 3", "A", "lab", 35], ["B-101", "Room 101 (B)", "B", "classroom", 50], ["B-102", "Room 102 (B)", "B", "classroom", 50], ["B-L01", "Electronics Lab", "B", "lab", 30], ], ) # ---- departments / programs / batches: 3 departments, 2 programs # each, 2 sessions (25/26) -> 12 batches total (was 6) ---- department_programs = { "CS": ["BSCS", "BSSE"], "EE": ["BSEE", "BSCE"], "BBA": ["BBA", "BSAF"], } program_codes = { "BSCS": "021519", "BSSE": "021520", "BSEE": "031519", "BSCE": "031520", "BBA": "041519", "BSAF": "041520", } batches_25 = [] batches_26 = [] for department, programs in department_programs.items(): for program in programs: code = program_codes[program] batches_25.append([f"25{code}", department, program, "25"]) batches_26.append([f"26{code}", department, program, "26"]) write_csv( "batch_codes/25/batches.csv", ["batch_code", "department", "program", "session"], batches_25, ) write_csv( "batch_codes/26/batches.csv", ["batch_code", "department", "program", "session"], batches_26, ) all_batches = batches_25 + batches_26 # ---- courses ---- # course_id, course_name, credit_hours, lectures_per_week, # labs_per_week, is_general, lecture_room_id, lab_room_id, # lecture_duration_minutes # # Mixed lecture durations: most courses are 90-min x N/week, but a few # are the special "2 hours once a week" case (lecture_duration_minutes # 120, lectures_per_week 1). A course's lab, if it has one, is always # exactly once a week (180 minutes) — never more. Deliberately more # courses per department than the room/instructor pool can comfortably # absorb without contention. courses = [ # CS department courses — lab rooms spread across A-L01/A-L02/A-L03 # so no single lab room is asked for more sessions than exist # weekly slots for it (5 days x 2 lab-slot-pairs = 10/week). ["CS101", "Programming Fundamentals", 4, 2, 1, "no", "A-101", "A-L01", 90], ["CS201", "Data Structures", 4, 2, 1, "no", "A-102", "A-L02", 90], ["CS301", "Operating Systems", 3, 1, 1, "no", "A-101", "A-L03", 120], ["CS302", "Database Systems", 4, 2, 1, "no", "A-102", "A-L01", 90], # EE department courses ["EE101", "Basic Electronics", 4, 2, 1, "no", "A-201", "A-L02", 90], ["EE201", "Digital Logic Design", 4, 2, 1, "no", "B-101", "B-L01", 90], ["EE301", "Signals and Systems", 3, 1, 0, "no", "B-102", "", 120], # BBA department courses ["BBA101", "Principles of Management", 3, 2, 0, "no", "B-101", "", 90], ["BBA201", "Financial Accounting", 3, 2, 0, "no", "B-102", "", 90], ["BBA301", "Marketing Management", 3, 1, 0, "no", "A-103", "", 120], # General (shared) courses ["GEN101", "Functional English", 3, 2, 0, "yes", "A-103", "", 90], ["GEN102", "Islamic Studies", 2, 1, 0, "yes", "A-103", "", 90], ["GEN103", "Pakistan Studies", 2, 1, 0, "yes", "A-202", "", 120], ] write_csv( "courses/courses.csv", [ "course_id", "course_name", "credit_hours", "lectures_per_week", "labs_per_week", "is_general", "lecture_room_id", "lab_room_id", "lecture_duration_minutes", ], courses, ) # ---- course offerings: course + section (batches in that section) + # instructor ---- # Deliberately tight instructor pool reused across many sections/ # courses (some instructors will end up wanted in overlapping slots), # forcing real contention in Phase 2/Phase 3. dept_courses = { "CS": ["CS101", "CS201", "CS301", "CS302"], "EE": ["EE101", "EE201", "EE301"], "BBA": ["BBA101", "BBA201", "BBA301"], } # One instructor per course within a department (so no instructor ever # teaches two different courses to the same section/batch, which would # double their weekly slot demand against that batch's narrow shared # availability) — still a tight pool overall since each instructor # teaches their course across every section of the department. dept_instructors = { "CS": ["1001", "1002", "1003", "1009"], "EE": ["1004", "1005", "1010"], "BBA": ["1007", "1008", "1011"], } # CS302 additionally alternates between two instructors across its # sections (instead of one instructor owning all 4) since it has the # heaviest per-section load (2 lectures + 2 labs/week) — one person # covering every section of it would need more slot-0 room than a # single instructor realistically has once other batches' commitments # are accounted for. course_instructor_rotation = {"CS302": ["1009", "1012"]} instructor_names_extra = {"1012": "Ms. Rabia Nasir"} offerings = [] section_letters = ["A", "B", "C", "D"] dept_section_counter = {"CS": 0, "EE": 0, "BBA": 0} for batch_code, department, program, session in all_batches: section_index = dept_section_counter[department] section_id = section_letters[section_index % len(section_letters)] dept_section_counter[department] += 1 instr_pool = dept_instructors[department] # One instructor per course, consistent across every section, so # no instructor ever teaches two different courses to the same # batch (which would silently double their contested slot-0 # demand against that one batch). The pool is still tight overall # since each instructor teaches their course to every section of # the department -> real instructor contention across sections. for i, course_id in enumerate(dept_courses[department]): if course_id in course_instructor_rotation: rotation = course_instructor_rotation[course_id] instr = rotation[section_index % len(rotation)] else: instr = instr_pool[i % len(instr_pool)] offerings.append([course_id, section_id, batch_code, instr]) # general courses: one offering per (course, session), section "ALL" # grouping every batch code sharing that session for session, batch_group in ( ("25", [b[0] for b in batches_25]), ("26", [b[0] for b in batches_26]), ): offerings.append(["GEN101", "ALL", "|".join(batch_group), "1002"]) offerings.append(["GEN102", "ALL", "|".join(batch_group), "1006"]) offerings.append(["GEN103", "ALL", "|".join(batch_group), "1006"]) write_csv( "courses/course_offering.csv", ["course_id", "section_id", "batch_codes", "instructor_code"], offerings, ) # ---- instructors (4-digit employee codes) ---- # Derived from the offerings above so it can never drift: one row per # unique (instructor, course, section) teaching assignment. instructor_names = { "1001": "Dr. Ahsan Raza", "1002": "Ms. Sana Tariq", "1003": "Mr. Bilal Khan", "1004": "Dr. Farah Iqbal", "1005": "Mr. Usman Ali", "1006": "Ms. Hira Shah", "1007": "Dr. Nadia Farooq", "1008": "Mr. Kamran Sheikh", "1009": "Ms. Ayesha Malik", "1010": "Mr. Imran Qureshi", "1011": "Dr. Saima Aziz", **instructor_names_extra, } instructors = [] for course_id, section_id, batch_codes, instr_code in offerings: instructors.append([instr_code, instructor_names[instr_code], course_id, section_id]) write_csv( "instructor/instructors.csv", ["instructor_code", "instructor_name", "course_id", "section_id"], instructors, ) # ---- students (roll_no = batch_code-seq): 18 per batch (was 5) ---- students = [] for batch_code, department, program, session in all_batches: for seq in range(1, 19): roll_no = f"{batch_code}-{seq:03d}" students.append([roll_no, batch_code, f"Student {roll_no}"]) write_csv( "roll_no/students.csv", ["roll_no", "batch_code", "student_name"], students, ) print("\nDummy data generation complete.") if __name__ == "__main__": main()