update step 6
This commit is contained in:
1 parent
73590ccbf9
commit
e582745c07
2 files changed
+152
-51
No files matched your search
@@ -4,6 +4,8 @@ import math
|
|||||||
import random
|
import random
|
||||||
from inspect import currentframe
|
from inspect import currentframe
|
||||||
|
|
||||||
|
from click import pass_context
|
||||||
|
|
||||||
# Import from custom utilities
|
# Import from custom utilities
|
||||||
from util.mockStudents import getSampleStudents
|
from util.mockStudents import getSampleStudents
|
||||||
from util.generateCourses import getSampleCourses
|
from util.generateCourses import getSampleCourses
|
||||||
@@ -347,7 +349,7 @@ def generateScheduleV3(
|
|||||||
|
|
||||||
|
|
||||||
# Step 6 - Evaluate, move students to fix conflicts
|
# Step 6 - Evaluate, move students to fix conflicts
|
||||||
conflictLogs = []
|
conflictLogs = {}
|
||||||
|
|
||||||
for student in students:
|
for student in students:
|
||||||
initialBlocks = [student["schedule"][block] for block in student["schedule"]]
|
initialBlocks = [student["schedule"][block] for block in student["schedule"]]
|
||||||
@@ -378,11 +380,18 @@ def generateScheduleV3(
|
|||||||
while hasConflicts:
|
while hasConflicts:
|
||||||
# Check if conflicts have been resolved
|
# Check if conflicts have been resolved
|
||||||
blocks = [student["schedule"][block] for block in student["schedule"]]
|
blocks = [student["schedule"][block] for block in student["schedule"]]
|
||||||
|
# If there is exceptions, make them look normal in blocks
|
||||||
|
# list to ginore them when looking for clashes, and not to
|
||||||
|
# accidently overwrite while evaluating other classes
|
||||||
|
if len(exceptions) > 0:
|
||||||
|
for i, block in enumerate(blocks):
|
||||||
|
if i in exceptions: blocks[i] = ['EXPT']
|
||||||
conflicts = sum(1 for b in blocks if len(b)>1)
|
conflicts = sum(1 for b in blocks if len(b)>1)
|
||||||
|
|
||||||
if conflicts == 0: hasConflicts = False
|
if conflicts == 0: hasConflicts = False
|
||||||
|
|
||||||
elif conflicts > 0:
|
elif conflicts > 0:
|
||||||
|
|
||||||
blockLens = [len(block) for block in blocks]
|
blockLens = [len(block) for block in blocks]
|
||||||
freeBlocks = [index for index in range(len(blocks)) if len(blocks[index]) == 0]
|
freeBlocks = [index for index in range(len(blocks)) if len(blocks[index]) == 0]
|
||||||
|
|
||||||
@@ -422,44 +431,92 @@ def generateScheduleV3(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
elif found: done = True
|
elif found: done = True
|
||||||
|
|
||||||
if advancedConflict:
|
if advancedConflict:
|
||||||
classIndex = 0
|
attemptResolve = True
|
||||||
solved, done = False, False
|
classOutIndex = 0
|
||||||
classOut = blocks[clashIndex][classIndex]
|
while attemptResolve:
|
||||||
while not done:
|
foundSolution = False
|
||||||
|
newClassOut = blocks[clashIndex][classOutIndex]
|
||||||
for blockIndex in range(len(running)):
|
for blockIndex in range(len(running)):
|
||||||
if solved: break
|
if foundSolution: break
|
||||||
if blockIndex != clashIndex:
|
if blockIndex != clashIndex and blockIndex not in exceptions:
|
||||||
for cname in running[list(running)[blockIndex]]:
|
for cname in running[list(running)[blockIndex]]:
|
||||||
if cname[:-2] == classOut[:-2]:
|
if cname[:-2] == newClassOut[:-2]:
|
||||||
blockIn = f"block{blockIndex+1}"
|
blockIn = f"block{blockIndex+1}"
|
||||||
if blockIndex in freeBlocks:
|
if blockIndex in freeBlocks:
|
||||||
if len(running[blockIn][cname]["students"]) < classCap:
|
if len(running[blockIn][cname]["students"]) < classCap:
|
||||||
# In the rare or impossible case a student was not inserted to a class
|
# In the rare or impossible case a student was not inserted to a class
|
||||||
# And it weren't full, insert them into the class
|
# And it weren't full, insert them into the class
|
||||||
student["schedule"][blockOut].remove(classOut)
|
student["schedule"][blockOut].remove(newClassOut)
|
||||||
student["schedule"][blockIn].append(cname)
|
student["schedule"][blockIn].append(cname)
|
||||||
|
|
||||||
running[blockOut][classOut]["students"].remove(studentData)
|
running[blockOut][newClassOut]["students"].remove(studentData)
|
||||||
running[blockIn][cname]["students"].append(studentData)
|
running[blockIn][cname]["students"].append(studentData)
|
||||||
|
foundSolution = True
|
||||||
solved = True
|
|
||||||
break
|
break
|
||||||
else:
|
|
||||||
|
|
||||||
# Resort to remaining alternates
|
|
||||||
if len(student["remainingAlts"]) > 0:
|
|
||||||
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# Add to exceptions try to continue
|
|
||||||
print("Create exception and continue")
|
|
||||||
pass
|
|
||||||
elif len(blocks[blockIndex]) == 1:
|
elif len(blocks[blockIndex]) == 1:
|
||||||
pass
|
oClassOut = blocks[blockIndex][0]
|
||||||
elif len(blocks[blockIndex]) > 1:
|
found_oBlockSolution = False
|
||||||
print("How to solve")
|
for oBlockIndex in range(len(running)):
|
||||||
|
if found_oBlockSolution: break
|
||||||
|
if oBlockIndex != clashIndex and oBlockIndex not in exceptions and oBlockIndex in freeBlocks:
|
||||||
|
for ocname in running[list(running)[oBlockIndex]]:
|
||||||
|
if ocname[:-2] == oClassOut[:-2]:
|
||||||
|
oBlockIn = f"block{oBlockIndex+1}"
|
||||||
|
if len(running[oBlockIn][ocname]["students"]) < classCap:
|
||||||
|
student["schedule"][blockOut].remove(newClassOut)
|
||||||
|
student["schedule"][blockIn].append(cname)
|
||||||
|
student["schedule"][blockIn].remove(oClassOut)
|
||||||
|
student["schedule"][oBlockIn].append(ocname)
|
||||||
|
|
||||||
|
running[blockOut][newClassOut]["students"].remove(studentData)
|
||||||
|
running[blockIn][cname]["students"].append(studentData)
|
||||||
|
running[blockIn][oClassOut]["students"].remove(studentData)
|
||||||
|
running[oBlockIn][ocname]["students"].append(studentData)
|
||||||
|
|
||||||
|
found_oBlockSolution = True
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
if not found_oBlockSolution: exceptions.append(clashIndex)
|
||||||
|
|
||||||
|
foundSolution = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not foundSolution:
|
||||||
|
if classOutIndex < len(blocks[clashIndex]) - 1: classOutIndex += 1
|
||||||
|
elif classOutIndex == len(blocks[clashIndex]) - 1:
|
||||||
|
if len(student["remainingAlts"]) > 0:
|
||||||
|
exceptions.append(clashIndex)
|
||||||
|
print("Attempt to use alt")
|
||||||
|
attemptResolve = False
|
||||||
|
# Attempt to use alt
|
||||||
|
elif len(student["remainingAlts"]) == 0:
|
||||||
|
exceptions.append(clashIndex)
|
||||||
|
if student["Pupil #"] in conflictLogs:
|
||||||
|
conflictLogs[student["Pupil #"]].append({
|
||||||
|
"Pupil #": student["Pupil #"],
|
||||||
|
"Email": "",
|
||||||
|
"Type": "Critical",
|
||||||
|
"Code": "C-CR",
|
||||||
|
"Conflict": "Couldn't Resolve"
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
conflictLogs[student["Pupil #"]] = [{
|
||||||
|
"Pupil #": student["Pupil #"],
|
||||||
|
"Email": "",
|
||||||
|
"Type": "Critical",
|
||||||
|
"Code": "C-CR",
|
||||||
|
"Conflict": "Couldn't Resolve"
|
||||||
|
}]
|
||||||
|
|
||||||
|
attemptResolve = False
|
||||||
|
else: print("Impossible - Thanos")
|
||||||
|
elif foundSolution:
|
||||||
|
attemptResolve = False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(f"Fatal error ({getLineNumber()}): Impossible error")
|
print(f"Fatal error ({getLineNumber()}): Impossible error")
|
||||||
continue
|
continue
|
||||||
@@ -468,34 +525,60 @@ def generateScheduleV3(
|
|||||||
while not metSelfRequirements:
|
while not metSelfRequirements:
|
||||||
|
|
||||||
if (student["expectedClasses"] - 2) <= student["classes"] < student["expectedClasses"]:
|
if (student["expectedClasses"] - 2) <= student["classes"] < student["expectedClasses"]:
|
||||||
conflictLogs.append({
|
if student["Pupil #"] in conflictLogs:
|
||||||
"Pupil #": student["Pupil #"],
|
conflictLogs[student["Pupil #"]].append({
|
||||||
"Email": "",
|
"Pupil #": student["Pupil #"],
|
||||||
"Type": "Acceptable",
|
"Email": "",
|
||||||
"Code": "A-MC",
|
"Type": "Acceptable",
|
||||||
"Conflict": "Missing 1-2 classes"
|
"Code": "A-MC",
|
||||||
})
|
"Conflict": "Missing 1-2 classes"
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
conflictLogs[student["Pupil #"]] = [{
|
||||||
|
"Pupil #": student["Pupil #"],
|
||||||
|
"Email": "",
|
||||||
|
"Type": "Acceptable",
|
||||||
|
"Code": "A-MC",
|
||||||
|
"Conflict": "Missing 1-2 classes"
|
||||||
|
}]
|
||||||
metSelfRequirements = True
|
metSelfRequirements = True
|
||||||
|
|
||||||
elif student["classes"] < (student["expectedClasses"] - 2):
|
elif student["classes"] < (student["expectedClasses"] - 2):
|
||||||
# Difference between classes inserted to and
|
# Difference between classes inserted to and
|
||||||
# expected classes is too great, attempt to fix
|
# expected classes is too great, attempt to fix
|
||||||
conflictLogs.append({
|
if student["Pupil #"] in conflictLogs:
|
||||||
"Pupil #": student["Pupil #"],
|
conflictLogs[student["Pupil #"]].append({
|
||||||
"Email": "",
|
"Pupil #": student["Pupil #"],
|
||||||
"Type": "Critical",
|
"Email": "",
|
||||||
"Code": "C-MC",
|
"Type": "Critical",
|
||||||
"Conflict": "Missing too many classes"
|
"Code": "C-MC",
|
||||||
})
|
"Conflict": "Missing too many classes"
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
conflictLogs[student["Pupil #"]] = [{
|
||||||
|
"Pupil #": student["Pupil #"],
|
||||||
|
"Email": "",
|
||||||
|
"Type": "Critical",
|
||||||
|
"Code": "C-MC",
|
||||||
|
"Conflict": "Missing too many classes"
|
||||||
|
}]
|
||||||
metSelfRequirements = True
|
metSelfRequirements = True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(f"Fatal error ({getLineNumber()}): Impossible error")
|
print(f"Fatal error ({getLineNumber()}): Impossible error")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
criticals = []
|
||||||
|
acceptables = []
|
||||||
|
for student in conflictLogs:
|
||||||
|
for conflict in conflictLogs[student]:
|
||||||
|
if conflict["Type"] == "Critical": criticals.append(conflict)
|
||||||
|
elif conflict["Type"] == "Acceptable": acceptables.append(conflict)
|
||||||
|
|
||||||
finalConflictLogs = {
|
finalConflictLogs = {
|
||||||
"Critical": [conflict for conflict in conflictLogs if conflict["Type"] == "Critical"],
|
"All": conflictLogs,
|
||||||
"Acceptable": [conflict for conflict in conflictLogs if conflict["Type"] == "Acceptable"]
|
"Critical": criticals,
|
||||||
|
"Acceptable": acceptables
|
||||||
}
|
}
|
||||||
|
|
||||||
# Update Student records
|
# Update Student records
|
||||||
|
|||||||
+27
-9
@@ -20,15 +20,31 @@ def errorOutput(students) -> Tuple[PrettyTable, dict, dict]:
|
|||||||
conflicts = json.load(f)
|
conflicts = json.load(f)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
critical, acceptable = 0, 0
|
||||||
|
for student in conflicts["All"]:
|
||||||
|
studentHasCritical, studentHasAcceptable = False, False
|
||||||
|
read = False
|
||||||
|
for conflict in conflicts["All"][student]:
|
||||||
|
if read: break
|
||||||
|
if conflict["Type"] == "Critical":
|
||||||
|
critical += 1
|
||||||
|
studentHasCritical = True
|
||||||
|
if studentHasAcceptable: read = True
|
||||||
|
elif conflict["Type"] == "Acceptable":
|
||||||
|
acceptable += 1
|
||||||
|
studentHasAcceptable = True
|
||||||
|
if studentHasCritical: read = True
|
||||||
|
continue
|
||||||
|
|
||||||
t = PrettyTable(['Type', 'Error %', 'Success %', 'Error Ratio'])
|
t = PrettyTable(['Type', 'Error %', 'Success %', 'Error Ratio'])
|
||||||
|
|
||||||
errorsC = round(len(conflicts["Critical"]) / len(students) * 100, 2)
|
errorsC = round(critical / len(students) * 100, 2)
|
||||||
successC = round(100 - errorsC, 2)
|
successC = round(100 - errorsC, 2)
|
||||||
errorsA = round(len(conflicts["Acceptable"]) / len(students) * 100, 2)
|
errorsA = round(acceptable / len(students) * 100, 2)
|
||||||
successA = round(100 - errorsA, 2)
|
successA = round(100 - errorsA, 2)
|
||||||
|
|
||||||
t.add_row(['Critical', f"{errorsC} %", f"{successC} %", f"{len(conflicts['Critical'])}/{len(students)}"])
|
t.add_row(['Critical', f"{errorsC} %", f"{successC} %", f"{critical}/{len(students)}"])
|
||||||
t.add_row(['Acceptable', f"{errorsA} %", f"{successA} %", f"{len(conflicts['Acceptable'])}/{len(students)}"])
|
t.add_row(['Acceptable', f"{errorsA} %", f"{successA} %", f"{acceptable}/{len(students)}"])
|
||||||
|
|
||||||
return t, conflicts["Critical"], conflicts["Acceptable"]
|
return t, conflicts["Critical"], conflicts["Acceptable"]
|
||||||
|
|
||||||
@@ -69,8 +85,10 @@ if __name__ == '__main__':
|
|||||||
print(errors)
|
print(errors)
|
||||||
|
|
||||||
elif sys.argv[1].upper() == "ERRORS":
|
elif sys.argv[1].upper() == "ERRORS":
|
||||||
sampleStudents = getSampleStudents("./sample_data/course_selection_data.csv", True)
|
f = open('./output/students.json')
|
||||||
errors, critical, acceptable = errorOutput(sampleStudents)
|
studentData = json.load(f)
|
||||||
|
f.close()
|
||||||
|
errors, critical, acceptable = errorOutput(studentData)
|
||||||
print()
|
print()
|
||||||
print(errors)
|
print(errors)
|
||||||
|
|
||||||
@@ -78,11 +96,11 @@ if __name__ == '__main__':
|
|||||||
c1, c2, co = 0, 0, 0
|
c1, c2, co = 0, 0, 0
|
||||||
for error in critical:
|
for error in critical:
|
||||||
if error["Code"] == "C-MC": c1 +=1
|
if error["Code"] == "C-MC": c1 +=1
|
||||||
elif error["Code"] == "C-CSS": c2 += 1
|
elif error["Code"] == "C-CR": c2 += 1
|
||||||
else: co += 1
|
else: co += 1
|
||||||
|
|
||||||
print(f"x{c1} C-MC Errors: Critical - Missing Classes")
|
print(f"x{c1} C-MC Errors: Critical - Missing too many Classes")
|
||||||
print(f"x{c2} C-CSS Errors: Critical - Couldn't Solve Schedule")
|
print(f"x{c2} C-CR Errors: Critical - Couldn't Resolve")
|
||||||
print(f"x{co} Other/Undefined Critical Errors")
|
print(f"x{co} Other/Undefined Critical Errors")
|
||||||
|
|
||||||
print(f"\n{len(acceptable)} acceptable errors")
|
print(f"\n{len(acceptable)} acceptable errors")
|
||||||
|
|||||||
Reference in new issue
Block a user