From 2ea40bfe6fa157712e2d528f283540941eb4af31 Mon Sep 17 00:00:00 2001 From: Braeden Sowinski Date: Wed, 27 Apr 2022 13:22:05 -0700 Subject: [PATCH] finish step 5, being step 6 --- test/scheduleGenerator/v3.py | 57 ++++++++++++++++++++++++------------ test/tinker.py | 2 +- test/util/mockStudents.py | 24 ++++++++------- 3 files changed, 54 insertions(+), 29 deletions(-) diff --git a/test/scheduleGenerator/v3.py b/test/scheduleGenerator/v3.py index 98845d7..ca882d7 100644 --- a/test/scheduleGenerator/v3.py +++ b/test/scheduleGenerator/v3.py @@ -38,7 +38,7 @@ from util.generateCourses import getSampleCourses } ''' -minReq, median, classCap, blockClassLimit = 18, 24, 30, 26 +minReq, median, classCap = 18, 24, 30 mockStudents = [] activeCourses = {} running = { @@ -60,7 +60,7 @@ running = { # It starts by trying to get all classes full and give all students a full class list. # Then it starts to attempt to fit all classes into a timetable, making corretions along # the way. Corrections being moving a students class -def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]: +def generateScheduleV3(students: list, courses: dict, blockClassLimit: int=40, studentsDir: str="../output/students.json") -> dict[str, dict]: def equal(l): # Used to equalize list of numbers q,r = divmod(sum(l),len(l)) return [q+1]*r + [q]*(len(l)-r) @@ -80,7 +80,7 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]: # Step 2 - Generate empty classes allClassRunCounts = [] courseRunInfo = {} # Generated now, used in step 4 - emptyClasses = {} # List of all classes with how many students should be entered during generation + emptyClasses = {} # List of all classes with how many students should be entered during step 3 # calculate # of times to run class for i in range(len(activeCourses)): index = list(activeCourses)[i] @@ -164,16 +164,15 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]: } allClassRunCounts.append(classRunCount) + # Step 3 Fill emptyClasses with Students selectedCourses = {} - tempStudents = students + tempStudents = list(students) while len(tempStudents) > 0: - student = tempStudents[random.randint(0, len(students)-1)] + student = tempStudents[random.randint(0, len(tempStudents)-1)] alternates = [request for request in student["requests"] if request["alt"]] - altOffset = None - if len(alternates) > 0: altOffset = 0 for request in (request for request in student["requests"] if not request["alt"] and request["CrsNo"] not in ["XAT--12A-S", "XAT--12B-S"]): course = request["CrsNo"] getAvailableCourse = True @@ -184,16 +183,19 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]: if cname in selectedCourses: if len(selectedCourses[cname]["students"]) < emptyClasses[course][cname]["expectedLen"]: # Class exists with room for student - selectedCourses[cname]["students"].append(student["Pupil #"]) + selectedCourses[cname]["students"].append({ + "Pupil #": student["Pupil #"], + "index": student["studentIndex"] + }) getAvailableCourse = False break elif len(selectedCourses[cname]["students"]) == emptyClasses[course][cname]["expectedLen"]: # If class is full, and is last class of that course if cname[len(cname)-1] == f"{len(emptyClasses[course])-1}": - if altOffset is not None and altOffset <= len(alternates)-1: + if len(alternates) > 0: # Use alternate - course = alternates[altOffset]["CrsNo"] - altOffset += 1 + course = alternates[0]["CrsNo"] + alternates.remove(course) break else: # Force break loop, ignore and let an admin @@ -202,7 +204,10 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]: break elif cname not in selectedCourses: selectedCourses[cname] = { - "students": [student["Pupil #"]], + "students": [{ + "Pupil #": student["Pupil #"], + "index": student["studentIndex"] + }], "CrsNo": course, "Description": courses[course]["Description"] } @@ -210,18 +215,20 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]: break elif course not in emptyClasses: - if altOffset is not None and altOffset <= len(alternates)-1: + if len(alternates) > 0: # Use alternate - course = alternates[altOffset]["CrsNo"] - altOffset += 1 + course = alternates[0]["CrsNo"] + alternates.remove(course) else: # Force break loop, ignore and let an admin # handle options to solve for missing class getAvailableCourse = False + students[student["studentIndex"]]["remainingAlts"] = alternates tempStudents.remove(student) - # Step 4 - Attempt to fit classes into timetable + + # Step 4 - Attempt to fit classes into timetable def stepIndex(offset: int, stepType: int) -> int: # stepType 0 is for stepping between first and second semester if stepType == 0: @@ -264,7 +271,7 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]: while not classInserted: blockIndex += offset - if len(running[list(running)[blockIndex]]) < 40: + if len(running[list(running)[blockIndex]]) < blockClassLimit: running[list(running)[blockIndex]][cname] = { "CrsNo": course, "Description": emptyClasses[course][cname]["Description"], @@ -303,7 +310,7 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]: running[f"block{leastBlock+offset}"][course] = { "CrsNo": course, "Description": emptyClasses[course][cname]["Description"], - "Students": selectedCourses[cname]["students"], + "students": selectedCourses[cname]["students"], } allClassRunCounts[index] -= 1 @@ -313,11 +320,25 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]: allClassRunCounts.remove(allClassRunCounts[index]) courseRunInfo.pop(list(courseRunInfo)[index]) + # Step 5 - Fill student schedule + for block in running: + for cname in running[block]: + for student in running[block][cname]["students"]: + students[student["index"]]["schedule"][block].append(cname) + + with open(studentsDir, "w") as outfile: + json.dump(students, outfile, indent=2) + # Step 6 - Evaluate, move classes or students to fix + for student in students: + for block in student["schedule"]: + pass + return running + if __name__ == '__main__': print("Processing...") diff --git a/test/tinker.py b/test/tinker.py index df0c6ca..603ac57 100644 --- a/test/tinker.py +++ b/test/tinker.py @@ -43,7 +43,7 @@ if __name__ == '__main__': samplemockCourses = getSampleCourses("./sample_data/course_selection_data.csv", True) timetable = {} timetable["Version"] = 3 - timetable["timetable"] = generateScheduleV3(sampleStudents, samplemockCourses) + timetable["timetable"] = generateScheduleV3(sampleStudents, samplemockCourses, 40, "./output/students.json") else: print("Invalid argument") diff --git a/test/util/mockStudents.py b/test/util/mockStudents.py index b32f643..5a79683 100644 --- a/test/util/mockStudents.py +++ b/test/util/mockStudents.py @@ -51,6 +51,7 @@ def getSampleStudents(data_dir: str, log: bool = False) -> list[dict]: "Description": row["Description"], "alt": alternate }) + if row["CrsNo"] in ["XAT--12A-S", "XAT--12B-S"]: mockStudents[student["studentIndex"]]["expectedClasses"] -= 1 else: newStudent = { "Pupil #": row["Pupil #"], @@ -60,19 +61,22 @@ def getSampleStudents(data_dir: str, log: bool = False) -> list[dict]: "alt": alternate }], "schedule": { - "block1": "", - "block2": "", - "block3": "", - "block4": "", - "block5": "", - "block6": "", - "block7": "", - "block8": "", - "block9": "", - "block10": "" + "block1": [], + "block2": [], + "block3": [], + "block4": [], + "block5": [], + "block6": [], + "block7": [], + "block8": [], + "block9": [], + "block10": [] }, + "expectedClasses": 8, + "remainingAlts": [], "studentIndex": len(mockStudents) } + if row["CrsNo"] in ["XAT--12A-S", "XAT--12B-S"]: mockStudents[student["studentIndex"]]["expectedClasses"] -= 1 mockStudents.append(newStudent) if log: