finish step 5, being step 6
This commit is contained in:
1 parent
9a09218f86
commit
2ea40bfe6f
3 files changed
+54
-29
No files matched your search
@@ -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
@@ -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
@@ -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:
|
||||||
|
|||||||
Reference in new issue
Block a user