finish step 5, being step 6

This commit is contained in:
SowinskiBraeden committed 2022-04-27 13:22:05 -07:00
1 parent 9a09218f86
commit 2ea40bfe6f
3 files changed
+54 -29

No files matched your search

+39 -18
View File
@@ -38,7 +38,7 @@ from util.generateCourses import getSampleCourses
} }
''' '''
minReq, median, classCap, blockClassLimit = 18, 24, 30, 26 minReq, median, classCap = 18, 24, 30
mockStudents = [] mockStudents = []
activeCourses = {} activeCourses = {}
running = { running = {
@@ -60,7 +60,7 @@ running = {
# It starts by trying to get all classes full and give all students a full class list. # 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 # Then it starts to attempt to fit all classes into a timetable, making corretions along
# the way. Corrections being moving a students class # 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 def equal(l): # Used to equalize list of numbers
q,r = divmod(sum(l),len(l)) q,r = divmod(sum(l),len(l))
return [q+1]*r + [q]*(len(l)-r) 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 # Step 2 - Generate empty classes
allClassRunCounts = [] allClassRunCounts = []
courseRunInfo = {} # Generated now, used in step 4 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 # calculate # of times to run class
for i in range(len(activeCourses)): for i in range(len(activeCourses)):
index = list(activeCourses)[i] index = list(activeCourses)[i]
@@ -164,16 +164,15 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]:
} }
allClassRunCounts.append(classRunCount) allClassRunCounts.append(classRunCount)
# Step 3 Fill emptyClasses with Students # Step 3 Fill emptyClasses with Students
selectedCourses = {} selectedCourses = {}
tempStudents = students tempStudents = list(students)
while len(tempStudents) > 0: 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"]] 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"]): 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"] course = request["CrsNo"]
getAvailableCourse = True getAvailableCourse = True
@@ -184,16 +183,19 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]:
if cname in selectedCourses: if cname in selectedCourses:
if len(selectedCourses[cname]["students"]) < emptyClasses[course][cname]["expectedLen"]: if len(selectedCourses[cname]["students"]) < emptyClasses[course][cname]["expectedLen"]:
# Class exists with room for student # Class exists with room for student
selectedCourses[cname]["students"].append(student["Pupil #"]) selectedCourses[cname]["students"].append({
"Pupil #": student["Pupil #"],
"index": student["studentIndex"]
})
getAvailableCourse = False getAvailableCourse = False
break break
elif len(selectedCourses[cname]["students"]) == emptyClasses[course][cname]["expectedLen"]: elif len(selectedCourses[cname]["students"]) == emptyClasses[course][cname]["expectedLen"]:
# If class is full, and is last class of that course # If class is full, and is last class of that course
if cname[len(cname)-1] == f"{len(emptyClasses[course])-1}": 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 # Use alternate
course = alternates[altOffset]["CrsNo"] course = alternates[0]["CrsNo"]
altOffset += 1 alternates.remove(course)
break break
else: else:
# Force break loop, ignore and let an admin # Force break loop, ignore and let an admin
@@ -202,7 +204,10 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]:
break break
elif cname not in selectedCourses: elif cname not in selectedCourses:
selectedCourses[cname] = { selectedCourses[cname] = {
"students": [student["Pupil #"]], "students": [{
"Pupil #": student["Pupil #"],
"index": student["studentIndex"]
}],
"CrsNo": course, "CrsNo": course,
"Description": courses[course]["Description"] "Description": courses[course]["Description"]
} }
@@ -210,18 +215,20 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]:
break break
elif course not in emptyClasses: elif course not in emptyClasses:
if altOffset is not None and altOffset <= len(alternates)-1: if len(alternates) > 0:
# Use alternate # Use alternate
course = alternates[altOffset]["CrsNo"] course = alternates[0]["CrsNo"]
altOffset += 1 alternates.remove(course)
else: else:
# Force break loop, ignore and let an admin # Force break loop, ignore and let an admin
# handle options to solve for missing class # handle options to solve for missing class
getAvailableCourse = False getAvailableCourse = False
students[student["studentIndex"]]["remainingAlts"] = alternates
tempStudents.remove(student) 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: def stepIndex(offset: int, stepType: int) -> int:
# stepType 0 is for stepping between first and second semester # stepType 0 is for stepping between first and second semester
if stepType == 0: if stepType == 0:
@@ -264,7 +271,7 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]:
while not classInserted: while not classInserted:
blockIndex += offset blockIndex += offset
if len(running[list(running)[blockIndex]]) < 40: if len(running[list(running)[blockIndex]]) < blockClassLimit:
running[list(running)[blockIndex]][cname] = { running[list(running)[blockIndex]][cname] = {
"CrsNo": course, "CrsNo": course,
"Description": emptyClasses[course][cname]["Description"], "Description": emptyClasses[course][cname]["Description"],
@@ -303,7 +310,7 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]:
running[f"block{leastBlock+offset}"][course] = { running[f"block{leastBlock+offset}"][course] = {
"CrsNo": course, "CrsNo": course,
"Description": emptyClasses[course][cname]["Description"], "Description": emptyClasses[course][cname]["Description"],
"Students": selectedCourses[cname]["students"], "students": selectedCourses[cname]["students"],
} }
allClassRunCounts[index] -= 1 allClassRunCounts[index] -= 1
@@ -313,11 +320,25 @@ def generateScheduleV3(students: list, courses: dict) -> dict[str, dict]:
allClassRunCounts.remove(allClassRunCounts[index]) allClassRunCounts.remove(allClassRunCounts[index])
courseRunInfo.pop(list(courseRunInfo)[index]) courseRunInfo.pop(list(courseRunInfo)[index])
# Step 5 - Fill student schedule # 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 # Step 6 - Evaluate, move classes or students to fix
for student in students:
for block in student["schedule"]:
pass
return running return running
if __name__ == '__main__': if __name__ == '__main__':
print("Processing...") print("Processing...")
+1 -1
View File
@@ -43,7 +43,7 @@ if __name__ == '__main__':
samplemockCourses = getSampleCourses("./sample_data/course_selection_data.csv", True) samplemockCourses = getSampleCourses("./sample_data/course_selection_data.csv", True)
timetable = {} timetable = {}
timetable["Version"] = 3 timetable["Version"] = 3
timetable["timetable"] = generateScheduleV3(sampleStudents, samplemockCourses) timetable["timetable"] = generateScheduleV3(sampleStudents, samplemockCourses, 40, "./output/students.json")
else: else:
print("Invalid argument") print("Invalid argument")
+14 -10
View File
@@ -51,6 +51,7 @@ def getSampleStudents(data_dir: str, log: bool = False) -> list[dict]:
"Description": row["Description"], "Description": row["Description"],
"alt": alternate "alt": alternate
}) })
if row["CrsNo"] in ["XAT--12A-S", "XAT--12B-S"]: mockStudents[student["studentIndex"]]["expectedClasses"] -= 1
else: else:
newStudent = { newStudent = {
"Pupil #": row["Pupil #"], "Pupil #": row["Pupil #"],
@@ -60,19 +61,22 @@ def getSampleStudents(data_dir: str, log: bool = False) -> list[dict]:
"alt": alternate "alt": alternate
}], }],
"schedule": { "schedule": {
"block1": "", "block1": [],
"block2": "", "block2": [],
"block3": "", "block3": [],
"block4": "", "block4": [],
"block5": "", "block5": [],
"block6": "", "block6": [],
"block7": "", "block7": [],
"block8": "", "block8": [],
"block9": "", "block9": [],
"block10": "" "block10": []
}, },
"expectedClasses": 8,
"remainingAlts": [],
"studentIndex": len(mockStudents) "studentIndex": len(mockStudents)
} }
if row["CrsNo"] in ["XAT--12A-S", "XAT--12B-S"]: mockStudents[student["studentIndex"]]["expectedClasses"] -= 1
mockStudents.append(newStudent) mockStudents.append(newStudent)
if log: if log: