From eebc2cd2b698743645af04f27ca6de007837aae2 Mon Sep 17 00:00:00 2001 From: Prachi Sah Date: Wed, 29 Jan 2025 14:21:05 +0530 Subject: [PATCH 01/12] Code updated for the interdepartment teacher --- GA/__init__.py | 9 +- GA/chromosome.py | 57 +- Samples/chromosome.json | 1274 +++++++++++++++++++++++++++++++++++++++ Samples/samples.py | 58 ++ 4 files changed, 1376 insertions(+), 22 deletions(-) create mode 100644 Samples/chromosome.json diff --git a/GA/__init__.py b/GA/__init__.py index dcb9319..0fad3a4 100644 --- a/GA/__init__.py +++ b/GA/__init__.py @@ -12,7 +12,8 @@ SubjectWeeklyQuota, Classrooms, Sections, - + Depatments, + InterDepartment ) @@ -26,11 +27,13 @@ def timetable_generation(): teacher_preferences=TeacherWorkload.teacher_preferences, teacher_weekly_workload=TeacherWorkload.Weekly_workLoad, special_subjects=SpecialSubjects.special_subjects, + labs=SpecialSubjects.Labs, subject_quota_limits=SubjectWeeklyQuota.subject_quota, labs_list=Classrooms.labs, teacher_duty_days=TeacherWorkload.teacher_duty_days, + teacher_availability_matrix=InterDepartment.teacher_availability_matrix, + teacher_department_mapping=Depatments.teacher_department_mapping, ) - timetable = timetable_generator.create_timetable(Defaults.initial_no_of_chromosomes) # Fitness of each Chromosome fitness_calculator = TimetableFitnessEvaluator( @@ -91,5 +94,5 @@ def run_timetable_generation(): for generation in range(Defaults.total_no_of_generations): best_chromosome = timetable_generation() return best_chromosome - +print(run_timetable_generation()) run_timetable_generation() diff --git a/GA/chromosome.py b/GA/chromosome.py index 74c86b4..72a2439 100644 --- a/GA/chromosome.py +++ b/GA/chromosome.py @@ -17,9 +17,12 @@ def __init__( teacher_preferences: dict, teacher_weekly_workload: dict, special_subjects: dict, + labs:dict, subject_quota_limits: dict, labs_list: list, teacher_duty_days: dict, + teacher_availability_matrix=dict, + teacher_department_mapping=dict, ): self.sections_manager = Sections(total_sections) self.classrooms_manager = Classrooms(total_classrooms, total_labs) @@ -29,14 +32,18 @@ def __init__( self.weekdays = Defaults.working_days self.subject_teacher_mapping = teacher_subject_mapping self.subject_quota_limits = subject_quota_limits - self.lab_subject_list = labs_list + self.lab_subject_list=labs self.special_subject_list = special_subjects self.teacher_availability_preferences = teacher_preferences self.available_time_slots = TimeIntervalConstant.time_slots self.teacher_duty_days = teacher_duty_days self.weekly_workload = teacher_weekly_workload - self.teacher_assignment_tracker = {teacher: 0 for teacher in teacher_weekly_workload.keys()} + self.teacher_assignment_tracker = { + teacher: 0 for teacher in teacher_weekly_workload.keys() + } + self.teacher_availability_matrix = teacher_availability_matrix + # print("Teacher Availability Matrix: ", self.teacher_availability_matrix) self._map_sections_to_classrooms() self._teacher_section() @@ -117,19 +124,19 @@ def _generate_section_schedule( ) if selected_subject != "Library": section_subject_usage_tracker[section][selected_subject] += 1 - - # Handle double-slot subjects - if selected_subject in self.lab_subject_list and slot_index + 1 <= total_slots_for_section: + if selected_subject in self.lab_subject_list or selected_subject in self.special_subject_list and slot_index + 1 <= total_slots_for_section: next_time_slot = self.available_time_slots.get(slot_index + 1) section_schedule.append( - { + { "teacher_id": assigned_teacher, "subject_id": selected_subject, "classroom_id": assigned_room, "time_slot": next_time_slot, - } + } ) section_subject_usage_tracker[section][selected_subject] += 1 + slot_index += 1 + return section_schedule def _assign_subject_and_teacher( @@ -141,7 +148,7 @@ def _assign_subject_and_teacher( section_subject_usage_tracker, teacher_workload_tracker, ): - """Assign a subject and teacher for a specific slot.""" + """Assign a subject and teacher for a specific slot, respecting teacher preferences.""" available_subjects = self._get_available_subjects(section, section_subject_usage_tracker) random.shuffle(available_subjects) @@ -157,19 +164,28 @@ def _assign_subject_and_teacher( if subject not in subjects_scheduled_today: teachers_for_subject = self.subject_teacher_mapping[subject] + preferred_teachers = [ + teacher + for teacher in teachers_for_subject + if self.teacher_availability_preferences.get(teacher,[]) + ] teachers_with_least_load = sorted( - teachers_for_subject, key=lambda teacher: teacher_workload_tracker[teacher] + preferred_teachers, key=lambda teacher: teacher_workload_tracker[teacher] ) - assigned_teacher = teachers_with_least_load[0] - teacher_workload_tracker[assigned_teacher] += 1 - selected_subject = subject - subjects_scheduled_today.add(subject) - assigned_room = ( - self.classrooms_manager.labs[slot_index % len(self.classrooms_manager.labs)] - if subject in self.lab_subject_list - else assigned_classroom - ) - break + + for teacher in teachers_with_least_load: + if self.teacher_availability_matrix[teacher][slot_index - 1]: + assigned_teacher = teacher + teacher_workload_tracker[assigned_teacher] += 1 + selected_subject = subject + subjects_scheduled_today.add(subject) + assigned_room = ( + self.classrooms_manager.labs[slot_index % len(self.classrooms_manager.labs)] + if subject in self.lab_subject_list + else assigned_classroom + ) + self.teacher_availability_matrix[assigned_teacher][slot_index - 1] = False + break if not assigned_teacher: selected_subject = "Library" @@ -196,6 +212,9 @@ def generate_daily_schedule(self, half_day_section_list, section_subject_usage_t section, half_day_section_list, section_subject_usage_tracker, teacher_workload_tracker ) daily_schedule[section] = section_schedule + for teacher in self.teacher_availability_matrix.keys(): + for slot_index in range(len(self.available_time_slots)): + self.teacher_availability_matrix[teacher][slot_index] = True return daily_schedule, section_subject_usage_tracker diff --git a/Samples/chromosome.json b/Samples/chromosome.json new file mode 100644 index 0000000..e1ba7c0 --- /dev/null +++ b/Samples/chromosome.json @@ -0,0 +1,1274 @@ +{ + "Monday": { + "A": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + } + ], + "B": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + } + ], + "C": [ + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + } + ], + "D": [ + { + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AD08", + "subject_id": "PMA-502", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" + } + ], + "E": [ + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R5", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R5", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" + } + ], + "F": [ + { + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "R6", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SG19", + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" + } + ] + }, + "Tuesday": { + "A": [ + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + } + ], + "B": [ + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + } + ], + "C": [ + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "NJ13", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + } + ], + "D": [ + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" + } + ], + "E": [ + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" + } + ], + "F": [ + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R6", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R6", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R6", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" + } + ] + }, + "Wednesday": { + "A": [ + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + } + ], + "B": [ + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + } + ], + "C": [ + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + } + ], + "D": [ + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" + } + ], + "E": [ + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "NJ13", + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" + } + ], + "F": [ + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R6", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R6", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" + } + ] + }, + "Thursday": { + "A": [ + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + } + ], + "B": [ + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + } + ], + "C": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + } + ], + "D": [ + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" + } + ], + "E": [ + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" + } + ], + "F": [ + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R6", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" + } + ] + }, + "Friday": { + "A": [ + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + } + ], + "B": [ + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + } + ], + "C": [ + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + } + ], + "D": [ + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" + } + ], + "E": [ + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" + } + ], + "F": [ + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" + } + ] + }, + "Saturday": { + "A": [ + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + } + ], + "B": [ + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + } + ], + "C": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + } + ], + "D": [ + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" + } + ], + "E": [ + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" + } + ], + "F": [ + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" + } + ] + } +} \ No newline at end of file diff --git a/Samples/samples.py b/Samples/samples.py index f4c1b39..42ba68f 100644 --- a/Samples/samples.py +++ b/Samples/samples.py @@ -69,6 +69,64 @@ class PenaltyConstants: PENALTY_UN_PREFERRED_SLOT = 5 PENALTY_OVERLOAD_TEACHER = 10 +class Depatments: + teacher_department_mapping = { + "AB01": ["CSE"], + "PK02": ["CSE", "Management", "SOC"], + "SS03": ["CSE"], + "AA04": ["CSE", "Management"], + "AC05": ["CSE", "SOC"], + "SP06": ["CSE", "Management"], + "DP07": ["CSE", "SOC"], + "AD08": ["CSE", "Management"], + "RD09": ["CSE"], + "BJ10": ["CSE", "Management", "SOC"], + "RS11": ["CSE"], + "JM12": ["CSE", "SOC"], + "NJ13": ["CSE", "Management"], + "PM14": ["CSE"], + "AA15": ["CSE", "SOC"], + "SJ16": ["CSE", "Management"], + "AB17": ["CSE", "SOC"], + "HP18": ["CSE", "Management"], + "SG19": ["CSE", "SOC"], + "DT20": ["CSE", "Management"], + "PA21": ["CSE", "SOC"], + "NB22": ["CSE", "Management"], + "AK23": ["CSE"], + "AP24": ["CSE", "SOC"], + "VD25": ["CSE", "Management"], + "AK26": ["CSE"], +} +class InterDepartment: + teacher_availability_matrix= { + "AB01": [True, True, True, True, True, True, True], + "PK02": [True, True, True, True, True, True, True], + "SS03": [True, True, True, True, True, True, True], + "AA04": [True, True, True, True, True, True, True], + "AC05": [True, True, True, True, True, True, True], + "SP06": [True, True, True, True, True, True, True], + "DP07": [True, True, True, True, True, True, True], + "AD08": [True, True, True, True, True, True, True], + "RD09": [True, True, True, True, True, True, True], + "BJ10": [True, True, True, True, True, True, True], + "RS11": [True, True, True, True, True, True, True], + "JM12": [True, True, True, True, True, True, True], + "NJ13": [True, True, True, True, True, True, True], + "PM14": [True, True, True, True, True, True, True], + "AA15": [True, True, True, True, True, True, True], + "SJ16": [True, True, True, True, True, True, True], + "AB17": [True, True, True, True, True, True, True], + "HP18": [True, True, True, True, True, True, True], + "SG19": [True, True, True, True, True, True, True], + "DT20": [True, True, True, True, True, True, True], + "PA21": [True, True, True, True, True, True, True], + "NB22": [True, True, True, True, True, True, True], + "AK23": [True, True, True, True, True, True, True], + "AP24": [True, True, True, True, True, True, True], + "VD25": [True, True, True, True, True, True, True], + "AK26": [False, False, False, False, True, False, False], + } class TeacherWorkload: Weekly_workLoad = { From 2585f4e0f2a7aaf62e8ff5fde6829dbe6ece523d Mon Sep 17 00:00:00 2001 From: Prachi Sah Date: Thu, 30 Jan 2025 21:28:57 +0530 Subject: [PATCH 02/12] Inter-Department Updated --- GA/__init__.py | 26 ++++--- GA/chromosome.py | 173 +++++++++++++++++++++++++-------------------- Samples/samples.py | 54 +++++++------- 3 files changed, 141 insertions(+), 112 deletions(-) diff --git a/GA/__init__.py b/GA/__init__.py index 0fad3a4..cffaacd 100644 --- a/GA/__init__.py +++ b/GA/__init__.py @@ -1,5 +1,3 @@ -# This is the Orchestrator file, which will govern the flow. - from Constants.constant import Defaults from GA.mutation import TimeTableMutation, TimeTableCrossOver from GA.selection import TimeTableSelection @@ -34,7 +32,8 @@ def timetable_generation(): teacher_availability_matrix=InterDepartment.teacher_availability_matrix, teacher_department_mapping=Depatments.teacher_department_mapping, ) - timetable = timetable_generator.create_timetable(Defaults.initial_no_of_chromosomes) + timetable, teacher_availability_matrix = timetable_generator.create_timetable(Defaults.initial_no_of_chromosomes) + # Fitness of each Chromosome fitness_calculator = TimetableFitnessEvaluator( timetable, @@ -51,7 +50,6 @@ def timetable_generation(): fitness_scores = fitness_calculator.evaluate_timetable_fitness() - # Selection of all Chromosomes selection_object = TimeTableSelection() selected_chromosomes = selection_object.select_chromosomes(fitness_scores[1]) @@ -73,13 +71,13 @@ def timetable_generation(): crossover_chromosomes.append(child1) crossover_chromosomes.append(child2) - # Mutate all crossover Chromosomes mutation_object = TimeTableMutation() mutated_chromosomes = [ mutation_object.mutate_schedule_for_week(chromosome) for chromosome in crossover_chromosomes ] + best_chromosome_score = -1 best_chromosome = dict() @@ -87,12 +85,20 @@ def timetable_generation(): if int(week_score) > best_chromosome_score: best_chromosome_score = int(week_score) best_chromosome = timetable[week_no] - return best_chromosome + + return best_chromosome, teacher_availability_matrix def run_timetable_generation(): + best_chromosome = None + teacher_availability_matrix = None + for generation in range(Defaults.total_no_of_generations): - best_chromosome = timetable_generation() - return best_chromosome -print(run_timetable_generation()) -run_timetable_generation() + best_chromosome, teacher_availability_matrix = timetable_generation() + + return best_chromosome, teacher_availability_matrix + +if __name__ == "__main__": + print("Running Timetable Generation...") + best_chromosome, teacher_availability_matrix = run_timetable_generation() + # print("Timetable Generation Completed.",teacher_availability_matrix) \ No newline at end of file diff --git a/GA/chromosome.py b/GA/chromosome.py index 72a2439..25c74be 100644 --- a/GA/chromosome.py +++ b/GA/chromosome.py @@ -17,11 +17,11 @@ def __init__( teacher_preferences: dict, teacher_weekly_workload: dict, special_subjects: dict, - labs:dict, + labs: dict, subject_quota_limits: dict, labs_list: list, teacher_duty_days: dict, - teacher_availability_matrix=dict, + teacher_availability_matrix=None, teacher_department_mapping=dict, ): self.sections_manager = Sections(total_sections) @@ -32,7 +32,7 @@ def __init__( self.weekdays = Defaults.working_days self.subject_teacher_mapping = teacher_subject_mapping self.subject_quota_limits = subject_quota_limits - self.lab_subject_list=labs + self.lab_subject_list = labs self.special_subject_list = special_subjects self.teacher_availability_preferences = teacher_preferences self.available_time_slots = TimeIntervalConstant.time_slots @@ -42,8 +42,9 @@ def __init__( self.teacher_assignment_tracker = { teacher: 0 for teacher in teacher_weekly_workload.keys() } + + # Initialize teacher availability matrix with proper structure self.teacher_availability_matrix = teacher_availability_matrix - # print("Teacher Availability Matrix: ", self.teacher_availability_matrix) self._map_sections_to_classrooms() self._teacher_section() @@ -67,11 +68,7 @@ def _map_sections_to_classrooms(self): self.section_to_classroom_map[section] = classroom sorted_classrooms.remove(classroom) break - else: - raise ValueError( - f"No classroom can accommodate section {section} with strength {self.room_capacity_manager.section_strength[section]}." - ) - + def _teacher_section(self): """Map sections to teachers and subjects, ensuring balanced assignments.""" self.teacher_section_map = { @@ -92,53 +89,7 @@ def _teacher_section(self): def _initialize_teacher_workload_tracker(self): """Initialize a tracker for teacher workloads.""" return {teacher: 0 for teacher in self.weekly_workload.keys()} - - def _generate_section_schedule( - self, section, half_day_section_list, section_subject_usage_tracker, teacher_workload_tracker - ): - """Generate a schedule for an individual section.""" - section_schedule = [] - subjects_scheduled_today = set() - assigned_classroom = self.section_to_classroom_map[section] - total_slots_for_section = 4 if section in half_day_section_list else 7 - - for slot_index, current_time_slot in enumerate(self.available_time_slots.values(), start=1): - if slot_index > total_slots_for_section: - break - - assigned_teacher, selected_subject, assigned_room = self._assign_subject_and_teacher( - section, - slot_index, - subjects_scheduled_today, - assigned_classroom, - section_subject_usage_tracker, - teacher_workload_tracker, - ) - section_schedule.append( - { - "teacher_id": assigned_teacher, - "subject_id": selected_subject, - "classroom_id": assigned_room, - "time_slot": current_time_slot, - } - ) - if selected_subject != "Library": - section_subject_usage_tracker[section][selected_subject] += 1 - if selected_subject in self.lab_subject_list or selected_subject in self.special_subject_list and slot_index + 1 <= total_slots_for_section: - next_time_slot = self.available_time_slots.get(slot_index + 1) - section_schedule.append( - { - "teacher_id": assigned_teacher, - "subject_id": selected_subject, - "classroom_id": assigned_room, - "time_slot": next_time_slot, - } - ) - section_subject_usage_tracker[section][selected_subject] += 1 - slot_index += 1 - - return section_schedule - + def _assign_subject_and_teacher( self, section, @@ -147,6 +98,9 @@ def _assign_subject_and_teacher( assigned_classroom, section_subject_usage_tracker, teacher_workload_tracker, + teacher_availability_matrix, + index, + total_slots_for_section ): """Assign a subject and teacher for a specific slot, respecting teacher preferences.""" available_subjects = self._get_available_subjects(section, section_subject_usage_tracker) @@ -167,14 +121,18 @@ def _assign_subject_and_teacher( preferred_teachers = [ teacher for teacher in teachers_for_subject - if self.teacher_availability_preferences.get(teacher,[]) + if self.teacher_availability_preferences.get(teacher, []) ] teachers_with_least_load = sorted( preferred_teachers, key=lambda teacher: teacher_workload_tracker[teacher] ) for teacher in teachers_with_least_load: - if self.teacher_availability_matrix[teacher][slot_index - 1]: + # Ensure valid index access + if (teacher in teacher_availability_matrix and + len(teacher_availability_matrix[teacher]) > index and + len(teacher_availability_matrix[teacher][index]) > (slot_index - 1) and + teacher_availability_matrix[teacher][index][slot_index - 1]): assigned_teacher = teacher teacher_workload_tracker[assigned_teacher] += 1 selected_subject = subject @@ -184,7 +142,6 @@ def _assign_subject_and_teacher( if subject in self.lab_subject_list else assigned_classroom ) - self.teacher_availability_matrix[assigned_teacher][slot_index - 1] = False break if not assigned_teacher: @@ -193,6 +150,68 @@ def _assign_subject_and_teacher( assigned_room = assigned_classroom return assigned_teacher, selected_subject, assigned_room + def _generate_section_schedule( + self, + section, + half_day_section_list, + section_subject_usage_tracker, + teacher_workload_tracker, + teacher_availability_matrix, + index + ): + """Generate a schedule for an individual section and update teacher_availability_matrix immediately with index.""" + section_schedule = [] + subjects_scheduled_today = set() + assigned_classroom = self.section_to_classroom_map[section] + total_slots_for_section = 4 if section in half_day_section_list else 7 + + for slot_index, current_time_slot in enumerate(self.available_time_slots.values(), start=1): + if slot_index > total_slots_for_section: + break + + assigned_teacher, selected_subject, assigned_room = self._assign_subject_and_teacher( + section, + slot_index, + subjects_scheduled_today, + assigned_classroom, + section_subject_usage_tracker, + teacher_workload_tracker, + teacher_availability_matrix, + index, + total_slots_for_section + ) + + if assigned_teacher != "None": + teacher_availability_matrix[assigned_teacher][index][slot_index - 1] = False + + section_schedule.append( + { + "teacher_id": assigned_teacher, + "subject_id": selected_subject, + "classroom_id": assigned_room, + "time_slot": current_time_slot, + } + ) + + if selected_subject != "Library": + section_subject_usage_tracker[section][selected_subject] += 1 + + if selected_subject in self.lab_subject_list or selected_subject in self.special_subject_list: + next_time_slot = self.available_time_slots.get(slot_index + 1) + if next_time_slot and slot_index + 1 <= total_slots_for_section: + section_schedule.append( + { + "teacher_id": assigned_teacher, + "subject_id": selected_subject, + "classroom_id": assigned_room, + "time_slot": next_time_slot, + } + ) + section_subject_usage_tracker[section][selected_subject] += 1 + slot_index += 1 + + return section_schedule, teacher_availability_matrix + def _get_available_subjects(self, section, section_subject_usage_tracker): """Get a list of subjects available for a specific section.""" @@ -202,41 +221,45 @@ def _get_available_subjects(self, section, section_subject_usage_tracker): if section_subject_usage_tracker[section][subject] < self.subject_quota_limits.get(subject, 0) ] - def generate_daily_schedule(self, half_day_section_list, section_subject_usage_tracker): - """Generate the daily schedule for all sections.""" + + def generate_daily_schedule(self, half_day_section_list, section_subject_usage_tracker, index): + """Generate the daily schedule for all sections and update teacher_availability_matrix after each section.""" daily_schedule = {} teacher_workload_tracker = self._initialize_teacher_workload_tracker() for section in self.sections_manager.sections: - section_schedule = self._generate_section_schedule( - section, half_day_section_list, section_subject_usage_tracker, teacher_workload_tracker + section_schedule, self.teacher_availability_matrix = self._generate_section_schedule( + section, half_day_section_list, section_subject_usage_tracker, + teacher_workload_tracker, self.teacher_availability_matrix, index ) daily_schedule[section] = section_schedule - for teacher in self.teacher_availability_matrix.keys(): - for slot_index in range(len(self.available_time_slots)): - self.teacher_availability_matrix[teacher][slot_index] = True - return daily_schedule, section_subject_usage_tracker + return daily_schedule, section_subject_usage_tracker, self.teacher_availability_matrix + def _generate_weekly_schedule(self): - """Generate a schedule for a single week.""" + """Generate a schedule for a single week and update teacher_availability_matrix daily.""" week_schedule = {} section_subject_usage_tracker = { section: {subject: 0 for subject in self.subject_teacher_mapping.keys()} for section in self.sections_manager.sections } - for week_day in self.weekdays: + + for index, week_day in enumerate(self.weekdays): # Pass index for each day half_day_sections = self.sections_manager.sections[: len(self.sections_manager.sections) // 2] - daily_schedule, section_subject_usage_tracker = self.generate_daily_schedule( - half_day_sections, section_subject_usage_tracker + daily_schedule, section_subject_usage_tracker, self.teacher_availability_matrix = self.generate_daily_schedule( + half_day_sections, section_subject_usage_tracker, index # Pass index ) week_schedule[week_day] = daily_schedule - return week_schedule, section_subject_usage_tracker + + return week_schedule, section_subject_usage_tracker, self.teacher_availability_matrix + def create_timetable(self, num_weeks): - """Create the timetable for the given number of weeks.""" + """Create the timetable for the given number of weeks and track teacher_availability_matrix.""" timetable = {} + for week in range(1, num_weeks + 1): - week_schedule, section_subject_usage_tracker = self._generate_weekly_schedule() + week_schedule, section_subject_usage_tracker, self.teacher_availability_matrix = self._generate_weekly_schedule() timetable[f"Week {week}"] = week_schedule - return timetable + return timetable, self.teacher_availability_matrix # Return final updated matrix diff --git a/Samples/samples.py b/Samples/samples.py index 42ba68f..e996b16 100644 --- a/Samples/samples.py +++ b/Samples/samples.py @@ -99,33 +99,33 @@ class Depatments: "AK26": ["CSE"], } class InterDepartment: - teacher_availability_matrix= { - "AB01": [True, True, True, True, True, True, True], - "PK02": [True, True, True, True, True, True, True], - "SS03": [True, True, True, True, True, True, True], - "AA04": [True, True, True, True, True, True, True], - "AC05": [True, True, True, True, True, True, True], - "SP06": [True, True, True, True, True, True, True], - "DP07": [True, True, True, True, True, True, True], - "AD08": [True, True, True, True, True, True, True], - "RD09": [True, True, True, True, True, True, True], - "BJ10": [True, True, True, True, True, True, True], - "RS11": [True, True, True, True, True, True, True], - "JM12": [True, True, True, True, True, True, True], - "NJ13": [True, True, True, True, True, True, True], - "PM14": [True, True, True, True, True, True, True], - "AA15": [True, True, True, True, True, True, True], - "SJ16": [True, True, True, True, True, True, True], - "AB17": [True, True, True, True, True, True, True], - "HP18": [True, True, True, True, True, True, True], - "SG19": [True, True, True, True, True, True, True], - "DT20": [True, True, True, True, True, True, True], - "PA21": [True, True, True, True, True, True, True], - "NB22": [True, True, True, True, True, True, True], - "AK23": [True, True, True, True, True, True, True], - "AP24": [True, True, True, True, True, True, True], - "VD25": [True, True, True, True, True, True, True], - "AK26": [False, False, False, False, True, False, False], + teacher_availability_matrix = { + "AB01": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "PK02": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "SS03": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "AA04": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "AC05": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "SP06": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "DP07": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "AD08": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "RD09": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "BJ10": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "RS11": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "JM12": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "NJ13": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "PM14": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "AA15": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "SJ16": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "AB17": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "HP18": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "SG19": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "DT20": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "PA21": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "NB22": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "AK23": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "AP24": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "VD25": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], + "AK26": [[True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True], [True, True, True, True, True, True, True]], } class TeacherWorkload: From 8d986d99eae1519031ea20f89422bf0c897edae0 Mon Sep 17 00:00:00 2001 From: Prachi Sah Date: Fri, 31 Jan 2025 22:25:03 +0530 Subject: [PATCH 03/12] Inter-Department updated for time being --- GA/__init__.py | 2 +- GA/chromosome.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/GA/__init__.py b/GA/__init__.py index cffaacd..80d7c04 100644 --- a/GA/__init__.py +++ b/GA/__init__.py @@ -101,4 +101,4 @@ def run_timetable_generation(): if __name__ == "__main__": print("Running Timetable Generation...") best_chromosome, teacher_availability_matrix = run_timetable_generation() - # print("Timetable Generation Completed.",teacher_availability_matrix) \ No newline at end of file + print("Timetable Generation Completed.",best_chromosome) \ No newline at end of file diff --git a/GA/chromosome.py b/GA/chromosome.py index 25c74be..c1dafb6 100644 --- a/GA/chromosome.py +++ b/GA/chromosome.py @@ -181,8 +181,8 @@ def _generate_section_schedule( total_slots_for_section ) - if assigned_teacher != "None": - teacher_availability_matrix[assigned_teacher][index][slot_index - 1] = False + # if assigned_teacher != "None": + # teacher_availability_matrix[assigned_teacher][index][slot_index - 1] = False section_schedule.append( { @@ -192,7 +192,6 @@ def _generate_section_schedule( "time_slot": current_time_slot, } ) - if selected_subject != "Library": section_subject_usage_tracker[section][selected_subject] += 1 From deeb8872c812dcd85e65b7ec1a894c119bbe46e9 Mon Sep 17 00:00:00 2001 From: Prachi Sah Date: Sat, 1 Feb 2025 22:28:18 +0530 Subject: [PATCH 04/12] Teacher assigned --- GA/__init__.py | 2 +- GA/chromosome.py | 29 +++++++++++------------------ 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/GA/__init__.py b/GA/__init__.py index 80d7c04..33aa32a 100644 --- a/GA/__init__.py +++ b/GA/__init__.py @@ -101,4 +101,4 @@ def run_timetable_generation(): if __name__ == "__main__": print("Running Timetable Generation...") best_chromosome, teacher_availability_matrix = run_timetable_generation() - print("Timetable Generation Completed.",best_chromosome) \ No newline at end of file + # print("Timetable Generation Completed.",best_chromosome) \ No newline at end of file diff --git a/GA/chromosome.py b/GA/chromosome.py index c1dafb6..a44954a 100644 --- a/GA/chromosome.py +++ b/GA/chromosome.py @@ -89,7 +89,15 @@ def _teacher_section(self): def _initialize_teacher_workload_tracker(self): """Initialize a tracker for teacher workloads.""" return {teacher: 0 for teacher in self.weekly_workload.keys()} - + + def _get_available_subjects(self, section, section_subject_usage_tracker): + """Get a list of subjects available for a specific section.""" + return [ + subject + for subject in self.subject_teacher_mapping.keys() + if section_subject_usage_tracker[section][subject] < self.subject_quota_limits.get(subject, 0) + ] + def _assign_subject_and_teacher( self, section, @@ -126,7 +134,7 @@ def _assign_subject_and_teacher( teachers_with_least_load = sorted( preferred_teachers, key=lambda teacher: teacher_workload_tracker[teacher] ) - + # print(preferred_teachers) for teacher in teachers_with_least_load: # Ensure valid index access if (teacher in teacher_availability_matrix and @@ -144,12 +152,9 @@ def _assign_subject_and_teacher( ) break - if not assigned_teacher: - selected_subject = "Library" - assigned_teacher = "None" - assigned_room = assigned_classroom return assigned_teacher, selected_subject, assigned_room + def _generate_section_schedule( self, section, @@ -180,7 +185,6 @@ def _generate_section_schedule( index, total_slots_for_section ) - # if assigned_teacher != "None": # teacher_availability_matrix[assigned_teacher][index][slot_index - 1] = False @@ -208,19 +212,9 @@ def _generate_section_schedule( ) section_subject_usage_tracker[section][selected_subject] += 1 slot_index += 1 - return section_schedule, teacher_availability_matrix - def _get_available_subjects(self, section, section_subject_usage_tracker): - """Get a list of subjects available for a specific section.""" - return [ - subject - for subject in self.subject_teacher_mapping.keys() - if section_subject_usage_tracker[section][subject] < self.subject_quota_limits.get(subject, 0) - ] - - def generate_daily_schedule(self, half_day_section_list, section_subject_usage_tracker, index): """Generate the daily schedule for all sections and update teacher_availability_matrix after each section.""" daily_schedule = {} @@ -232,7 +226,6 @@ def generate_daily_schedule(self, half_day_section_list, section_subject_usage_t teacher_workload_tracker, self.teacher_availability_matrix, index ) daily_schedule[section] = section_schedule - return daily_schedule, section_subject_usage_tracker, self.teacher_availability_matrix From 6f810998820306a8289058b5417ffe962f5447f8 Mon Sep 17 00:00:00 2001 From: Prachi Sah Date: Sat, 1 Feb 2025 22:30:35 +0530 Subject: [PATCH 05/12] Teacher assigned --- GA/chromosome.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/GA/chromosome.py b/GA/chromosome.py index a44954a..5be46be 100644 --- a/GA/chromosome.py +++ b/GA/chromosome.py @@ -151,7 +151,11 @@ def _assign_subject_and_teacher( else assigned_classroom ) break - + + if not assigned_teacher: + selected_subject = "Library" + assigned_teacher = "None" + assigned_room = assigned_classroom return assigned_teacher, selected_subject, assigned_room @@ -226,6 +230,7 @@ def generate_daily_schedule(self, half_day_section_list, section_subject_usage_t teacher_workload_tracker, self.teacher_availability_matrix, index ) daily_schedule[section] = section_schedule + # print(f"Daily Schedule for {section}: {section_schedule}") return daily_schedule, section_subject_usage_tracker, self.teacher_availability_matrix From 3365da4971618c06de72a0db92d78d48dbffb546 Mon Sep 17 00:00:00 2001 From: KaranChauhan777 Date: Wed, 20 Nov 2024 17:17:34 +0530 Subject: [PATCH 06/12] Solved Time constants problem --- Samples/Fitness_Files/LabConstraints.py | 167 ++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 Samples/Fitness_Files/LabConstraints.py diff --git a/Samples/Fitness_Files/LabConstraints.py b/Samples/Fitness_Files/LabConstraints.py new file mode 100644 index 0000000..32896c1 --- /dev/null +++ b/Samples/Fitness_Files/LabConstraints.py @@ -0,0 +1,167 @@ +import random +import json +from constants.time_intervals import TimeIntervalConstant + +# Constants +LABS = ["Lab1", "Lab2", "Lab3", "Lab4"] +CLASSES = ["Class1", "Class2", "Class3", "Class4"] +TEACHERS = ["Teacher1", "Teacher2", "Teacher3", "Teacher4"] + +# Constraints +MAX_LAB_COUNT = 2 # Max labs per week for a teacher +MAX_CLASS_COUNT = 3 # Max classes per week for a teacher +MAX_TEACHER_WORKLOAD = 5 # Max hours a teacher can work in a week +MAX_CONTINUOUS_CLASSES = 2 # Max consecutive hours for a teacher + +# Workload for subjects +SUBJECT_WORKLOAD = { + "TCS-531": 3, + "TCS-502": 3, + "TCS-503": 3, + "PCS-506": 1, + "PCS-503": 1, + "TMA-502": 3, + "PMA-502": 1, + "TCS-509": 3, + "XCS-501": 2, + "CSP-501": 1, + "SCS-501": 1, + "Placement_Class": 1 +} + +# Teacher availability and subjects they teach +TEACHER_SUBJECTS = { + "TCS-531": ["AB01", "PK02"], + "TCS-502": ["SS03", "AA04", "AC05"], + "TCS-503": ["SP06", "DP07", "AC05"], + "PCS-506": ["AD08", "RD09"], + "TMA-502": ["BJ10", "RS11", "JM12", "NJ13"], + "PMA-502": ["PM14", "AD08", "AA15"], + "TCS-509": ["SJ16", "AB17", "HP18", "SG19"], + "XCS-501": ["DT20", "PA21", "NB22"], + "CSP-501": ["AK23"], + "SCS-501": ["AP24"], + "PCS-503": ["RS11", "DP07", "SP06", "VD25"], + "Placement_Class": ["AK26"] +} + +# Initialize available time slots +TimeSlots = TimeIntervalConstant.time_slots # Assuming TimeIntervalConstant has a list of time intervals + + +class TimetableFitness: + def __init__(self): + self.teachers = TEACHERS + self.labs = LABS + self.classes = CLASSES + self.subject_workload = SUBJECT_WORKLOAD + self.teacher_subjects = TEACHER_SUBJECTS + self.teacher_schedule = {teacher: {} for teacher in self.teachers} + self.section_strength = {cls: random.randint(40, 60) for cls in self.classes} + + def assign_classes_and_labs(self): + """ + Assign classes and labs to teachers based on constraints and time slots. + """ + for teacher in self.teachers: + subjects = [subject for subject, teachers in TEACHER_SUBJECTS.items() if teacher in teachers] + for subject in subjects: + time_slot = random.choice(TimeSlots) # Randomly select an available time slot + if time_slot not in self.teacher_schedule[teacher]: + self.teacher_schedule[teacher][time_slot] = subject + + def LabNo(self): + """ + Check if the number of labs assigned is within the constraints. + """ + for teacher, schedule in self.teacher_schedule.items(): + lab_count = sum(1 for subject in schedule.values() if subject in self.labs) + if lab_count > MAX_LAB_COUNT: + return False + return True + + def ClassNo(self): + """ + Check if the number of classes assigned is within the constraints. + """ + for teacher, schedule in self.teacher_schedule.items(): + class_count = sum(1 for subject in schedule.values() if subject in self.classes) + if class_count > MAX_CLASS_COUNT: + return False + return True + + def TeacherWorkload(self): + """ + Ensure total workload per teacher is within the limit. + """ + for teacher, schedule in self.teacher_schedule.items(): + total_hours = sum(SUBJECT_WORKLOAD.get(subject, 0) for subject in schedule.values()) + if total_hours > MAX_TEACHER_WORKLOAD: + return False + return True + + def ContinuousClasses(self): + """ + Ensure teachers do not have more than the allowed continuous classes. + """ + for teacher, schedule in self.teacher_schedule.items(): + sorted_slots = sorted(schedule.keys()) + continuous_count = 0 + for i in range(len(sorted_slots) - 1): + if TimeSlots.index(sorted_slots[i + 1]) - TimeSlots.index(sorted_slots[i]) == 1: + continuous_count += 1 + if continuous_count > MAX_CONTINUOUS_CLASSES: + return False + else: + continuous_count = 0 + return True + + def fitnessFunc(self): + """ + Calculate fitness score based on all constraints. + """ + fitness_score = 0 + + if self.LabNo(): + fitness_score += 10 + else: + fitness_score -= 10 + + if self.ClassNo(): + fitness_score += 10 + else: + fitness_score -= 10 + + if self.TeacherWorkload(): + fitness_score += 10 + else: + fitness_score -= 10 + + if self.ContinuousClasses(): + fitness_score += 10 + else: + fitness_score -= 10 + + return fitness_score + + +# Main Execution +timetable_fitness = TimetableFitness() + +# Assign classes and labs to teachers +timetable_fitness.assign_classes_and_labs() + +# Calculate fitness score +fitness_score = timetable_fitness.fitnessFunc() + +# Output +print(f"Final Fitness Score: {fitness_score}") + +# Save results to a JSON file +output = { + "teacher_schedule": timetable_fitness.teacher_schedule, + "fitness_score": fitness_score, + "section_strength": timetable_fitness.section_strength +} +with open("Timetable_Fitness_Result.json", "w") as f: + json.dump(output, f, indent=4) From 69276faeddcdfab1b312f1235221fc9345f2c56f Mon Sep 17 00:00:00 2001 From: KaranChauhan777 Date: Thu, 28 Nov 2024 16:40:39 +0530 Subject: [PATCH 07/12] Tested function in each file by checking test case is running or not. --- test_suite/Crossover_Test.py | 629 ++++++++++++++++++++++++++++++ test_suite/Fitness_Test.py | 692 ++++++++++++++++++++++++++++++++++ test_suite/Mutation_Test.py | 366 ++++++++++++++++++ test_suite/ReadMe.md | 7 + test_suite/Selection_Tests.py | 298 +++++++++++++++ test_suite/unit_test.py | 0 6 files changed, 1992 insertions(+) create mode 100644 test_suite/Crossover_Test.py create mode 100644 test_suite/Fitness_Test.py create mode 100644 test_suite/Mutation_Test.py create mode 100644 test_suite/ReadMe.md create mode 100644 test_suite/Selection_Tests.py create mode 100644 test_suite/unit_test.py diff --git a/test_suite/Crossover_Test.py b/test_suite/Crossover_Test.py new file mode 100644 index 0000000..bce6513 --- /dev/null +++ b/test_suite/Crossover_Test.py @@ -0,0 +1,629 @@ +#CROSSOVER + +#1. def create_chromosome(self): + +import random +import unittest + +# TimetableScheduler Class +class TimetableScheduler: + def __init__(self): + self.sections = ["A", "B", "C", "D"] + self.time_slots = [1, 2, 3, 4, 5, 6, 7] # Time slots per day + self.days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] + self.subject_teacher_map = { + "TCS-531": ["AB01", "PK02"], + "TCS-502": ["SS03", "AA04", "AC05"], + "TCS-503": ["SP06", "DP07", "AC05"], + "PCS-506": ["AD08", "RD09"], + "TMA-502": ["BJ10", "RS11", "JM12", "NJ13"], + "PMA-502": ["PM14", "AD08", "AA15"], + "TCS-509": ["SJ16", "AB17", "HP18", "SG19"], + "XCS-501": ["DT20", "PA21", "NB22"], + "CSP-501": ["AK23"], + "SCS-501": ["AP24"] + } + self.classrooms = ["R1", "R2", "R3", "R4", "R5"] + self.room_capacity = {"R1": 200, "R2": 230, "R3": 240, "R4": 250, "R5": 250} + self.section_strength = {"A": 200, "B": 200, "C": 200, "D": 100} + + def create_chromosome(self): + schedule = {} + teacher_schedule = {slot: set() for slot in self.time_slots} + room_schedule = {slot: {} for slot in self.time_slots} + + for day in self.days: + schedule[day] = {} + for section in self.sections: + schedule[day][section] = [] + used_time_slots = set() + + while len(schedule[day][section]) < 7: + subject = random.choice(list(self.subject_teacher_map.keys())) + teacher = random.choice(self.subject_teacher_map[subject]) + classroom = random.choice(self.classrooms) + + # Find an available time slot + available_slots = [ + slot for slot in self.time_slots if slot not in used_time_slots + ] + if not available_slots: + break + + time_slot = random.choice(available_slots) + + # Check for conflicts + if (teacher not in teacher_schedule[time_slot] and + section not in room_schedule[time_slot].get(classroom, [])): + + entry = { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": classroom, + "time_slot": time_slot + } + schedule[day][section].append(entry) + used_time_slots.add(time_slot) + + # Update schedules to avoid future conflicts + teacher_schedule[time_slot].add(teacher) + if classroom not in room_schedule[time_slot]: + room_schedule[time_slot][classroom] = [] + room_schedule[time_slot][classroom].append(section) + + # Ensure the section has all 7 slots for the day + if len(schedule[day][section]) < 7: + raise RuntimeError( + f"Failed to generate a complete schedule for section {section} on {day}." + ) + + return schedule + + +# Unit Test Class for TimetableScheduler +class TestTimetableScheduler(unittest.TestCase): + def setUp(self): + self.scheduler = TimetableScheduler() + + def test_schedule_for_all_days_and_sections(self): + schedule = self.scheduler.create_chromosome() + + # Check every day has all sections with 7 time slots + for day in self.scheduler.days: + self.assertIn(day, schedule) + for section in self.scheduler.sections: + self.assertIn(section, schedule[day]) + self.assertEqual(len(schedule[day][section]), 7) + + def test_conflict_free_teacher_allocation(self): + schedule = self.scheduler.create_chromosome() + + # Check for teacher conflicts + for day in self.scheduler.days: + teacher_schedule = {slot: set() for slot in self.scheduler.time_slots} + for section in self.scheduler.sections: + for entry in schedule[day][section]: + teacher = entry["teacher_id"] + time_slot = entry["time_slot"] + self.assertNotIn(teacher, teacher_schedule[time_slot]) + teacher_schedule[time_slot].add(teacher) + + def test_room_capacity(self): + schedule = self.scheduler.create_chromosome() + + # Ensure room capacity meets section strength + for day in self.scheduler.days: + for section in self.scheduler.sections: + for entry in schedule[day][section]: + classroom = entry["classroom_id"] + self.assertGreaterEqual( + self.scheduler.room_capacity[classroom], + self.scheduler.section_strength[section] + ) + + def test_time_slots_coverage(self): + schedule = self.scheduler.create_chromosome() + + # Check if all time slots are covered per section per day + for day in self.scheduler.days: + for section in self.scheduler.sections: + time_slots = [entry["time_slot"] for entry in schedule[day][section]] + self.assertEqual(len(set(time_slots)), 7) # Ensure exactly 7 slots + + +if __name__ == "__main__": + unittest.main() + +#2. def create_multiple_chromosomes(self, num_chromosomes): + +import random +import unittest +import time + +class TimetableScheduler: + def __init__(self, subjects, rooms, days, sections, max_attempts=10): + self.subjects = subjects # List of subject codes + self.rooms = rooms # List of available rooms + self.days = days # List of days in the week + self.sections = sections # List of sections + self.max_attempts = max_attempts # Max attempts to find a valid slot + + self.schedule = {} # Schedule dictionary to store the timetable + + def generate_random_schedule(self): + """ Generate a random schedule for each subject and section. """ + timetable = {} + for subject in self.subjects: + for section in self.sections: + attempts = 0 + while attempts < self.max_attempts: + # Randomly select a day, room, and time slot + day = random.choice(self.days) + room = random.choice(self.rooms) + time_slot = f"{day}_{room}" + + # Check if the time_slot is available (no conflicts) + if self.is_slot_available(subject, section, time_slot): + timetable[subject] = (section, day, room, time_slot) + break + else: + attempts += 1 + if attempts == self.max_attempts: + raise RuntimeError( + f"Failed to assign a valid slot for {subject} on {day} for {section} after {attempts} attempts." + ) + return timetable + + def is_slot_available(self, subject, section, time_slot): + """ Check if a slot is available by ensuring no conflicts. """ + # Check if any other subject has been scheduled in the same time slot + for key, value in self.schedule.items(): + if value[3] == time_slot: # Slot conflict + return False + return True + + def create_chromosome(self): + """ Create a chromosome that represents a valid timetable. """ + attempts = 0 + while attempts < self.max_attempts: + try: + timetable = self.generate_random_schedule() + self.schedule = timetable # Assign the generated schedule to the global timetable + return timetable + except RuntimeError as e: + print(f"Error: {e}") + attempts += 1 + if attempts == self.max_attempts: + raise RuntimeError("Failed to generate a valid timetable after multiple attempts.") + # Reset and retry + self.schedule = {} + + return None + + def create_multiple_chromosomes(self, num_chromosomes): + """ Create multiple chromosomes (timetables). """ + chromosomes = [] + for _ in range(num_chromosomes): + chromosome = self.create_chromosome() + if chromosome: + chromosomes.append(chromosome) + else: + print("Failed to create a valid chromosome.") + return chromosomes + + +class TestTimetableScheduler(unittest.TestCase): + + def setUp(self): + """ Setup the test case environment. """ + self.subjects = ['PCS-506', 'XCS-501', 'TCS-502', 'CSP-501', 'TCS-531'] + self.rooms = ['Room 101', 'Room 102', 'Room 103'] + self.days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] + self.sections = ['A', 'B', 'C'] + + self.scheduler = TimetableScheduler(self.subjects, self.rooms, self.days, self.sections) + + def test_chromosome_structure(self): + """ Test the structure of each chromosome. """ + num_chromosomes = 3 + start_time = time.time() + chromosomes = self.scheduler.create_multiple_chromosomes(num_chromosomes) + end_time = time.time() + + self.assertEqual(len(chromosomes), num_chromosomes) + print(f"Ran {num_chromosomes} tests in {end_time - start_time:.4f} seconds") + + for idx, chromosome in enumerate(chromosomes): + print(f"Chromosome {idx+1}:") + for subject, (section, day, room, time_slot) in chromosome.items(): + print(f"{subject} - {section} - {day} - {room} - {time_slot}") + print() + + def test_conflict_free_chromosomes(self): + """ Test that chromosomes are conflict-free. """ + num_chromosomes = 3 + start_time = time.time() + chromosomes = self.scheduler.create_multiple_chromosomes(num_chromosomes) + end_time = time.time() + + self.assertEqual(len(chromosomes), num_chromosomes) + print(f"Ran {num_chromosomes} tests in {end_time - start_time:.4f} seconds") + + for idx, chromosome in enumerate(chromosomes): + for subject, (section, day, room, time_slot) in chromosome.items(): + # Check for conflicts (same time slot for different subjects) + for other_subject, other_values in chromosome.items(): + if subject != other_subject and time_slot == other_values[3]: + self.fail(f"Conflict detected: {subject} and {other_subject} in the same time slot: {time_slot}") + + def test_create_multiple_chromosomes_count(self): + """ Test if the correct number of chromosomes is generated. """ + num_chromosomes = 5 + start_time = time.time() + chromosomes = self.scheduler.create_multiple_chromosomes(num_chromosomes) + end_time = time.time() + + self.assertEqual(len(chromosomes), num_chromosomes) + print(f"Ran {num_chromosomes} tests in {end_time - start_time:.4f} seconds") + + def test_room_capacity_in_multiple_chromosomes(self): + """ Test room capacity constraint across multiple chromosomes. """ + num_chromosomes = 3 + start_time = time.time() + chromosomes = self.scheduler.create_multiple_chromosomes(num_chromosomes) + end_time = time.time() + + self.assertEqual(len(chromosomes), num_chromosomes) + print(f"Ran {num_chromosomes} tests in {end_time - start_time:.4f} seconds") + + # Test room capacity constraints for each timetable (e.g., number of rooms used) + for idx, chromosome in enumerate(chromosomes): + room_usage = {} + for subject, (section, day, room, time_slot) in chromosome.items(): + room_usage[room] = room_usage.get(room, 0) + 1 + print(f"Chromosome {idx+1} Room Usage: {room_usage}") + + +# Run the tests +if __name__ == "__main__": + unittest.main() + +#3. def crossover(self, parent1, parent2): + +import random +import unittest + +class TimetableScheduler: + def __init__(self, subjects, rooms, days, sections): + self.subjects = subjects # List of subject codes + self.rooms = rooms # List of available rooms + self.days = days # List of days in the week + self.sections = sections # List of sections + + def crossover(self, parent1, parent2): + """Perform single-point crossover on two parent chromosomes.""" + offspring1 = {} + offspring2 = {} + + days = self.days + crossover_point = random.randint(1, len(days) - 1) + + for i, day in enumerate(days): + if i < crossover_point: + offspring1[day] = parent1[day] + offspring2[day] = parent2[day] + else: + offspring1[day] = parent2[day] + offspring2[day] = parent1[day] + + return offspring1, offspring2 + +# Unit test for the crossover function +class TestCrossoverMethod(unittest.TestCase): + + def setUp(self): + # Example data to simulate chromosomes for testing + self.subjects = ['PCS-506', 'XCS-501', 'TCS-502', 'CSP-501', 'TCS-531'] + self.rooms = ['Room 101', 'Room 102', 'Room 103'] + self.days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] + self.sections = ['A', 'B', 'C'] + + # Example chromosomes (parents) + self.parent1 = { + 'Monday': 'PCS-506', + 'Tuesday': 'XCS-501', + 'Wednesday': 'TCS-502', + 'Thursday': 'CSP-501', + 'Friday': 'TCS-531' + } + self.parent2 = { + 'Monday': 'TCS-502', + 'Tuesday': 'PCS-506', + 'Wednesday': 'CSP-501', + 'Thursday': 'TCS-531', + 'Friday': 'XCS-501' + } + + # Create an instance of the TimetableScheduler + self.scheduler = TimetableScheduler(self.subjects, self.rooms, self.days, self.sections) + + def test_crossover_correct_number_of_offspring(self): + """Test if crossover produces exactly two offspring.""" + offspring1, offspring2 = self.scheduler.crossover(self.parent1, self.parent2) + self.assertEqual(len(offspring1), len(self.days)) + self.assertEqual(len(offspring2), len(self.days)) + + def test_crossover_correct_assignment(self): + """Test if crossover correctly assigns the parts from parents.""" + offspring1, offspring2 = self.scheduler.crossover(self.parent1, self.parent2) + + # Get the crossover point from the offspring's first day in the timetable + crossover_point = next(i for i, day in enumerate(self.days) if offspring1[self.days[i]] != self.parent1[self.days[i]]) + + # Check the assignment from parent1 and parent2 in the offspring + for i in range(crossover_point): + self.assertEqual(offspring1[self.days[i]], self.parent1[self.days[i]]) + self.assertEqual(offspring2[self.days[i]], self.parent2[self.days[i]]) + + for i in range(crossover_point, len(self.days)): + self.assertEqual(offspring1[self.days[i]], self.parent2[self.days[i]]) + self.assertEqual(offspring2[self.days[i]], self.parent1[self.days[i]]) + + def test_crossover_randomness(self): + """Test if the crossover point is truly random by checking different results.""" + offspring1_a, offspring2_a = self.scheduler.crossover(self.parent1, self.parent2) + offspring1_b, offspring2_b = self.scheduler.crossover(self.parent1, self.parent2) + + # Check that the offspring are not the same (i.e., crossover point was different) + self.assertNotEqual(offspring1_a, offspring1_b) + self.assertNotEqual(offspring2_a, offspring2_b) + + def test_crossover_empty_parent(self): + """Test if crossover handles empty parents gracefully.""" + empty_parent = {} + with self.assertRaises(KeyError): + self.scheduler.crossover(empty_parent, self.parent2) + + with self.assertRaises(KeyError): + self.scheduler.crossover(self.parent1, empty_parent) + + +if __name__ == '__main__': + unittest.main() + +#4. def mutate_timetable(self, chromosome, mutation_rate=0.7): + +import random +import unittest + +class TimetableScheduler: + def __init__(self, classrooms, sections, time_slots): + self.classrooms = classrooms # List of classrooms available for assignment + self.sections = sections # List of sections + self.time_slots = time_slots # List of time slots available for scheduling + + def mutate_timetable(self, chromosome, mutation_rate=0.7): + """Applies mutation to a timetable chromosome.""" + classrooms = self.classrooms + sections = self.sections + mutated_chromosome = chromosome.copy() + + for day, section_schedule in mutated_chromosome.items(): + for section, entries in section_schedule.items(): + for entry in entries: + if random.random() < mutation_rate: + entry["time_slot"] = random.choice(self.time_slots) + entry["classroom_id"] = random.choice(classrooms) + entry["section"] = random.choice(sections) + + return mutated_chromosome + + +class TestMutateTimetable(unittest.TestCase): + + def setUp(self): + # Example data to simulate a timetable chromosome + self.classrooms = ['Room 101', 'Room 102', 'Room 103'] + self.sections = ['A', 'B', 'C'] + self.time_slots = ['9:00 AM', '10:00 AM', '11:00 AM', '1:00 PM', '2:00 PM'] + + # Example timetable (chromosome) + self.chromosome = { + 'Monday': { + 'A': [{'time_slot': '9:00 AM', 'classroom_id': 'Room 101', 'section': 'A'}], + 'B': [{'time_slot': '10:00 AM', 'classroom_id': 'Room 102', 'section': 'B'}], + 'C': [{'time_slot': '11:00 AM', 'classroom_id': 'Room 103', 'section': 'C'}], + }, + 'Tuesday': { + 'A': [{'time_slot': '9:00 AM', 'classroom_id': 'Room 101', 'section': 'A'}], + 'B': [{'time_slot': '10:00 AM', 'classroom_id': 'Room 102', 'section': 'B'}], + 'C': [{'time_slot': '11:00 AM', 'classroom_id': 'Room 103', 'section': 'C'}], + } + } + + # Create an instance of the TimetableScheduler + self.scheduler = TimetableScheduler(self.classrooms, self.sections, self.time_slots) + + def test_mutation_occurrence(self): + """Test if the mutation occurs with the expected frequency.""" + mutation_rate = 0.7 # Mutation rate is set to 70% + + # Run multiple mutations to get more stable results + num_runs = 10 # You can try more runs for higher accuracy + mutation_count = 0 + total_entries = sum(len(entries) for day in self.chromosome.values() for section, entries in day.items()) + + for _ in range(num_runs): + mutated_chromosome = self.scheduler.mutate_timetable(self.chromosome, mutation_rate) + + # Count how many entries were mutated + mutated_count = 0 + for day, section_schedule in mutated_chromosome.items(): + for section, entries in section_schedule.items(): + for entry in entries: + # Check if the mutation applied (i.e., values changed) + if entry["time_slot"] != '9:00 AM' or entry["classroom_id"] != 'Room 101' or entry["section"] != 'A': + mutated_count += 1 + + mutation_percentage = mutated_count / total_entries + mutation_count += mutation_percentage # Accumulate mutation percentages + + # Calculate average mutation rate across runs + average_mutation_percentage = mutation_count / num_runs + print(f"Average Mutation Rate: {average_mutation_percentage}") # To check the mutation rate in the output + + # Assert that mutation occurs in the expected range + self.assertGreaterEqual(average_mutation_percentage, 0.5) # At least 50% mutation + self.assertLessEqual(average_mutation_percentage, 1.0) # No more than 100% mutation + + def test_mutate_timetable(self): + """Test if mutation correctly applies changes to the timetable.""" + mutated_chromosome = self.scheduler.mutate_timetable(self.chromosome, mutation_rate=1.0) + + # Check if mutation has applied changes + for day, section_schedule in mutated_chromosome.items(): + for section, entries in section_schedule.items(): + for entry in entries: + # Check if time_slot, classroom_id, and section have been mutated + self.assertIn(entry["time_slot"], self.time_slots) + self.assertIn(entry["classroom_id"], self.classrooms) + self.assertIn(entry["section"], self.sections) + + def test_mutation_validity(self): + """Test if the mutated timetable remains valid.""" + mutated_chromosome = self.scheduler.mutate_timetable(self.chromosome, mutation_rate=1.0) + + # Validate that the mutation results in valid values (time_slot, classroom_id, section) + for day, section_schedule in mutated_chromosome.items(): + for section, entries in section_schedule.items(): + for entry in entries: + # Ensure the mutated values are within the expected lists + self.assertIn(entry["time_slot"], self.time_slots) + self.assertIn(entry["classroom_id"], self.classrooms) + self.assertIn(entry["section"], self.sections) + + def test_mutate_empty_chromosome(self): + """Test mutation with an empty chromosome.""" + empty_chromosome = {} + mutated_chromosome = self.scheduler.mutate_timetable(empty_chromosome, mutation_rate=1.0) + self.assertEqual(mutated_chromosome, empty_chromosome) # Should remain empty + + +if __name__ == '__main__': + unittest.main() + +#5.def print_chromosomes(self, chromosomes): + +import unittest +from unittest.mock import patch +from io import StringIO + +class TimetableScheduler: + def __init__(self, classrooms, sections, teachers, subjects, time_slots): + self.classrooms = classrooms # List of classrooms available for assignment + self.sections = sections # List of sections + self.teachers = teachers # List of teacher IDs + self.subjects = subjects # List of subject IDs + self.time_slots = time_slots # List of time slots available for scheduling + + def print_chromosomes(self, chromosomes): + for idx, chromosome in enumerate(chromosomes, 1): + print(f"\nChromosome {idx}:") + for day, sections in chromosome.items(): + print(f" {day}:") + for section, schedule in sections.items(): + print(f" Section {section}:") + for entry in schedule: + print(f" Time Slot: {entry['time_slot']}") + print(f" Teacher ID: {entry['teacher_id']}") + print(f" Subject ID: {entry['subject_id']}") + print(f" Classroom ID: {entry['classroom_id']}") + print() + + +class TestTimetableScheduler(unittest.TestCase): + + def setUp(self): + # Example data to simulate a timetable chromosome + self.classrooms = ['Room 101', 'Room 102', 'Room 103'] + self.sections = ['A', 'B', 'C'] + self.teachers = ['T1', 'T2', 'T3'] + self.subjects = ['S1', 'S2', 'S3'] + self.time_slots = ['9:00 AM', '10:00 AM', '11:00 AM', '1:00 PM', '2:00 PM'] + + # Example timetable chromosomes + self.chromosomes = [ + { + 'Monday': { + 'A': [{'time_slot': '9:00 AM', 'teacher_id': 'T1', 'subject_id': 'S1', 'classroom_id': 'Room 101'}], + 'B': [{'time_slot': '10:00 AM', 'teacher_id': 'T2', 'subject_id': 'S2', 'classroom_id': 'Room 102'}], + 'C': [{'time_slot': '11:00 AM', 'teacher_id': 'T3', 'subject_id': 'S3', 'classroom_id': 'Room 103'}], + }, + 'Tuesday': { + 'A': [{'time_slot': '9:00 AM', 'teacher_id': 'T1', 'subject_id': 'S1', 'classroom_id': 'Room 101'}], + 'B': [{'time_slot': '10:00 AM', 'teacher_id': 'T2', 'subject_id': 'S2', 'classroom_id': 'Room 102'}], + 'C': [{'time_slot': '11:00 AM', 'teacher_id': 'T3', 'subject_id': 'S3', 'classroom_id': 'Room 103'}], + } + } + ] + + # Create an instance of the TimetableScheduler + self.scheduler = TimetableScheduler(self.classrooms, self.sections, self.teachers, self.subjects, self.time_slots) + + @patch('sys.stdout', new_callable=StringIO) + def test_print_chromosomes(self, mock_stdout): + """Test if print_chromosomes correctly prints the chromosome information.""" + self.scheduler.print_chromosomes(self.chromosomes) + + # Get the printed output + output = mock_stdout.getvalue() + + # Expected output for the given chromosomes + expected_output = """ +Chromosome 1: + Monday: + Section A: + Time Slot: 9:00 AM + Teacher ID: T1 + Subject ID: S1 + Classroom ID: Room 101 + + Section B: + Time Slot: 10:00 AM + Teacher ID: T2 + Subject ID: S2 + Classroom ID: Room 102 + + Section C: + Time Slot: 11:00 AM + Teacher ID: T3 + Subject ID: S3 + Classroom ID: Room 103 + + Tuesday: + Section A: + Time Slot: 9:00 AM + Teacher ID: T1 + Subject ID: S1 + Classroom ID: Room 101 + + Section B: + Time Slot: 10:00 AM + Teacher ID: T2 + Subject ID: S2 + Classroom ID: Room 102 + + Section C: + Time Slot: 11:00 AM + Teacher ID: T3 + Subject ID: S3 + Classroom ID: Room 103 +""" + # Clean up any extra whitespace from the actual output + output = output.strip() + + # Assert if the output matches the expected output + self.assertEqual(output, expected_output.strip()) + +if __name__ == '__main__': + unittest.main() diff --git a/test_suite/Fitness_Test.py b/test_suite/Fitness_Test.py new file mode 100644 index 0000000..43b709b --- /dev/null +++ b/test_suite/Fitness_Test.py @@ -0,0 +1,692 @@ +# FITNESS + +#1. def __init__(self, sections): + +import unittest + +class Sections: + def __init__(self, num_sections): + # Generate sections as sectionA, sectionB, sectionC, etc. + self.sections = [f"section{chr(65 + i)}" for i in range(num_sections)] + + def __iter__(self): + # Make the class iterable by returning an iterator over the sections list + return iter(self.sections) + + def __len__(self): + # Optionally, define the length of the sections + return len(self.sections) + + def __getitem__(self, index): + # Optional: Enable indexing for the sections + return self.sections[index] + + +# TimetableFitness Class (to manage and calculate timetable fitness) +class TimetableFitness: + def __init__(self, sections): + # Ensure `self.sections` is iterable (convert Sections object to a list if needed) + if isinstance(sections, Sections): + self.sections = list(sections) # Convert to list if it is a custom iterable object + else: + self.sections = sections # Assume it is already a list or other iterable + + # Mocking additional data attributes for the sake of this example + self.days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] + self.time_slots = ["9-10 AM", "10-11 AM", "11-12 PM", "1-2 PM", "2-3 PM"] + + # Teacher-subject mapping + self.subject_teacher_map = {"TCS-531": ["Teacher1", "Teacher2"], "TCS-502": ["Teacher3", "Teacher4"]} + + # Classroom and capacity details + self.classrooms = ["Room1", "Room2", "Room3"] + self.room_capacity = {"Room1": 30, "Room2": 25, "Room3": 40} + + # Data structures for tracking schedules + self.teacher_schedule = {slot: {} for slot in self.time_slots} + self.room_schedule = {slot: {} for slot in self.time_slots} + self.assigned_teachers = {section: {} for section in self.sections} + self.section_rooms = {section: self.classrooms[i % len(self.classrooms)] for i, section in enumerate(self.sections)} + + # Subject-specific constraints + self.subject_weekly_quota = { + "TCS-531": 3, + "TCS-502": 3, + "Placement_Class": 1 + } + + self.weekly_assignments = {section: {subject: 0 for subject in self.subject_weekly_quota} for section in self.sections} + + # Teacher constraints + self.teacher_preferences = {teacher_id: [1, 2, 3, 4, 5, 6, 7] for teacher_id in ["Teacher1", "Teacher2", "Teacher3", "Teacher4"]} + self.teacher_work_load = {teacher: 5 for teacher in self.teacher_preferences} + self.teacher_assignments = {} + + +# Unit Test Class +class TestTimetableFitness(unittest.TestCase): + def setUp(self): + # Create an instance of Sections with 5 sections + self.sections = Sections(5) + self.timetable_fitness = TimetableFitness(self.sections) + + def test_sections_iterable(self): + # Test that Sections object is iterable + sections_list = list(self.sections) + expected_sections = ["sectionA", "sectionB", "sectionC", "sectionD", "sectionE"] + self.assertEqual(sections_list, expected_sections) + + def test_section_rooms_mapping(self): + # Test that each section is mapped to a classroom + section_rooms = self.timetable_fitness.section_rooms + self.assertEqual(len(section_rooms), len(self.sections)) + self.assertTrue(all(section in section_rooms for section in self.sections)) + + def test_assigned_teachers_structure(self): + # Test that assigned teachers structure is initialized correctly + assigned_teachers = self.timetable_fitness.assigned_teachers + self.assertEqual(len(assigned_teachers), len(self.sections)) + self.assertTrue(all(section in assigned_teachers for section in self.sections)) + + +if __name__ == "__main__": + unittest.main() + +#2. def generate_day_schedule(self, day, half_day_sections, week_number): + +import random +from collections import defaultdict + +class TimetableGenerator: + def __init__(self, sections, time_slots, subject_teacher_map, section_rooms, lab): + self.sections = sections + self.time_slots = time_slots + self.subject_teacher_map = subject_teacher_map + self.section_rooms = section_rooms + self.lab = lab + self.weekly_assignments = defaultdict(lambda: defaultdict(int)) + self.teacher_schedule = defaultdict(dict) + self.room_schedule = defaultdict(dict) + self.teacher_assignments = defaultdict(dict) + + def generate_day_schedule(self, day, half_day_sections, week_number): + day_schedule = {} + subject_teacher_usage = {subject: iter(teachers) for subject, teachers in self.subject_teacher_map.items()} + + for section in self.sections: + section_schedule = [] + subjects_used_today = set() + current_room = self.section_rooms[section] + num_slots = 4 if section in half_day_sections else 7 + + index = 1 + while index <= num_slots: + time_slot = self.time_slots[index] + + # Skip if time_slot is already used + if any(item['time_slot'] == time_slot for item in section_schedule): + index += 1 + continue + + available_subjects = list(self.subject_teacher_map.keys()) + subject, teacher = None, None + is_lab = False + + while available_subjects: + subject = random.choice(available_subjects) + + # Handle special rules for Placement_Class and labs + if subject == "Placement_Class" and index != 6: + available_subjects.remove(subject) + continue + + if "Lab" in subject: + if index + 1 > num_slots: # Ensure space for double slot + available_subjects.remove(subject) + continue + is_lab = True # Mark this as a lab + + if subject not in subjects_used_today: + teacher_iter = subject_teacher_usage[subject] + try: + teacher = next(teacher_iter) + except StopIteration: + teacher_iter = iter(self.subject_teacher_map[subject]) + teacher = next(teacher_iter) + subject_teacher_usage[subject] = teacher_iter + break + + available_subjects.remove(subject) + + if subject is None or teacher is None: + subject, teacher = "Library", "None" + + if subject != "Library": + self.weekly_assignments[section][subject] += 1 + + subjects_used_today.add(subject) + + # Assign rooms based on whether it's a lab + assigned_room = random.choice(self.lab) if is_lab else current_room + self.teacher_schedule[time_slot][teacher] = section + self.room_schedule[time_slot][assigned_room] = section + + # Handle double slot allocation for labs + if is_lab: + next_slot_index = index + 1 + if next_slot_index <= num_slots: + next_time_slot = self.time_slots[next_slot_index] + section_schedule.append({ + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_room, + "time_slot": next_time_slot + }) + self.room_schedule[next_time_slot][assigned_room] = section + self.teacher_schedule[next_time_slot][teacher] = section + index += 1 # Skip the next slot for lab + + section_schedule.append({ + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_room, + "time_slot": time_slot + }) + + index += 1 + + day_schedule[section] = section_schedule + return day_schedule +import unittest + +class TestGenerateDaySchedule(unittest.TestCase): + def setUp(self): + self.sections = ["A", "B", "C"] + self.time_slots = { + 1: "9:00-10:00", + 2: "10:00-11:00", + 3: "11:00-12:00", + 4: "12:00-1:00", + 5: "2:00-3:00", + 6: "3:00-4:00", + 7: "4:00-5:00" + } + self.subject_teacher_map = { + "Math": ["T1", "T2"], + "Physics": ["T3"], + "Chemistry Lab": ["T4"], + "Placement_Class": ["T5"], + } + self.section_rooms = {"A": "R1", "B": "R2", "C": "R3"} + self.lab = ["Lab1", "Lab2"] + self.tt = TimetableGenerator( + self.sections, self.time_slots, self.subject_teacher_map, self.section_rooms, self.lab + ) + + def test_schedule_structure(self): + schedule = self.tt.generate_day_schedule("Monday", ["A"], 1) + self.assertIsInstance(schedule, dict) + for section, section_schedule in schedule.items(): + self.assertIsInstance(section_schedule, list) + for entry in section_schedule: + self.assertIn("teacher_id", entry) + self.assertIn("subject_id", entry) + self.assertIn("classroom_id", entry) + self.assertIn("time_slot", entry) + + def test_half_day_slots(self): + schedule = self.tt.generate_day_schedule("Monday", ["A"], 1) + for section, section_schedule in schedule.items(): + if section == "A": + self.assertEqual(len(section_schedule), 4) + else: + self.assertEqual(len(section_schedule), 7) + + def test_no_repeated_time_slots(self): + schedule = self.tt.generate_day_schedule("Monday", ["A"], 1) + for section, section_schedule in schedule.items(): + time_slots = [entry["time_slot"] for entry in section_schedule] + self.assertEqual(len(time_slots), len(set(time_slots))) + + def test_lab_double_slots(self): + schedule = self.tt.generate_day_schedule("Monday", ["A"], 1) + for section, section_schedule in schedule.items(): + for i, entry in enumerate(section_schedule): + if "Lab" in entry['subject_id']: + current_slot = entry['time_slot'] + current_index = list(self.tt.time_slots.values()).index(current_slot) + + # Check if the lab occupies the next time slot + if current_index + 1 < len(self.tt.time_slots): + next_slot = section_schedule[i + 1]['time_slot'] + expected_next_slot = self.tt.time_slots[current_index + 1] + self.assertEqual( + next_slot, + expected_next_slot, + f"Lab for section {section} did not occupy consecutive slots as expected." + ) + + def test_teacher_and_room_assignments(self): + schedule = self.tt.generate_day_schedule("Monday", ["A"], 1) + for section, section_schedule in schedule.items(): + for entry in section_schedule: + teacher = entry['teacher_id'] + room = entry['classroom_id'] + time_slot = entry['time_slot'] + self.assertIn(teacher, self.tt.teacher_schedule[time_slot]) + self.assertIn(room, self.tt.room_schedule[time_slot]) + + +if __name__ == "__main__": + unittest.main() + +#3. def create_timetable(self, num_weeks=1): + +import unittest +from collections import defaultdict +import random + +class TimetableGenerator: + def __init__(self, sections, time_slots, subject_teacher_map, section_rooms, lab): + self.sections = sections + self.time_slots = time_slots + self.subject_teacher_map = subject_teacher_map + self.section_rooms = section_rooms + self.lab = lab + self.days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] # Ensure days are initialized + self.weekly_assignments = defaultdict(lambda: defaultdict(int)) + self.teacher_schedule = defaultdict(dict) + self.room_schedule = defaultdict(dict) + self.teacher_assignments = defaultdict(dict) + + def generate_day_schedule(self, day, half_day_sections, week_number): + day_schedule = {} + subject_teacher_usage = {subject: iter(teachers) for subject, teachers in self.subject_teacher_map.items()} + + for section in self.sections: + section_schedule = [] + subjects_used_today = set() + current_room = self.section_rooms[section] + num_slots = 4 if section in half_day_sections else 7 + + for index in range(1, num_slots + 1): + time_slot = self.time_slots[index] + + # Skip if time_slot is already used + if any(item["time_slot"] == time_slot for item in section_schedule): + continue + + available_subjects = list(self.subject_teacher_map.keys()) + subject, teacher = None, None + is_lab = False + + while available_subjects: + subject = random.choice(available_subjects) + + # Handle special rules for Placement_Class and labs + if subject == "Placement_Class" and index <= 4: # Placement classes are after lunch + available_subjects.remove(subject) + continue + + if "Lab" in subject: + if index + 1 > num_slots: # Ensure space for double slot + available_subjects.remove(subject) + continue + is_lab = True # Mark this as a lab + + if subject not in subjects_used_today: + teacher_iter = subject_teacher_usage[subject] + try: + teacher = next(teacher_iter) + self.teacher_assignments.setdefault(section, {})[subject] = teacher + except StopIteration: + teacher_iter = iter(self.subject_teacher_map[subject]) + teacher = next(teacher_iter) + subject_teacher_usage[subject] = teacher_iter + self.teacher_assignments.setdefault(section, {})[subject] = teacher + + break + + available_subjects.remove(subject) + + if subject is None or teacher is None: + subject, teacher = "Library", "None" + + if subject != "Library": + self.weekly_assignments[section][subject] += 1 + + subjects_used_today.add(subject) + + # Assign rooms based on whether it's a lab + if is_lab: + assigned_room = random.choice(self.lab) + else: + assigned_room = current_room + + self.teacher_schedule[time_slot][teacher] = section + self.room_schedule[time_slot][assigned_room] = section + + # Handle double slot allocation for labs + if is_lab: + next_slot_index = index + 1 + if next_slot_index <= num_slots: + next_time_slot = self.time_slots[next_slot_index] + + # Assign consecutive slot to the lab + section_schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_room, + "time_slot": next_time_slot, + } + ) + self.room_schedule[next_time_slot][assigned_room] = section + self.teacher_schedule[next_time_slot][teacher] = section + index += 1 # Skip the next slot for lab + + section_schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_room, + "time_slot": time_slot, + } + ) + + day_schedule[section] = section_schedule + return day_schedule + + def create_timetable(self, num_weeks=1): + timetable = {} + for week in range(1, num_weeks + 1): + week_schedule = {} + for week_day in self.days: + half_day_sections = random.sample(self.sections, len(self.sections) // 2) + week_schedule[week_day] = self.generate_day_schedule(week_day, half_day_sections, week) + timetable[f"Week {week}"] = week_schedule + return timetable + + +class TestTimetableGenerator(unittest.TestCase): + def setUp(self): + self.sections = ["A", "B", "C"] + self.time_slots = { + 1: "9:00-10:00", + 2: "10:00-11:00", + 3: "11:00-12:00", + 4: "12:00-1:00", + 5: "2:00-3:00", + 6: "3:00-4:00", + 7: "4:00-5:00" + } + self.subject_teacher_map = { + "Math": ["T1", "T2"], + "Physics": ["T3"], + "Chemistry Lab": ["T4"], + "Placement_Class": ["T5"], + } + self.section_rooms = {"A": "R1", "B": "R2", "C": "R3"} + self.lab = ["Lab1", "Lab2"] + self.tt = TimetableGenerator( + self.sections, self.time_slots, self.subject_teacher_map, self.section_rooms, self.lab + ) + + def test_create_timetable(self): + timetable = self.tt.create_timetable(num_weeks=2) + self.assertIsInstance(timetable, dict) + self.assertGreater(len(timetable), 0) + self.assertIn("Week 1", timetable) + self.assertIn("Week 2", timetable) + + # Verify timetable structure + for week, week_schedule in timetable.items(): + self.assertIsInstance(week_schedule, dict) + for day, day_schedule in week_schedule.items(): + self.assertIsInstance(day_schedule, dict) + for section, section_schedule in day_schedule.items(): + self.assertIsInstance(section_schedule, list) + for entry in section_schedule: + self.assertIn("teacher_id", entry) + self.assertIn("subject_id", entry) + self.assertIn("classroom_id", entry) + self.assertIn("time_slot", entry) + +if __name__ == "__main__": + unittest.main() + +#4. def calculate_fitness(self, chromosome): + +import unittest +from collections import defaultdict +import random + +class TimetableGenerator: + def __init__(self, sections, time_slots, subject_teacher_map, section_rooms, lab, section_strength, room_capacity, teacher_preferences, teacher_work_load): + self.sections = sections + self.time_slots = time_slots + self.subject_teacher_map = subject_teacher_map + self.section_rooms = section_rooms + self.lab = lab + self.section_strength = section_strength + self.room_capacity = room_capacity + self.teacher_preferences = teacher_preferences + self.teacher_work_load = teacher_work_load + self.days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] + self.weekly_assignments = defaultdict(lambda: defaultdict(int)) + self.teacher_schedule = defaultdict(dict) + self.room_schedule = defaultdict(dict) + self.teacher_assignments = defaultdict(dict) + + def generate_day_schedule(self, day, half_day_sections, week_number): + day_schedule = {} + subject_teacher_usage = {subject: iter(teachers) for subject, teachers in self.subject_teacher_map.items()} + + for section in self.sections: + section_schedule = [] + subjects_used_today = set() + current_room = self.section_rooms[section] + num_slots = 4 if section in half_day_sections else 7 + + for index in range(1, num_slots + 1): + time_slot = self.time_slots[index] + + if any(item["time_slot"] == time_slot for item in section_schedule): + continue + + available_subjects = list(self.subject_teacher_map.keys()) + subject, teacher = None, None + is_lab = False + + while available_subjects: + subject = random.choice(available_subjects) + + if subject == "Placement_Class" and index <= 4: + available_subjects.remove(subject) + continue + + if "Lab" in subject: + if index + 1 > num_slots: + available_subjects.remove(subject) + continue + is_lab = True + + if subject not in subjects_used_today: + teacher_iter = subject_teacher_usage[subject] + try: + teacher = next(teacher_iter) + self.teacher_assignments.setdefault(section, {})[subject] = teacher + except StopIteration: + teacher_iter = iter(self.subject_teacher_map[subject]) + teacher = next(teacher_iter) + subject_teacher_usage[subject] = teacher_iter + self.teacher_assignments.setdefault(section, {})[subject] = teacher + + break + + available_subjects.remove(subject) + + if subject is None or teacher is None: + subject, teacher = "Library", "None" + + if subject != "Library": + self.weekly_assignments[section][subject] += 1 + + subjects_used_today.add(subject) + + if is_lab: + assigned_room = random.choice(self.lab) + else: + assigned_room = current_room + + self.teacher_schedule[time_slot][teacher] = section + self.room_schedule[time_slot][assigned_room] = section + + if is_lab: + next_slot_index = index + 1 + if next_slot_index <= num_slots: + next_time_slot = self.time_slots[next_slot_index] + section_schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_room, + "time_slot": next_time_slot, + } + ) + self.room_schedule[next_time_slot][assigned_room] = section + self.teacher_schedule[next_time_slot][teacher] = section + index += 1 + + section_schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_room, + "time_slot": time_slot, + } + ) + + day_schedule[section] = section_schedule + return day_schedule + + def calculate_fitness(self, chromosome): + week_fitness_scores = [] + + for week_num, (week, week_schedule) in enumerate(chromosome.items(), start=1): + overall_fitness_score = 0 + section_fitness_scores = {} + + for day, day_schedule in week_schedule.items(): + section_fitness_scores[day] = {} + + for section, section_schedule in day_schedule.items(): + section_score = 100 + teacher_time_slots = {} + classroom_time_slots = {} + teacher_load = {} + + for item in section_schedule: + teacher = item['teacher_id'] + classroom = item['classroom_id'] + time_slot = item['time_slot'] + strength = self.section_strength.get(section, 0) + + if "Break" in time_slot: + continue + + if (teacher, time_slot) in teacher_time_slots: + section_score -= 30 + else: + teacher_time_slots[(teacher, time_slot)] = section + + if (classroom, time_slot) in classroom_time_slots: + section_score -= 20 + else: + classroom_time_slots[(classroom, time_slot)] = section + + if teacher not in teacher_load: + teacher_load[teacher] = [] + teacher_load[teacher].append(time_slot) + + if strength > self.room_capacity.get(classroom, 0): + section_score -= 25 + + preferred_slots = self.teacher_preferences.get(teacher, []) + if time_slot not in preferred_slots: + section_score -= 5 + + for teacher, time_slots in teacher_load.items(): + if len(time_slots) > self.teacher_work_load.get(teacher, 5): + section_score -= 10 + + section_fitness_scores[day][section] = section_score + overall_fitness_score += section_score + + week_fitness_scores.append({ + "week": f"Week {week_num}", + "score": overall_fitness_score + }) + + return week_fitness_scores, section_fitness_scores + + +class TestTimetableGenerator(unittest.TestCase): + def setUp(self): + self.sections = ["A", "B", "C"] + self.time_slots = { + 1: "9:00-10:00", + 2: "10:00-11:00", + 3: "11:00-12:00", + 4: "12:00-1:00", + 5: "2:00-3:00", + 6: "3:00-4:00", + 7: "4:00-5:00" + } + self.subject_teacher_map = { + "Math": ["T1", "T2"], + "Physics": ["T3"], + "Chemistry Lab": ["T4"], + "Placement_Class": ["T5"], + } + self.section_rooms = {"A": "R1", "B": "R2", "C": "R3"} + self.lab = ["Lab1", "Lab2"] + self.section_strength = {"A": 40, "B": 35, "C": 50} + self.room_capacity = {"R1": 40, "R2": 35, "R3": 50} + self.teacher_preferences = {"T1": ["9:00-10:00", "3:00-4:00"], "T2": ["10:00-11:00"]} + self.teacher_work_load = {"T1": 10, "T2": 15, "T3": 8, "T4": 12, "T5": 6} + + self.tt = TimetableGenerator( + self.sections, self.time_slots, self.subject_teacher_map, self.section_rooms, + self.lab, self.section_strength, self.room_capacity, self.teacher_preferences, self.teacher_work_load + ) + + def test_calculate_fitness(self): + # Create a mock chromosome with timetable data + chromosome = { + "Week 1": { + "Monday": { + "A": [{"teacher_id": "T1", "subject_id": "Math", "classroom_id": "R1", "time_slot": "9:00-10:00"}], + "B": [{"teacher_id": "T3", "subject_id": "Physics", "classroom_id": "R2", "time_slot": "10:00-11:00"}], + }, + "Tuesday": { + "C": [{"teacher_id": "T4", "subject_id": "Chemistry Lab", "classroom_id": "Lab1", "time_slot": "11:00-12:00"}], + } + } + } + + week_fitness_scores, section_fitness_scores = self.tt.calculate_fitness(chromosome) + + # Test the overall fitness score for Week 1 + self.assertIsInstance(week_fitness_scores, list) + self.assertGreater(len(week_fitness_scores), 0) + self.assertEqual(week_fitness_scores[0]["week"], "Week 1") + self.assertIn("score", week_fitness_scores[0]) + + # Test the section fitness score for a specific day and section + self.assertIn("Monday", section_fitness_scores) + self.assertIn("A", section_fitness_scores["Monday"]) + self.assertIsInstance(section_fitness_scores["Monday"]["A"], int) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/test_suite/Mutation_Test.py b/test_suite/Mutation_Test.py new file mode 100644 index 0000000..8e0e1aa --- /dev/null +++ b/test_suite/Mutation_Test.py @@ -0,0 +1,366 @@ +#MUTATION TESTS + +#1. def mutate_time_slots_in_section(self, schedule, section): + +import unittest +import random + +class TestMutateTimeSlots(unittest.TestCase): + def setUp(self): + # Initialize a mock schedule for testing + self.schedule = { + "SectionA": [ + {"subject": "Math", "time_slot": "9:00-10:00"}, + {"subject": "English", "time_slot": "10:00-11:00"}, + {"subject": "Science", "time_slot": "11:00-12:00"} + ], + "SectionB": [ + {"subject": "History", "time_slot": "9:00-10:00"}, + ], + "SectionC": [] # Empty section + } + + def mutate_time_slots_in_section(self, schedule, section): + """ + Mutates the time slots within a particular section by shuffling them. + + Args: + schedule (dict): The schedule containing sections and their data. + section (str): The section in which mutation should occur. + + Returns: + bool: True if mutation was performed, False otherwise. + """ + if section in schedule: + section_list = schedule[section] + + if len(section_list) < 2: + return False + + time_slots = [entry["time_slot"] for entry in section_list] + random.shuffle(time_slots) + + for i, entry in enumerate(section_list): + entry["time_slot"] = time_slots[i] + + return True + return False + + def test_valid_mutation(self): + # Test a valid mutation for SectionA + original_schedule = [entry["time_slot"] for entry in self.schedule["SectionA"]] + result = self.mutate_time_slots_in_section(self.schedule, "SectionA") + self.assertTrue(result, "Mutation should occur with valid input") + mutated_schedule = [entry["time_slot"] for entry in self.schedule["SectionA"]] + + # Check if time slots were shuffled + self.assertNotEqual(original_schedule, mutated_schedule, "Schedules should differ after mutation") + self.assertCountEqual(original_schedule, mutated_schedule, "Schedules should have the same elements after mutation") + + def test_not_enough_time_slots(self): + # Test mutation for SectionB (not enough time slots to shuffle) + result = self.mutate_time_slots_in_section(self.schedule, "SectionB") + self.assertFalse(result, "Mutation should not occur for single-element sections") + + def test_empty_section(self): + # Test mutation for SectionC (empty section) + result = self.mutate_time_slots_in_section(self.schedule, "SectionC") + self.assertFalse(result, "Mutation should not occur for empty sections") + + def test_non_existent_section(self): + # Test mutation for a section not in the schedule + result = self.mutate_time_slots_in_section(self.schedule, "SectionD") + self.assertFalse(result, "Mutation should not occur for non-existent sections") + +if __name__ == "__main__": + unittest.main(verbosity=2) + + + +#2. def mutate_schedule_for_week(self, weekly_schedule): + +import unittest +import random +import copy +from unittest.mock import patch + + +class Mutator: + def __init__(self, mutation_rate=0.5): + self.mutation_rate = mutation_rate + + def mutate_time_slots_in_section(self, schedule, section): + """ + Mutates the time slots within a particular section by shuffling them. + + Args: + schedule (dict): The schedule containing sections and their data. + section (str): The section in which mutation should occur. + + Returns: + bool: True if mutation was performed, False otherwise. + """ + if section in schedule: + section_list = schedule[section] + if len(section_list) < 2: + return False # Not enough time slots to shuffle. + + original_slots = [entry["time_slot"] for entry in section_list] + mutated_slots = original_slots[:] + attempt_count = 0 + + while mutated_slots == original_slots and attempt_count < 5: # Ensure mutation occurs + random.shuffle(mutated_slots) + attempt_count += 1 + + if mutated_slots != original_slots: + for i, entry in enumerate(section_list): + entry["time_slot"] = mutated_slots[i] + return True + + return False + + def mutate_schedule_for_week(self, weekly_schedule): + """ + Mutates the time slots for all weekdays in the weekly schedule. + + Args: + weekly_schedule (dict): The weekly schedule containing days, sections, and data. + + Returns: + dict: The mutated weekly schedule. + """ + mutated_weekly_schedule = copy.deepcopy(weekly_schedule) + has_mutation = False # Track if any mutation occurs + + for day, day_schedule in mutated_weekly_schedule.items(): + total_sections = list(day_schedule.keys()) + num_to_mutate = max(1, int(self.mutation_rate * len(total_sections))) + + sections_to_mutate = random.sample(total_sections, num_to_mutate) + + for section in sections_to_mutate: + if self.mutate_time_slots_in_section(day_schedule, section): + has_mutation = True + + if not has_mutation: # Ensure at least one mutation occurs + random_section = random.choice(list(mutated_weekly_schedule.values())) + random_section_name = random.choice(list(random_section.keys())) + self.mutate_time_slots_in_section(random_section, random_section_name) + + return mutated_weekly_schedule + + def print_weekly_schedule(self, weekly_schedule): + """ + Prints the weekly schedule in a readable format. + + Args: + weekly_schedule (dict): The weekly schedule to print. + """ + print("\nWeekly Schedule:") + print("=" * 50) + for day, day_schedule in weekly_schedule.items(): + print(f"Day: {day}") + print("-" * 30) + for section, entries in day_schedule.items(): + print(f" Section {section}:") + for entry in entries: + print( + f" Teacher: {entry['teacher_id']}, Subject: {entry['subject_id']}, " + f"Time Slot: {entry['time_slot']}" + ) + print("=" * 50) + + +class TestMutation(unittest.TestCase): + def setUp(self): + self.weekly_schedule = { + "Monday": { + "A": [ + {"teacher_id": "T1", "subject_id": "Math", "time_slot": "9:00 - 9:55"}, + {"teacher_id": "T2", "subject_id": "Physics", "time_slot": "9:55 - 10:50"}, + ], + "B": [ + {"teacher_id": "T3", "subject_id": "Chemistry", "time_slot": "11:10 - 12:05"}, + {"teacher_id": "T4", "subject_id": "Biology", "time_slot": "12:05 - 1:00"}, + ], + }, + "Tuesday": { + "A": [ + {"teacher_id": "T5", "subject_id": "History", "time_slot": "1:20 - 2:15"}, + ] + }, + } + self.mutator = Mutator() + + def test_default_mutation_rate(self): + self.assertEqual(self.mutator.mutation_rate, 0.5) + + def test_mutate_time_slots_in_section(self): + section_schedule = [ + {"teacher_id": "T1", "subject_id": "Math", "time_slot": "9:00 - 9:55"}, + {"teacher_id": "T2", "subject_id": "Physics", "time_slot": "9:55 - 10:50"}, + ] + original_schedule = copy.deepcopy(section_schedule) + result = self.mutator.mutate_time_slots_in_section({"A": section_schedule}, "A") + self.assertTrue(result) + mutated_schedule = section_schedule + self.assertNotEqual( + [entry["time_slot"] for entry in original_schedule], + [entry["time_slot"] for entry in mutated_schedule], + "Time slots were not mutated", + ) + + @patch("random.shuffle") + def test_mutate_schedule_for_week(self, mock_shuffle): + # Mock shuffle to ensure mutation logic works. + def mock_shuffle_fn(lst): + lst.reverse() + + mock_shuffle.side_effect = mock_shuffle_fn + + mutated_schedule = self.mutator.mutate_schedule_for_week(self.weekly_schedule) + self.assertIsInstance(mutated_schedule, dict) + + # Ensure the time slots are not identical + original_time_slots = [ + entry["time_slot"] + for day in self.weekly_schedule.values() + for section in day.values() + for entry in section + ] + mutated_time_slots = [ + entry["time_slot"] + for day in mutated_schedule.values() + for section in day.values() + for entry in section + ] + self.assertNotEqual(original_time_slots, mutated_time_slots, "Mutation did not occur.") + + def test_print_weekly_schedule(self): + # This is primarily for manual verification; ensure no exceptions occur + self.mutator.print_weekly_schedule(self.weekly_schedule) + + +if __name__ == "__main__": + unittest.main() + + +#3. def print_weekly_schedule(self, weekly_schedule): + +import unittest +from unittest.mock import patch +import random +import copy +from Samples.Mutation_Files.mutation import Mutation + +class TestMutation(unittest.TestCase): + + def setUp(self): + """Setup a basic schedule for testing.""" + self.schedule = { + "Monday": { + "A": [ + {"teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L5", "time_slot": "9:55 - 10:50"}, + {"teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L5", "time_slot": "9:00 - 9:55"} + ] + }, + "Tuesday": { + "A": [{"teacher_id": "AK22", "subject_id": "CSP-501", "classroom_id": "R1", "time_slot": "9:00 - 9:55"}], + "B": [{"teacher_id": "BJ16", "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "9:55 - 10:50"}] + } + } + + def test_mutate_schedule_for_week(self): + """ + Test mutation of the entire week's schedule. + """ + mutation_rate = 0.5 # For testing, we can use a fixed mutation rate (50%) + mutator = Mutation(mutation_rate=mutation_rate) + + # Make a deep copy of the original schedule to compare later + original_schedule = copy.deepcopy(self.schedule) + + # Perform mutation + mutated_schedule = mutator.mutate_schedule_for_week(self.schedule) + + # Test if the mutated schedule is different from the original schedule + self.assertNotEqual(self.schedule, mutated_schedule, "The mutated schedule should be different from the original schedule.") + + # Test that at least one section was mutated + for day in mutated_schedule: + for section in mutated_schedule[day]: + # Check if time slots were shuffled (i.e., they're different from the original time slots) + for original_entry, mutated_entry in zip(original_schedule[day][section], mutated_schedule[day][section]): + self.assertNotEqual(original_entry['time_slot'], mutated_entry['time_slot'], + f"Time slot for {section} on {day} should be mutated.") + + def test_mutate_schedule_with_no_mutation(self): + """ + Test if mutation does not occur when the mutation rate is 0. + """ + mutation_rate = 0 # No mutation expected + mutator = Mutation(mutation_rate=mutation_rate) + + # Make a deep copy of the original schedule to compare later + original_schedule = copy.deepcopy(self.schedule) + + # Perform mutation + mutated_schedule = mutator.mutate_schedule_for_week(self.schedule) + + # Test that the mutated schedule is the same as the original schedule + self.assertEqual(self.schedule, mutated_schedule, "The schedule should remain the same if mutation rate is 0.") + + def test_mutate_schedule_with_max_mutation(self): + """ + Test if mutation affects all sections when mutation rate is 1. + """ + mutation_rate = 1 # Full mutation expected + mutator = Mutation(mutation_rate=mutation_rate) + + # Make a deep copy of the original schedule to compare later + original_schedule = copy.deepcopy(self.schedule) + + # Perform mutation + mutated_schedule = mutator.mutate_schedule_for_week(self.schedule) + + # Test that the mutated schedule is different from the original schedule + self.assertNotEqual(self.schedule, mutated_schedule, "The mutated schedule should be different from the original schedule.") + + # Check that all time slots were mutated + for day in mutated_schedule: + for section in mutated_schedule[day]: + for original_entry, mutated_entry in zip(original_schedule[day][section], mutated_schedule[day][section]): + self.assertNotEqual(original_entry['time_slot'], mutated_entry['time_slot'], + f"Time slot for {section} on {day} should be mutated.") + + @patch("random.sample") # Mock random.sample to control which sections get mutated + def test_mutate_schedule_with_controlled_mutation(self, mock_sample): + """ + Test mutation when we control which sections are selected for mutation. + """ + mutation_rate = 0.5 + mutator = Mutation(mutation_rate=mutation_rate) + + # Mock random.sample to return a controlled selection of sections to mutate + mock_sample.return_value = ["A"] # Only mutate section A + + # Make a deep copy of the original schedule to compare later + original_schedule = copy.deepcopy(self.schedule) + + # Perform mutation + mutated_schedule = mutator.mutate_schedule_for_week(self.schedule) + + # Test that only section A was mutated, and others remain the same + for day in mutated_schedule: + for section in mutated_schedule[day]: + for original_entry, mutated_entry in zip(original_schedule[day][section], mutated_schedule[day][section]): + if section == "A": + self.assertNotEqual(original_entry['time_slot'], mutated_entry['time_slot'], + f"Time slot for {section} on {day} should be mutated.") + else: + self.assertEqual(original_entry['time_slot'], mutated_entry['time_slot'], + f"Time slot for {section} on {day} should not be mutated.") + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/test_suite/ReadMe.md b/test_suite/ReadMe.md new file mode 100644 index 0000000..4759136 --- /dev/null +++ b/test_suite/ReadMe.md @@ -0,0 +1,7 @@ +This folder will contain the test for all the +different flows we have. + +Along with, + +1. unit test -> these will test individual components, like functionalities of Fitness, Crossover, Mutation, etc. +2. functional test -> collectively working of these modules. \ No newline at end of file diff --git a/test_suite/Selection_Tests.py b/test_suite/Selection_Tests.py new file mode 100644 index 0000000..f16f42f --- /dev/null +++ b/test_suite/Selection_Tests.py @@ -0,0 +1,298 @@ +# SELECTION TESTS + +#1.def get_all_time_slots(): + +import unittest +from Constants.time_intervals import TimeIntervalConstant +class TestTimeIntervalConstant(unittest.TestCase): + + def test_get_all_time_slots(self): + expected_time_slots = ["9:00 - 9:55", "9:55 - 10:50", "11:10 - 12:05", "12:05 - 1:00", "1:20 - 2:15", "2:15 - 3:10", "3:30 - 4:25"] + self.assertEqual(TimeIntervalConstant.get_all_time_slots(), expected_time_slots) + +if __name__ == "__main__": + unittest.main() + +import unittest +from Samples.Selection_Files.selection import Timetable # Correct import +from unittest import mock + +class TestTimetable(unittest.TestCase): + + def setUp(self): + # Initialize the object to be tested + self.timetable = Timetable() # Initialize the correct class + + def test_generate_day_schedule_structure(self): + # Call the method + day_schedule = self.timetable.generate_day_schedule() + + # Check if the returned day_schedule is a dictionary with sections + self.assertIsInstance(day_schedule, dict) + self.assertGreater(len(day_schedule), 0) + + for section, schedule in day_schedule.items(): + self.assertIn(section, self.timetable.sections) # Ensure sections are correct + self.assertIsInstance(schedule, list) + self.assertGreater(len(schedule), 0) + + def test_teacher_and_classroom_usage(self): + # Call the method + day_schedule = self.timetable.generate_day_schedule() + + # Check that no teacher is assigned to the same time slot more than once + time_slot_teacher_usage = {time_slot: set() for time_slot in self.timetable.time_slots} + time_slot_classroom_usage = {time_slot: set() for time_slot in self.timetable.time_slots} + + for section, schedule in day_schedule.items(): + for schedule_item in schedule: + if "Break" not in schedule_item["time_slot"]: + self.assertNotIn(schedule_item["teacher_id"], time_slot_teacher_usage[schedule_item["time_slot"]]) + self.assertNotIn(schedule_item["classroom_id"], time_slot_classroom_usage[schedule_item["time_slot"]]) + + # Track teacher and classroom usage + time_slot_teacher_usage[schedule_item["time_slot"]].add(schedule_item["teacher_id"]) + time_slot_classroom_usage[schedule_item["time_slot"]].add(schedule_item["classroom_id"]) + + def test_break_periods(self): + # Call the method + day_schedule = self.timetable.generate_day_schedule() + + # Check that "Break" is assigned correctly + break_found = False + for section, schedule in day_schedule.items(): + for schedule_item in schedule: + if "Break" in schedule_item["subject_id"]: + break_found = True + self.assertEqual(schedule_item["teacher_id"], "None") + self.assertEqual(schedule_item["classroom_id"], "N/A") + + self.assertTrue(break_found, "No break periods found in the schedule") + +if __name__ == "__main__": + unittest.main() + + +#2. def create_timetable(self): + +import unittest +from Samples.Selection_Files.selection import Timetable +from unittest import mock + +class TestTimetable(unittest.TestCase): + + def setUp(self): + # Initialize the Timetable object + self.timetable = Timetable() + + def test_create_timetable_structure(self): + # Call the method + timetable = self.timetable.create_timetable() + + # Check if the timetable is a dictionary with days as keys + self.assertIsInstance(timetable, dict) + self.assertGreater(len(timetable), 0) # Ensure timetable is not empty + + # Verify that all days are present + for day in self.timetable.days: + self.assertIn(day, timetable) + + # Verify that each day contains a schedule (which is a dictionary) + for day, day_schedule in timetable.items(): + self.assertIsInstance(day_schedule, dict) + self.assertGreater(len(day_schedule), 0) + + def test_generate_day_schedule_called(self): + # Mock the generate_day_schedule method to check if it's called for each day + with mock.patch.object(self.timetable, 'generate_day_schedule', return_value={}) as mock_method: + self.timetable.create_timetable() + + # Check if generate_day_schedule was called for each day + self.assertEqual(mock_method.call_count, len(self.timetable.days)) + + def test_timetable_content(self): + # Call the method + timetable = self.timetable.create_timetable() + + # Check that each day's schedule contains sections + for day, day_schedule in timetable.items(): + for section, schedule in day_schedule.items(): + self.assertIn(section, self.timetable.sections) # Ensure valid sections + self.assertIsInstance(schedule, list) + self.assertGreater(len(schedule), 0) + +if __name__ == "__main__": + unittest.main() + +#3. def create_multiple_timelines(self, num_chromosomes): + +import unittest +from Samples.Selection_Files.selection import Timetable # Adjust the import as necessary + +class TestTimetable(unittest.TestCase): + + def setUp(self): + # Initialize the object to be tested + self.timetable = Timetable() # Ensure this is the correct class that has create_multiple_timelines method + + def test_create_multiple_timelines(self): + num_chromosomes = 5 # You can change this number to test with different sizes + + # Call the method + timelines = self.timetable.create_multiple_timelines(num_chromosomes) + + # Check if the returned value is a list + self.assertIsInstance(timelines, list) + + # Check if the list has the correct number of timetables + self.assertEqual(len(timelines), num_chromosomes) + + # Optionally, check if each element in the list is a timetable (or has the correct structure) + for timetable in timelines: + self.assertIsInstance(timetable, dict) # Assuming the timetable is a dictionary as in your previous methods + # Check that the timetable contains valid data, like sections + for section, schedule in timetable.items(): + self.assertIn(section, self.timetable.sections) + self.assertIsInstance(schedule, list) + self.assertGreater(len(schedule), 0) + +if __name__ == "__main__": + unittest.main() + +#4. def calculate_fitness(self, chromosome): + +import unittest +from Samples.Selection_Files.selection import Timetable # Adjust the import path + +class TestTimetableFitness(unittest.TestCase): + + def setUp(self): + # Initialize the object to be tested + self.timetable = Timetable() # Ensure this is the class that has calculate_fitness method + + def test_fitness_calculation_valid_chromosome(self): + # Create a valid chromosome structure + chromosome = { + "Monday": { + "A": [ + {"teacher_id": "T1", "classroom_id": "R1", "time_slot": "9:00-10:00", "subject_id": "S1"}, + {"teacher_id": "T2", "classroom_id": "R2", "time_slot": "10:00-11:00", "subject_id": "S2"}, + {"teacher_id": "T3", "classroom_id": "R3", "time_slot": "11:00-12:00", "subject_id": "S3"}, + {"teacher_id": "None", "classroom_id": "N/A", "time_slot": "Break", "subject_id": "Break"}, + ], + "B": [ + {"teacher_id": "T4", "classroom_id": "R1", "time_slot": "9:00-10:00", "subject_id": "S4"}, + {"teacher_id": "T5", "classroom_id": "R2", "time_slot": "10:00-11:00", "subject_id": "S5"}, + {"teacher_id": "T1", "classroom_id": "R3", "time_slot": "11:00-12:00", "subject_id": "S6"}, + {"teacher_id": "None", "classroom_id": "N/A", "time_slot": "Break", "subject_id": "Break"}, + ], + } + } + + # Calculate fitness + fitness_score = self.timetable.calculate_fitness(chromosome) + + # Assert fitness score is within expected range + self.assertIsInstance(fitness_score, int) + self.assertGreaterEqual(fitness_score, 0) # Fitness should not be negative + + def test_fitness_calculation_with_violations(self): + # Create a chromosome with violations (e.g., overlapping time slots) + chromosome = { + "Monday": { + "A": [ + {"teacher_id": "T1", "classroom_id": "R1", "time_slot": "9:00-10:00", "subject_id": "S1"}, + {"teacher_id": "T1", "classroom_id": "R2", "time_slot": "9:00-10:00", "subject_id": "S2"}, # Teacher overlap + {"teacher_id": "T3", "classroom_id": "R3", "time_slot": "11:00-12:00", "subject_id": "S3"}, + ], + "B": [ + {"teacher_id": "T4", "classroom_id": "R1", "time_slot": "9:00-10:00", "subject_id": "S4"}, + {"teacher_id": "T5", "classroom_id": "R1", "time_slot": "9:00-10:00", "subject_id": "S5"}, # Room overlap + ], + } + } + + # Calculate fitness + fitness_score = self.timetable.calculate_fitness(chromosome) + + # Assert fitness score accounts for penalties + self.assertIsInstance(fitness_score, int) + self.assertLess(fitness_score, 100) # Expect reduced fitness score due to violations + + def test_fitness_calculation_with_over_capacity(self): + # Create a chromosome where room capacity is exceeded + chromosome = { + "Monday": { + "A": [ + {"teacher_id": "T1", "classroom_id": "R3", "time_slot": "9:00-10:00", "subject_id": "S1"}, # Over capacity + ] + } + } + + # Calculate fitness + fitness_score = self.timetable.calculate_fitness(chromosome) + + # Assert fitness score is reduced due to capacity violation + self.assertIsInstance(fitness_score, int) + self.assertLess(fitness_score, 100) # Expect reduced fitness score due to capacity penalty + + def test_fitness_calculation_no_timetable(self): + # Test with an empty chromosome + chromosome = {} + + # Calculate fitness + fitness_score = self.timetable.calculate_fitness(chromosome) + + # Assert fitness score is zero + self.assertEqual(fitness_score, 0) + +if __name__ == "__main__": + unittest.main() + +#5. def select_top_chromosomes(self, population: list, percentage=0.30) -> list: + +import random +def select_top_chromosomes(self, population: list, percentage=0.30) -> list: + num_to_select = int(len(population) * percentage) + fitness_scores = [(chromosome, self.calculate_fitness(chromosome)) for chromosome in population] + sorted_chromosomes = sorted(fitness_scores, key=lambda x: x[1], reverse=True) + + best_num = int(num_to_select * 0.20) + worst_num = int(num_to_select * 0.10) + middle_num = num_to_select - (best_num + worst_num) + + best_chromosomes = sorted_chromosomes[:best_num] + worst_chromosomes = sorted_chromosomes[-worst_num:] + middle_chromosomes = sorted_chromosomes[best_num:best_num + middle_num] + + roulette_num = int(middle_num * 0.70) + rank_num = middle_num - roulette_num + + # Handle empty middle_chromosomes for roulette selection + if middle_chromosomes: + total_fitness = sum(fitness for _, fitness in middle_chromosomes) + roulette_chromosomes = random.choices( + middle_chromosomes, + weights=[fitness / total_fitness for _, fitness in middle_chromosomes], + k=roulette_num + ) + else: + roulette_chromosomes = [] + + if middle_chromosomes: + total_rank = sum(range(1, len(middle_chromosomes) + 1)) + rank_probabilities = [i / total_rank for i in range(1, len(middle_chromosomes) + 1)] + rank_chromosomes = random.choices( + middle_chromosomes, + weights=rank_probabilities, + k=rank_num + ) + else: + rank_chromosomes = [] + + selected_chromosomes = [chromosome for chromosome, _ in best_chromosomes] + selected_chromosomes += [chromosome for chromosome, _ in worst_chromosomes] + selected_chromosomes += [chromosome for chromosome, _ in roulette_chromosomes] + selected_chromosomes += [chromosome for chromosome, _ in rank_chromosomes] + + return selected_chromosomes \ No newline at end of file diff --git a/test_suite/unit_test.py b/test_suite/unit_test.py new file mode 100644 index 0000000..e69de29 From afdebbb5e207a750faad74e3d9523921e15cfe74 Mon Sep 17 00:00:00 2001 From: Ayush-Techie Date: Mon, 2 Dec 2024 15:00:45 +0530 Subject: [PATCH 08/12] Changes for section allocation --- Constants/section_allocation.py | 83 ++++++++++++++------------------- Samples/Adhocs/common.py | 22 +++++++++ 2 files changed, 57 insertions(+), 48 deletions(-) create mode 100644 Samples/Adhocs/common.py diff --git a/Constants/section_allocation.py b/Constants/section_allocation.py index 4c866a2..417b905 100644 --- a/Constants/section_allocation.py +++ b/Constants/section_allocation.py @@ -1,11 +1,12 @@ import random from collections import defaultdict from typing import List, Dict -from Constants.constant import SectionsConstants +from Constants.constant import SectionsConstants, Defaults +from Samples.Adhocs.common import generate_students class StudentScorer: - def __init__(self, attribute_weights: Dict[str, int] = None): + def __init__(self, students: list, attribute_weights: Dict[str, int] = None): """ Initialize the scorer with attribute weights. @@ -13,18 +14,19 @@ def __init__(self, attribute_weights: Dict[str, int] = None): """ self.attribute_weights = attribute_weights or SectionsConstants.ATTRIBUTE_WEIGHTS + self.students = students def calculate_dynamic_cgpa_threshold(self, students: List[Dict], top_percentage: int = 30) -> float: """ - Calculate the CGPA threshold for the top X% of students. + Calculate the CGPA threshold for the top X% of students. - Args: - students (List[Dict]): List of student dictionaries with 'CGPA'. - top_percentage (int): Percentage of students considered top. + Args: + students (List[Dict]): List of student dictionaries with 'CGPA'. + top_percentage (int): Percentage of students considered top. - Returns: - float: The CGPA threshold. + Returns: + float: The CGPA threshold. """ @@ -111,54 +113,39 @@ def divide_students_into_sections(self, students: List[Dict], class_strength: in return sections + def entry_point_for_section_divide(self): + # Calculate the dynamic CGPA threshold + cgpa_threshold_calc = self.calculate_dynamic_cgpa_threshold( + self.students, + top_percentage=30 + ) + # Assign conditions and scores + self.assign_dynamic_conditions(cgpa_threshold_calc) + students_with_scores = self.assign_scores_to_students(self.students) + # Divide students into sections + class_strength = Defaults.max_class_capacity + sections = self.divide_students_into_sections( + students_with_scores, + class_strength + ) + section_allocated_students = list() -def generate_students(num_students: int = 500) -> List[Dict]: - """ - Generate a list of random students with CGPA and hostler status. + for i, section in enumerate(sections, 1): + for student in section: + student["section"] = chr(64 + i) + section_allocated_students.append(student) - Args: - num_students (int): Number of students to generate. + return section_allocated_students - Returns: - List[Dict]: List of student dictionaries. - """ - return [ - { - 'ID': i, - 'CGPA': round(random.uniform(6.0, 9.8), 2), - 'Hostler': random.choice([True, False]) - } - for i in range(1, num_students + 1) - ] if __name__ == "__main__": - # Initialize constants and scorer - scorer = StudentScorer() - students = generate_students(num_students=500) - - # Calculate the dynamic CGPA threshold - cgpa_threshold = scorer.calculate_dynamic_cgpa_threshold(students, top_percentage=30) - print(f"Dynamic CGPA Threshold (Top 30%): {cgpa_threshold}") - - # Assign conditions and scores - scorer.assign_dynamic_conditions(cgpa_threshold) - students_with_scores = scorer.assign_scores_to_students(students) - - # Divide students into sections - class_strength = 50 - sections = scorer.divide_students_into_sections(students_with_scores, class_strength) - - # Display the sections - for i, section in enumerate(sections, 1): - print(f"Section {i} (Total Students: {len(section)}):") - for student in section: - print( - f" Student ID: {student['ID']}, CGPA: {student['CGPA']}, " - f"Hostler: {student['Hostler']}, score: {student['score']}" - ) + + scorer = StudentScorer(generate_students(num_students=500)) + from icecream import ic + ic(scorer.entry_point_for_section_divide()) diff --git a/Samples/Adhocs/common.py b/Samples/Adhocs/common.py new file mode 100644 index 0000000..dd72e78 --- /dev/null +++ b/Samples/Adhocs/common.py @@ -0,0 +1,22 @@ +import random + + +def generate_students(num_students: int = 500) -> list: + """ + Generate a list of random students with CGPA and hostler status. + + Args: + num_students (int): Number of students to generate. + + Returns: + List[Dict]: List of student dictionaries. + """ + + return [ + { + 'ID': i, + 'CGPA': round(random.uniform(6.0, 9.8), 2), + 'Hostler': random.choice([True, False]) + } + for i in range(1, num_students + 1) + ] From 6f1a3003c14132ac753df8a9650e02b966aa7a23 Mon Sep 17 00:00:00 2001 From: Falguni Makhija Date: Sat, 30 Nov 2024 18:28:09 +0530 Subject: [PATCH 09/12] Your commit message --- Samples/T. time table.py | 61 ++++++++ Samples/Teacher's chromo.json | 277 ++++++++++++++++++++++++++++++++++ 2 files changed, 338 insertions(+) create mode 100644 Samples/T. time table.py create mode 100644 Samples/Teacher's chromo.json diff --git a/Samples/T. time table.py b/Samples/T. time table.py new file mode 100644 index 0000000..b1bcef8 --- /dev/null +++ b/Samples/T. time table.py @@ -0,0 +1,61 @@ +from samples import SubjectTeacherMap, WorkingDays, SampleChromosome + + +class TeacherTimetable: + def __init__(self): + # Creating timetable for each teacher + self.teacher_timetable = { + teacher: {day: [] for day in WorkingDays.days} + for teachers in SubjectTeacherMap.subject_teacher_map.values() # Iterate over list of teachers + for teacher in teachers # Iterate over individual teachers in the list + } + + + def generate_teacher_timetable(self, chromosome): + """ + Populate the teacher's timetable based on the provided chromosome schedule. + Now includes the course (Week 1, Week 2, etc.) information. + """ + for week, days in chromosome.items(): # week corresponds to "Week 1", "Week 2", etc. + for day, sections in days.items(): # day corresponds to Monday, Tuesday, etc. + for section, classes in sections.items(): + for entry in classes: + teacher_id = entry["teacher_id"] + subject_id = entry["subject_id"] + time_slot = entry["time_slot"] + classroom_id = entry["classroom_id"] + + # Add this slot to the teacher's timetable + self.teacher_timetable[teacher_id][day].append({ + "course": week, # Include the course name (Week 1, Week 2, etc.) + "section": section, + "subject_id": subject_id, + "time_slot": time_slot, + "classroom_id": classroom_id, + }) + + + def display_timetable(self): + for teacher, days in self.teacher_timetable.items(): + print(f"Teacher: {teacher}") + for day, classes in days.items(): + print(f" {day}:") + for class_entry in classes: + print(f" {class_entry['course']} - Section: {class_entry['section']}, Subject: {class_entry['subject_id']}, Time: {class_entry['time_slot']}, Classroom: {class_entry['classroom_id']}") + + +# Main Code +if __name__ == "__main__": + # Initialize timetable + teacher_timetable = TeacherTimetable() + + # Generate timetable from the sample chromosome + w = { + "Week 1": SampleChromosome.schedule1, + "Week 2": SampleChromosome.schedule1 + } + teacher_timetable.generate_teacher_timetable(w) + + # Display the teacher-specific timetable + teacher_timetable.display_timetable() + diff --git a/Samples/Teacher's chromo.json b/Samples/Teacher's chromo.json new file mode 100644 index 0000000..aa5f1ea --- /dev/null +++ b/Samples/Teacher's chromo.json @@ -0,0 +1,277 @@ +{ + "Teacher1": { + "Monday": [ + { + "course_name": "BTech", + "subject_id": "TCS-509", + "classroom_name": "R1", + "time_slot": "9:00 - 9:55", + "section": "A" + }, + { + "course_name": "BTech", + "subject_id": "TCS-509", + "classroom_name": "R1", + "time_slot": "10:00 - 10:55", + "section": "A" + } + ], + "Tuesday": [ + { + "course_name": "BCA", + "subject_id": "CSP-501", + "classroom_name": "L2", + "time_slot": "9:00 - 9:55", + "section": "B" + }, + { + "course_name": "BTech", + "subject_id": "TCS-509", + "classroom_name": "R1", + "time_slot": "11:00 - 11:55", + "section": "A" + } + ], + "Wednesday": [ + { + "course_name": "MBA", + "subject_id": "MKT-202", + "classroom_name": "R3", + "time_slot": "9:00 - 9:55", + "section": "A" + }, + { + "course_name": "BCA", + "subject_id": "CSP-501", + "classroom_name": "L2", + "time_slot": "10:00 - 10:55", + "section": "B" + } + ], + "Thursday": [ + { + "course_name": "BTech", + "subject_id": "TCS-509", + "classroom_name": "R1", + "time_slot": "9:00 - 9:55", + "section": "A" + }, + { + "course_name": "MBA", + "subject_id": "MKT-202", + "classroom_name": "R3", + "time_slot": "11:00 - 11:55", + "section": "A" + }, + { + "course_name": "BTech", + "subject_id": "TCS-509", + "classroom_name": "R1", + "time_slot": "2:00 - 2:55", + "section": "B" + } + ], + "Friday": [ + { + "course_name": "BCA", + "subject_id": "CSP-501", + "classroom_name": "L2", + "time_slot": "9:00 - 9:55", + "section": "B" + }, + { + "course_name": "MBA", + "subject_id": "MKT-202", + "classroom_name": "R3", + "time_slot": "11:00 - 11:55", + "section": "A" + }, + { + "course_name": "BTech", + "subject_id": "TCS-509", + "classroom_name": "R1", + "time_slot": "2:00 - 2:55", + "section": "A" + } + ] + }, + "Teacher2": { + "Tuesday": [ + { + "course_name": "BCA", + "subject_id": "CSP-501", + "classroom_name": "L2", + "time_slot": "9:00 - 9:55", + "section": "B" + }, + { + "course_name": "BTech", + "subject_id": "TCS-509", + "classroom_name": "R1", + "time_slot": "11:00 - 11:55", + "section": "A" + } + ], + "Wednesday": [ + { + "course_name": "MBA", + "subject_id": "MKT-202", + "classroom_name": "R3", + "time_slot": "9:00 - 9:55", + "section": "A" + }, + { + "course_name": "BCA", + "subject_id": "CSP-501", + "classroom_name": "L2", + "time_slot": "10:00 - 10:55", + "section": "B" + } + ], + "Thursday": [ + { + "course_name": "BTech", + "subject_id": "TCS-509", + "classroom_name": "R1", + "time_slot": "9:00 - 9:55", + "section": "A" + }, + { + "course_name": "MBA", + "subject_id": "MKT-202", + "classroom_name": "R3", + "time_slot": "11:00 - 11:55", + "section": "A" + }, + { + "course_name": "BTech", + "subject_id": "TCS-509", + "classroom_name": "R1", + "time_slot": "2:00 - 2:55", + "section": "B" + } + ], + "Friday": [ + { + "course_name": "BCA", + "subject_id": "CSP-501", + "classroom_name": "L2", + "time_slot": "9:00 - 9:55", + "section": "B" + }, + { + "course_name": "MBA", + "subject_id": "MKT-202", + "classroom_name": "R3", + "time_slot": "11:00 - 11:55", + "section": "A" + }, + { + "course_name": "BTech", + "subject_id": "TCS-509", + "classroom_name": "R1", + "time_slot": "2:00 - 2:55", + "section": "A" + } + ], + "Saturday": [ + { + "course_name": "BTech", + "subject_id": "TCS-509", + "classroom_name": "R1", + "time_slot": "9:00 - 9:55", + "section": "A" + }, + { + "course_name": "BTech", + "subject_id": "TCS-509", + "classroom_name": "R1", + "time_slot": "10:00 - 10:55", + "section": "A" + } + ] + }, + "Teacher3": { + "Monday": [ + { + "course_name": "BBA", + "subject_id": "HRM-301", + "classroom_name": "L3", + "time_slot": "9:00 - 9:55", + "section": "C" + }, + { + "course_name": "MBA", + "subject_id": "FIN-305", + "classroom_name": "R4", + "time_slot": "11:00 - 11:55", + "section": "A" + } + ], + "Tuesday": [ + { + "course_name": "BTech", + "subject_id": "TCS-510", + "classroom_name": "R2", + "time_slot": "10:00 - 10:55", + "section": "A" + }, + { + "course_name": "BCA", + "subject_id": "CSP-502", + "classroom_name": "L1", + "time_slot": "2:00 - 2:55", + "section": "B" + } + ], + "Wednesday": [ + { + "course_name": "MBA", + "subject_id": "MKT-203", + "classroom_name": "R3", + "time_slot": "10:00 - 10:55", + "section": "A" + }, + { + "course_name": "BBA", + "subject_id": "HRM-301", + "classroom_name": "L3", + "time_slot": "2:00 - 2:55", + "section": "C" + } + ] + }, + "Teacher4": { + "Thursday": [ + { + "course_name": "BCA", + "subject_id": "CSP-503", + "classroom_name": "L1", + "time_slot": "9:00 - 9:55", + "section": "B" + }, + { + "course_name": "BTech", + "subject_id": "TCS-511", + "classroom_name": "R2", + "time_slot": "11:00 - 11:55", + "section": "C" + } + ], + "Friday": [ + { + "course_name": "MBA", + "subject_id": "FIN-306", + "classroom_name": "R4", + "time_slot": "9:00 - 9:55", + "section": "A" + }, + { + "course_name": "BBA", + "subject_id": "HRM-302", + "classroom_name": "L3", + "time_slot": "10:00 - 10:55", + } + ] + } +} \ No newline at end of file From 76842708f4f0cc57fe56cbea70cd339e5807d129 Mon Sep 17 00:00:00 2001 From: Falguni Makhija Date: Mon, 2 Dec 2024 15:01:21 +0530 Subject: [PATCH 10/12] Teachers' tt done --- Constants/Teachers_tt.py | 79 +++ Constants/teacher_timetable.json | 1050 ++++++++++++++++++++++++++++++ 2 files changed, 1129 insertions(+) create mode 100644 Constants/Teachers_tt.py create mode 100644 Constants/teacher_timetable.json diff --git a/Constants/Teachers_tt.py b/Constants/Teachers_tt.py new file mode 100644 index 0000000..a76498b --- /dev/null +++ b/Constants/Teachers_tt.py @@ -0,0 +1,79 @@ +import json +from Samples.samples import (SubjectTeacherMap, WorkingDays, SampleChromosome) + +class TeacherTimetable: + def __init__(self): + # Create timetable for each teacher + self.teacher_timetable = { + teacher: {day: [] for day in WorkingDays.days} + for teachers in SubjectTeacherMap.subject_teacher_map.values() + for teacher in teachers + } + self.teacher_assignments = {teacher: {} for teachers in SubjectTeacherMap.subject_teacher_map.values() for teacher in teachers} + + def generate_teacher_timetable(self, chromosome): + """ + Populate the teacher's timetable based on the provided chromosome schedule. + Now includes the course (Week 1, Week 2, etc.) information. + """ + for week, days in chromosome.items(): + for day, sections in days.items(): + for section, classes in sections.items(): + for entry in classes: + teacher_id = entry["teacher_id"] + subject_id = entry["subject_id"] + time_slot = entry["time_slot"] + classroom_id = entry["classroom_id"] + + # Conflict check: Ensure the teacher is not already assigned at this time slot + if teacher_id in self.teacher_assignments and time_slot in self.teacher_assignments[teacher_id]: + continue # Skip this assignment if there's a conflict + + # Add the class to the teacher's timetable + self.teacher_timetable[teacher_id][day].append({ + "course": week, # Include course information (Week 1, Week 2) + "section": section, + "subject_id": subject_id, + "time_slot": time_slot, + "classroom_id": classroom_id, + }) + + # Update the teacher's assignments to reflect the added class + if teacher_id not in self.teacher_assignments: + self.teacher_assignments[teacher_id] = {} + self.teacher_assignments[teacher_id][time_slot] = { + "course": week, + "section": section, + "subject_id": subject_id, + "classroom_id": classroom_id, + "day": day + } + + # Debug: Ensure each teacher is assigned a class + if teacher_id not in self.teacher_timetable: + print(f"Teacher {teacher_id} has no classes assigned.") + + def save_timetable_to_json(self, file_path="Constants/teacher_timetable.json"): + """ + Save the teacher's timetable to a JSON file. + """ + try: + with open(file_path, "w") as json_file: + json.dump(self.teacher_timetable, json_file, indent=4) + except Exception as e: + print(f"Error saving timetable to '{file_path}': {e}") + + +if __name__ == "__main__": + teacher_timetable = TeacherTimetable() + + # Generate timetable from the sample chromosome (Week 1 and Week 2) + w = { + "Week 2": SampleChromosome.schedule2, + "Week 1": SampleChromosome.schedule1 + } + + teacher_timetable.generate_teacher_timetable(w) + + # Save timetable to a JSON file + teacher_timetable.save_timetable_to_json() diff --git a/Constants/teacher_timetable.json b/Constants/teacher_timetable.json new file mode 100644 index 0000000..42c705c --- /dev/null +++ b/Constants/teacher_timetable.json @@ -0,0 +1,1050 @@ +{ + "AB01": { + "Monday": [ + { + "course": "Week 2", + "section": "B", + "subject_id": "TCS-531", + "time_slot": "2:15 - 3:10", + "classroom_id": "R2" + }, + { + "course": "Week 1", + "section": "D", + "subject_id": "TCS-531", + "time_slot": "1:20 - 2:15", + "classroom_id": "R4" + } + ], + "Tuesday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "TCS-531", + "time_slot": "9:00 - 9:55", + "classroom_id": "R1" + }, + { + "course": "Week 1", + "section": "B", + "subject_id": "TCS-531", + "time_slot": "9:55 - 10:50", + "classroom_id": "R2" + } + ], + "Wednesday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "TCS-531", + "time_slot": "11:10 - 12:05", + "classroom_id": "R3" + } + ], + "Thursday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "TCS-531", + "time_slot": "3:30 - 4:25", + "classroom_id": "R3" + } + ], + "Friday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "TCS-531", + "time_slot": "12:05 - 1:00", + "classroom_id": "R3" + } + ], + "Saturday": [] + }, + "PK02": { + "Monday": [], + "Tuesday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "TCS-531", + "time_slot": "12:05 - 1:00", + "classroom_id": "R3" + }, + { + "course": "Week 1", + "section": "C", + "subject_id": "TCS-531", + "time_slot": "1:20 - 2:15", + "classroom_id": "R3" + } + ], + "Wednesday": [ + { + "course": "Week 2", + "section": "D", + "subject_id": "TCS-531", + "time_slot": "2:15 - 3:10", + "classroom_id": "R4" + } + ], + "Thursday": [ + { + "course": "Week 1", + "section": "D", + "subject_id": "TCS-531", + "time_slot": "9:55 - 10:50", + "classroom_id": "R4" + } + ], + "Friday": [], + "Saturday": [] + }, + "SS03": { + "Monday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "TCS-502", + "time_slot": "3:30 - 4:25", + "classroom_id": "R1" + } + ], + "Tuesday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "TCS-502", + "time_slot": "12:05 - 1:00", + "classroom_id": "R1" + }, + { + "course": "Week 1", + "section": "B", + "subject_id": "TCS-502", + "time_slot": "9:00 - 9:55", + "classroom_id": "R2" + } + ], + "Wednesday": [ + { + "course": "Week 2", + "section": "B", + "subject_id": "TCS-502", + "time_slot": "9:55 - 10:50", + "classroom_id": "R2" + } + ], + "Thursday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "TCS-502", + "time_slot": "2:15 - 3:10", + "classroom_id": "R1" + } + ], + "Friday": [], + "Saturday": [] + }, + "AA04": { + "Monday": [], + "Tuesday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "TCS-502", + "time_slot": "3:30 - 4:25", + "classroom_id": "R3" + } + ], + "Wednesday": [], + "Thursday": [ + { + "course": "Week 2", + "section": "B", + "subject_id": "TCS-502", + "time_slot": "9:00 - 9:55", + "classroom_id": "R2" + } + ], + "Friday": [ + { + "course": "Week 2", + "section": "D", + "subject_id": "TCS-502", + "time_slot": "12:05 - 1:00", + "classroom_id": "R4" + } + ], + "Saturday": [] + }, + "AC05": { + "Monday": [], + "Tuesday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "TCS-503", + "time_slot": "9:55 - 10:50", + "classroom_id": "R3" + }, + { + "course": "Week 2", + "section": "D", + "subject_id": "TCS-502", + "time_slot": "12:05 - 1:00", + "classroom_id": "R4" + } + ], + "Wednesday": [ + { + "course": "Week 1", + "section": "D", + "subject_id": "TCS-503", + "time_slot": "11:10 - 12:05", + "classroom_id": "R4" + } + ], + "Thursday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "TCS-503", + "time_slot": "1:20 - 2:15", + "classroom_id": "R3" + } + ], + "Friday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "TCS-503", + "time_slot": "9:00 - 9:55", + "classroom_id": "R3" + } + ], + "Saturday": [] + }, + "SP06": { + "Monday": [ + { + "course": "Week 2", + "section": "B", + "subject_id": "TCS-503", + "time_slot": "1:20 - 2:15", + "classroom_id": "R2" + } + ], + "Tuesday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "TCS-503", + "time_slot": "11:10 - 12:05", + "classroom_id": "R1" + }, + { + "course": "Week 1", + "section": "C", + "subject_id": "TCS-503", + "time_slot": "2:15 - 3:10", + "classroom_id": "R3" + } + ], + "Wednesday": [ + { + "course": "Week 2", + "section": "D", + "subject_id": "PCS-503", + "time_slot": "9:55 - 10:50", + "classroom_id": "L4" + }, + { + "course": "Week 2", + "section": "D", + "subject_id": "PCS-503", + "time_slot": "9:00 - 9:55", + "classroom_id": "L4" + } + ], + "Thursday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "TCS-503", + "time_slot": "3:30 - 4:25", + "classroom_id": "R1" + } + ], + "Friday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "TCS-503", + "time_slot": "12:05 - 1:00", + "classroom_id": "R1" + } + ], + "Saturday": [] + }, + "DP07": { + "Monday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "PCS-503", + "time_slot": "12:05 - 1:00", + "classroom_id": "L4" + }, + { + "course": "Week 2", + "section": "C", + "subject_id": "PCS-503", + "time_slot": "11:10 - 12:05", + "classroom_id": "L4" + }, + { + "course": "Week 1", + "section": "C", + "subject_id": "TCS-503", + "time_slot": "9:00 - 9:55", + "classroom_id": "R3" + } + ], + "Tuesday": [], + "Wednesday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "PCS-503", + "time_slot": "2:15 - 3:10", + "classroom_id": "L1" + }, + { + "course": "Week 2", + "section": "C", + "subject_id": "PCS-503", + "time_slot": "1:20 - 2:15", + "classroom_id": "L1" + } + ], + "Thursday": [ + { + "course": "Week 2", + "section": "B", + "subject_id": "TCS-503", + "time_slot": "9:55 - 10:50", + "classroom_id": "R2" + } + ], + "Friday": [], + "Saturday": [] + }, + "AD08": { + "Monday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "PCS-506", + "time_slot": "12:05 - 1:00", + "classroom_id": "L5" + }, + { + "course": "Week 2", + "section": "A", + "subject_id": "PCS-506", + "time_slot": "11:10 - 12:05", + "classroom_id": "L5" + } + ], + "Tuesday": [ + { + "course": "Week 2", + "section": "D", + "subject_id": "PCS-506", + "time_slot": "9:55 - 10:50", + "classroom_id": "L3" + }, + { + "course": "Week 2", + "section": "D", + "subject_id": "PCS-506", + "time_slot": "9:00 - 9:55", + "classroom_id": "L3" + } + ], + "Wednesday": [], + "Thursday": [], + "Friday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "PCS-506", + "time_slot": "2:15 - 3:10", + "classroom_id": "L5" + }, + { + "course": "Week 2", + "section": "C", + "subject_id": "PCS-506", + "time_slot": "1:20 - 2:15", + "classroom_id": "L5" + } + ], + "Saturday": [] + }, + "RD09": { + "Monday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "PCS-506", + "time_slot": "9:55 - 10:50", + "classroom_id": "L5" + }, + { + "course": "Week 2", + "section": "C", + "subject_id": "PCS-506", + "time_slot": "9:00 - 9:55", + "classroom_id": "L5" + } + ], + "Tuesday": [], + "Wednesday": [], + "Thursday": [], + "Friday": [], + "Saturday": [] + }, + "BJ10": { + "Monday": [ + { + "course": "Week 1", + "section": "A", + "subject_id": "TMA-502", + "time_slot": "1:20 - 2:15", + "classroom_id": "R1" + } + ], + "Tuesday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "TMA-502", + "time_slot": "2:15 - 3:10", + "classroom_id": "R3" + } + ], + "Wednesday": [ + { + "course": "Week 2", + "section": "B", + "subject_id": "TMA-502", + "time_slot": "12:05 - 1:00", + "classroom_id": "R2" + } + ], + "Thursday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "TMA-502", + "time_slot": "11:10 - 12:05", + "classroom_id": "R1" + } + ], + "Friday": [ + { + "course": "Week 1", + "section": "B", + "subject_id": "TMA-502", + "time_slot": "9:00 - 9:55", + "classroom_id": "R2" + } + ], + "Saturday": [] + }, + "RS11": { + "Monday": [ + { + "course": "Week 2", + "section": "B", + "subject_id": "PCS-503", + "time_slot": "9:55 - 10:50", + "classroom_id": "L3" + }, + { + "course": "Week 2", + "section": "B", + "subject_id": "PCS-503", + "time_slot": "9:00 - 9:55", + "classroom_id": "L3" + } + ], + "Tuesday": [], + "Wednesday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "PCS-503", + "time_slot": "12:05 - 1:00", + "classroom_id": "L3" + }, + { + "course": "Week 2", + "section": "A", + "subject_id": "PCS-503", + "time_slot": "11:10 - 12:05", + "classroom_id": "L3" + } + ], + "Thursday": [ + { + "course": "Week 1", + "section": "B", + "subject_id": "TMA-502", + "time_slot": "1:20 - 2:15", + "classroom_id": "R2" + } + ], + "Friday": [ + { + "course": "Week 1", + "section": "B", + "subject_id": "PCS-503", + "time_slot": "2:15 - 3:10", + "classroom_id": "L3" + } + ], + "Saturday": [] + }, + "JM12": { + "Monday": [], + "Tuesday": [], + "Wednesday": [ + { + "course": "Week 1", + "section": "D", + "subject_id": "TMA-502", + "time_slot": "12:05 - 1:00", + "classroom_id": "R4" + } + ], + "Thursday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "TMA-502", + "time_slot": "9:55 - 10:50", + "classroom_id": "R3" + }, + { + "course": "Week 1", + "section": "D", + "subject_id": "TMA-502", + "time_slot": "11:10 - 12:05", + "classroom_id": "R4" + } + ], + "Friday": [], + "Saturday": [] + }, + "NJ13": { + "Monday": [], + "Tuesday": [], + "Wednesday": [], + "Thursday": [ + { + "course": "Week 2", + "section": "D", + "subject_id": "TMA-502", + "time_slot": "9:00 - 9:55", + "classroom_id": "R4" + } + ], + "Friday": [], + "Saturday": [] + }, + "PM14": { + "Monday": [ + { + "course": "Week 2", + "section": "D", + "subject_id": "PMA-502", + "time_slot": "9:55 - 10:50", + "classroom_id": "L1" + }, + { + "course": "Week 2", + "section": "D", + "subject_id": "PMA-502", + "time_slot": "9:00 - 9:55", + "classroom_id": "L1" + }, + { + "course": "Week 1", + "section": "B", + "subject_id": "PMA-502", + "time_slot": "12:05 - 1:00", + "classroom_id": "L4" + }, + { + "course": "Week 1", + "section": "B", + "subject_id": "PMA-502", + "time_slot": "11:10 - 12:05", + "classroom_id": "L4" + } + ], + "Tuesday": [], + "Wednesday": [], + "Thursday": [], + "Friday": [ + { + "course": "Week 1", + "section": "C", + "subject_id": "PMA-502", + "time_slot": "2:15 - 3:10", + "classroom_id": "L5" + }, + { + "course": "Week 1", + "section": "C", + "subject_id": "PMA-502", + "time_slot": "1:20 - 2:15", + "classroom_id": "L5" + } + ], + "Saturday": [] + }, + "AA15": { + "Monday": [], + "Tuesday": [], + "Wednesday": [], + "Thursday": [], + "Friday": [], + "Saturday": [] + }, + "SJ16": { + "Monday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "TCS-509", + "time_slot": "9:55 - 10:50", + "classroom_id": "R1" + }, + { + "course": "Week 1", + "section": "A", + "subject_id": "TCS-509", + "time_slot": "2:15 - 3:10", + "classroom_id": "R1" + } + ], + "Tuesday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "TCS-509", + "time_slot": "3:30 - 4:25", + "classroom_id": "R1" + } + ], + "Wednesday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "TCS-509", + "time_slot": "9:00 - 9:55", + "classroom_id": "R1" + } + ], + "Thursday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "TCS-509", + "time_slot": "12:05 - 1:00", + "classroom_id": "R1" + } + ], + "Friday": [], + "Saturday": [] + }, + "AB17": { + "Monday": [ + { + "course": "Week 2", + "section": "B", + "subject_id": "TCS-509", + "time_slot": "12:05 - 1:00", + "classroom_id": "R2" + }, + { + "course": "Week 1", + "section": "B", + "subject_id": "TCS-509", + "time_slot": "9:55 - 10:50", + "classroom_id": "R2" + } + ], + "Tuesday": [], + "Wednesday": [ + { + "course": "Week 2", + "section": "B", + "subject_id": "TCS-509", + "time_slot": "11:10 - 12:05", + "classroom_id": "R2" + }, + { + "course": "Week 1", + "section": "B", + "subject_id": "TCS-509", + "time_slot": "9:00 - 9:55", + "classroom_id": "R2" + } + ], + "Thursday": [], + "Friday": [ + { + "course": "Week 1", + "section": "C", + "subject_id": "TCS-509", + "time_slot": "3:30 - 4:25", + "classroom_id": "R3" + } + ], + "Saturday": [] + }, + "HP18": { + "Monday": [ + { + "course": "Week 1", + "section": "C", + "subject_id": "TCS-509", + "time_slot": "9:55 - 10:50", + "classroom_id": "R3" + } + ], + "Tuesday": [], + "Wednesday": [ + { + "course": "Week 2", + "section": "D", + "subject_id": "TCS-509", + "time_slot": "3:30 - 4:25", + "classroom_id": "R4" + } + ], + "Thursday": [], + "Friday": [ + { + "course": "Week 2", + "section": "D", + "subject_id": "TCS-509", + "time_slot": "11:10 - 12:05", + "classroom_id": "R4" + }, + { + "course": "Week 1", + "section": "D", + "subject_id": "TCS-509", + "time_slot": "12:05 - 1:00", + "classroom_id": "R4" + } + ], + "Saturday": [] + }, + "SG19": { + "Monday": [ + { + "course": "Week 1", + "section": "D", + "subject_id": "TCS-509", + "time_slot": "2:15 - 3:10", + "classroom_id": "R4" + } + ], + "Tuesday": [ + { + "course": "Week 1", + "section": "D", + "subject_id": "TCS-509", + "time_slot": "9:00 - 9:55", + "classroom_id": "R4" + } + ], + "Wednesday": [], + "Thursday": [], + "Friday": [], + "Saturday": [] + }, + "DT20": { + "Monday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "XCS-501", + "time_slot": "1:20 - 2:15", + "classroom_id": "R1" + } + ], + "Tuesday": [ + { + "course": "Week 2", + "section": "D", + "subject_id": "XCS-501", + "time_slot": "11:10 - 12:05", + "classroom_id": "R4" + }, + { + "course": "Week 1", + "section": "D", + "subject_id": "XCS-501", + "time_slot": "12:05 - 1:00", + "classroom_id": "R4" + } + ], + "Wednesday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "XCS-501", + "time_slot": "9:55 - 10:50", + "classroom_id": "R1" + } + ], + "Thursday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "XCS-501", + "time_slot": "9:00 - 9:55", + "classroom_id": "R3" + } + ], + "Friday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "XCS-501", + "time_slot": "3:30 - 4:25", + "classroom_id": "R3" + } + ], + "Saturday": [] + }, + "PA21": { + "Monday": [ + { + "course": "Week 2", + "section": "B", + "subject_id": "XCS-501", + "time_slot": "3:30 - 4:25", + "classroom_id": "R2" + } + ], + "Tuesday": [ + { + "course": "Week 2", + "section": "B", + "subject_id": "XCS-501", + "time_slot": "9:55 - 10:50", + "classroom_id": "R2" + } + ], + "Wednesday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "XCS-501", + "time_slot": "12:05 - 1:00", + "classroom_id": "R3" + } + ], + "Thursday": [ + { + "course": "Week 2", + "section": "D", + "subject_id": "XCS-501", + "time_slot": "11:10 - 12:05", + "classroom_id": "R4" + } + ], + "Friday": [], + "Saturday": [] + }, + "NB22": { + "Monday": [ + { + "course": "Week 2", + "section": "D", + "subject_id": "XCS-501", + "time_slot": "11:10 - 12:05", + "classroom_id": "R4" + } + ], + "Tuesday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "XCS-501", + "time_slot": "9:00 - 9:55", + "classroom_id": "R3" + }, + { + "course": "Week 1", + "section": "C", + "subject_id": "XCS-501", + "time_slot": "9:55 - 10:50", + "classroom_id": "R3" + } + ], + "Wednesday": [ + { + "course": "Week 2", + "section": "D", + "subject_id": "XCS-501", + "time_slot": "12:05 - 1:00", + "classroom_id": "R4" + } + ], + "Thursday": [], + "Friday": [], + "Saturday": [] + }, + "AK23": { + "Monday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "CSP-501", + "time_slot": "9:00 - 9:55", + "classroom_id": "R1" + }, + { + "course": "Week 1", + "section": "D", + "subject_id": "CSP-501", + "time_slot": "3:30 - 4:25", + "classroom_id": "R4" + } + ], + "Tuesday": [ + { + "course": "Week 2", + "section": "C", + "subject_id": "CSP-501", + "time_slot": "11:10 - 12:05", + "classroom_id": "R3" + } + ], + "Wednesday": [], + "Thursday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "CSP-501", + "time_slot": "1:20 - 2:15", + "classroom_id": "R1" + }, + { + "course": "Week 2", + "section": "C", + "subject_id": "CSP-501", + "time_slot": "2:15 - 3:10", + "classroom_id": "R3" + }, + { + "course": "Week 2", + "section": "D", + "subject_id": "CSP-501", + "time_slot": "12:05 - 1:00", + "classroom_id": "R4" + } + ], + "Friday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "CSP-501", + "time_slot": "9:55 - 10:50", + "classroom_id": "R1" + } + ], + "Saturday": [] + }, + "AP24": { + "Monday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "SCS-501", + "time_slot": "2:15 - 3:10", + "classroom_id": "R1" + }, + { + "course": "Week 2", + "section": "B", + "subject_id": "SCS-501", + "time_slot": "11:10 - 12:05", + "classroom_id": "R2" + } + ], + "Tuesday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "SCS-501", + "time_slot": "9:55 - 10:50", + "classroom_id": "R1" + }, + { + "course": "Week 2", + "section": "C", + "subject_id": "SCS-501", + "time_slot": "1:20 - 2:15", + "classroom_id": "R3" + } + ], + "Wednesday": [ + { + "course": "Week 2", + "section": "B", + "subject_id": "SCS-501", + "time_slot": "9:00 - 9:55", + "classroom_id": "R2" + } + ], + "Thursday": [], + "Friday": [], + "Saturday": [] + }, + "VD25": { + "Monday": [], + "Tuesday": [], + "Wednesday": [], + "Thursday": [], + "Friday": [], + "Saturday": [] + }, + "AK26": { + "Monday": [], + "Tuesday": [ + { + "course": "Week 2", + "section": "A", + "subject_id": "Placement_Class", + "time_slot": "2:15 - 3:10", + "classroom_id": "R1" + } + ], + "Wednesday": [], + "Thursday": [], + "Friday": [], + "Saturday": [] + } +} \ No newline at end of file From e486c504d63429940f552a77474d684c518f9833 Mon Sep 17 00:00:00 2001 From: Prachi Sah Date: Mon, 2 Dec 2024 15:51:08 +0530 Subject: [PATCH 11/12] Teachers_tt.py updated --- Constants/Teachers_tt.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Constants/Teachers_tt.py b/Constants/Teachers_tt.py index a76498b..30c4d7b 100644 --- a/Constants/Teachers_tt.py +++ b/Constants/Teachers_tt.py @@ -49,31 +49,39 @@ def generate_teacher_timetable(self, chromosome): "day": day } - # Debug: Ensure each teacher is assigned a class - if teacher_id not in self.teacher_timetable: - print(f"Teacher {teacher_id} has no classes assigned.") + return self.teacher_timetable def save_timetable_to_json(self, file_path="Constants/teacher_timetable.json"): """ - Save the teacher's timetable to a JSON file. + Create a dictionary and save the teacher's timetable to a JSON file. """ try: + # Create a dictionary explicitly from the teacher timetable + timetable_dict = { + teacher: { + day: classes + for day, classes in days.items() + } + for teacher, days in self.teacher_timetable.items() + } + + # Save the dictionary to a JSON file with open(file_path, "w") as json_file: - json.dump(self.teacher_timetable, json_file, indent=4) + json.dump(timetable_dict, json_file, indent=4) + + print(f"Timetable successfully saved to '{file_path}'.") except Exception as e: print(f"Error saving timetable to '{file_path}': {e}") - if __name__ == "__main__": teacher_timetable = TeacherTimetable() - # Generate timetable from the sample chromosome (Week 1 and Week 2) w = { "Week 2": SampleChromosome.schedule2, "Week 1": SampleChromosome.schedule1 } - teacher_timetable.generate_teacher_timetable(w) - + teacher_tt=teacher_timetable.generate_teacher_timetable(w) + print(teacher_tt) # Save timetable to a JSON file - teacher_timetable.save_timetable_to_json() + teacher_timetable.save_timetable_to_json() From 232f97e41a1ad3a5ffbfac35adbd2e4afb3d233d Mon Sep 17 00:00:00 2001 From: Ayush Kapri Date: Sun, 2 Feb 2025 10:03:42 +0530 Subject: [PATCH 12/12] Fixup for the non-working --- GA/chromosome.py | 4 ++-- GA/fitness.py | 1 + __init__.py | 24 ++++++++++++++---------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/GA/chromosome.py b/GA/chromosome.py index 5be46be..7f90987 100644 --- a/GA/chromosome.py +++ b/GA/chromosome.py @@ -242,10 +242,10 @@ def _generate_weekly_schedule(self): for section in self.sections_manager.sections } - for index, week_day in enumerate(self.weekdays): # Pass index for each day + for index, week_day in enumerate(self.weekdays): half_day_sections = self.sections_manager.sections[: len(self.sections_manager.sections) // 2] daily_schedule, section_subject_usage_tracker, self.teacher_availability_matrix = self.generate_daily_schedule( - half_day_sections, section_subject_usage_tracker, index # Pass index + half_day_sections, section_subject_usage_tracker, index ) week_schedule[week_day] = daily_schedule diff --git a/GA/fitness.py b/GA/fitness.py index 7dcdb65..ca8c78e 100644 --- a/GA/fitness.py +++ b/GA/fitness.py @@ -38,6 +38,7 @@ def evaluate_timetable_fitness(self): daily_section_fitness_scores = {} weekly_fitness_scores = {} teacher_workload_tracking = {} + for week, week_schedule in self.timetable.items(): weekly_fitness = 0 daily_section_fitness_scores[week] = {} diff --git a/__init__.py b/__init__.py index 90a99a7..1721540 100644 --- a/__init__.py +++ b/__init__.py @@ -11,7 +11,7 @@ SpecialSubjects, SubjectWeeklyQuota, Classrooms, - Sections, + Sections, InterDepartment, ) @@ -28,12 +28,12 @@ def timetable_generation(): special_subjects=SpecialSubjects.special_subjects, subject_quota_limits=SubjectWeeklyQuota.subject_quota, labs_list=Classrooms.labs, + labs=SpecialSubjects.Labs, teacher_duty_days=TeacherWorkload.teacher_duty_days, + teacher_availability_matrix=InterDepartment.teacher_availability_matrix, ) - timetable = timetable_generator.create_timetable(Defaults.initial_no_of_chromosomes) - from icecream import ic - ic(timetable) + timetable, teacher_avail_matrix = timetable_generator.create_timetable(Defaults.initial_no_of_chromosomes) # Fitness of each Chromosome fitness_calculator = TimetableFitnessEvaluator( timetable, @@ -54,7 +54,7 @@ def timetable_generation(): # Selection of all Chromosomes selection_object = TimeTableSelection() selected_chromosomes = selection_object.select_chromosomes(fitness_scores[1]) - ic(len(selected_chromosomes)) + # ic(len(selected_chromosomes)) # Crossover for all selected Chromosomes crossover_object = TimeTableCrossOver() @@ -82,10 +82,10 @@ def timetable_generation(): ] - ic(mutated_chromosomes) - ic(selected_chromosomes) - - ic(selected_chromosomes) + # ic(mutated_chromosomes) + # ic(selected_chromosomes) + # + # ic(selected_chromosomes) # Store best of Chromosomes best_chromosome_score = -1 best_chromosome = dict() @@ -94,11 +94,15 @@ def timetable_generation(): if int(week_score) > best_chromosome_score: best_chromosome_score = int(week_score) best_chromosome = timetable[week_no] + return best_chromosome - ic(f"Best Chromosome: {best_chromosome}") def run_timetable_generation(): for generation in range(Defaults.total_no_of_generations): best_chromosome = timetable_generation() return best_chromosome + + +tt = run_timetable_generation() +# ic(f"Best Chromosome: {tt}") \ No newline at end of file