194 lines
6.6 KiB
Python
194 lines
6.6 KiB
Python
#!/usr/bin/env python
|
|
from os.path import exists as file_exists
|
|
from prettytable import PrettyTable
|
|
from typing import Tuple
|
|
from time import time, sleep
|
|
import itertools
|
|
import threading
|
|
import json
|
|
import sys
|
|
|
|
# Import required utilities
|
|
from util.mockStudents import getSampleStudents
|
|
from util.generateCourses import getSampleCourses
|
|
|
|
# Import other utilities
|
|
from util.debug import debug
|
|
|
|
# Import Algorithm
|
|
from scheduleGenerator.generator import generateScheduleV3
|
|
|
|
done = False
|
|
|
|
# consts
|
|
blockClassLimit = 40
|
|
|
|
# print()
|
|
# debug(f'Current blockClassLimit = {blockClassLimit}')
|
|
|
|
def processing(msg: str):
|
|
for c in itertools.cycle(['|', '/', '-', '\\']):
|
|
if done: break
|
|
sys.stdout.write(f'\r{msg} {c}')
|
|
sys.stdout.flush()
|
|
sleep(0.1)
|
|
|
|
def errorOutput(students) -> Tuple[PrettyTable, dict, dict]:
|
|
# Error Table calulation / output
|
|
f = open('./output/conflicts.json')
|
|
conflicts = json.load(f)
|
|
f.close()
|
|
totalCritical = conflicts["Critical"]["Students"]
|
|
totalAcceptable = conflicts["Acceptable"]["Students"]
|
|
|
|
t = PrettyTable(['Type', 'Error %', 'Success %', 'Student Error Ratio'])
|
|
|
|
errorsC = round(totalCritical / len(students) * 100, 2)
|
|
successC = round(100 - errorsC, 2)
|
|
errorsA = round(totalAcceptable / len(students) * 100, 2)
|
|
successA = round(100 - errorsA, 2)
|
|
|
|
t.add_row(['Critical', f"{errorsC} %", f"{successC} %", f"{totalCritical}/{len(students)} Students"])
|
|
t.add_row(['Acceptable', f"{errorsA} %", f"{successA} %", f"{totalAcceptable}/{len(students)} Students"])
|
|
|
|
return t, conflicts["Critical"], conflicts["Acceptable"]
|
|
|
|
if __name__ == '__main__':
|
|
|
|
if len(sys.argv) == 1:
|
|
print()
|
|
|
|
st = time() # Start time
|
|
|
|
showError = False
|
|
noAnim = False
|
|
for e in sys.argv:
|
|
if e.lower() == 'showerror': showError = True
|
|
if e.lower() == 'noanim': noAnim = True
|
|
|
|
if noAnim:
|
|
sampleStudents = getSampleStudents("./sample_data/course_selection_data.csv", True)
|
|
sampleCourses = getSampleCourses("./sample_data/course_selection_data.csv", True)
|
|
timetable = {}
|
|
timetable["Version"] = 3
|
|
timetable["timetable"] = generateScheduleV3(sampleStudents, sampleCourses, blockClassLimit, "./output/students.json", "./output/conflicts.json")
|
|
else:
|
|
t = threading.Thread(target=processing, args=('Collection student requests',))
|
|
t.start() # Start animation
|
|
sampleStudents = getSampleStudents("./sample_data/course_selection_data.csv", True)
|
|
done = True # End Animation
|
|
print('\nStudent list generated.\n')
|
|
|
|
|
|
t = threading.Thread(target=processing, args=('Collection Course Information',))
|
|
done = False # reset animation
|
|
t.start() # Start animation
|
|
sampleCourses = getSampleCourses("./sample_data/course_selection_data.csv", True)
|
|
done = True # End Animation
|
|
print('\nCourse Information Collected.\n')
|
|
|
|
sleep(0.1)
|
|
t = threading.Thread(target=processing, args=('Processing',))
|
|
done = False # reset animation
|
|
t.start() # Start animation
|
|
timetable = {}
|
|
timetable["Version"] = 3
|
|
timetable["timetable"] = generateScheduleV3(sampleStudents, sampleCourses, blockClassLimit, "./output/students.json", "./output/conflicts.json")
|
|
|
|
done = True # End Animation
|
|
|
|
et = time() # End time
|
|
elapsed_time = round((et - st), 3) # Execution time
|
|
print(f'\n\nDone - Finished in {elapsed_time} seconds\n')
|
|
|
|
if showError:
|
|
errors, _, _ = errorOutput(sampleStudents)
|
|
print(errors)
|
|
|
|
with open("./output/timetable.json", "w") as outfile:
|
|
json.dump(timetable, outfile, indent=2)
|
|
|
|
elif sys.argv[1].lower() == "no_refresh":
|
|
print()
|
|
|
|
showError = False
|
|
noAnim = False
|
|
for e in sys.argv:
|
|
if e.lower() == 'showerror': showError = True
|
|
if e.lower() == 'noanim': noAnim = True
|
|
|
|
st = time() # Start time
|
|
|
|
if not file_exists('./output/students.json') or not file_exists('./output/courses.json'):
|
|
print('\n No previous data exists, please use the following command\n./tinker.py generate_data')
|
|
exit()
|
|
|
|
with open('./output/students.json') as f: sampleStudents = json.load(f)
|
|
with open('./output/courses.json') as f: sampleCourses = json.load(f)
|
|
|
|
for student in sampleStudents:
|
|
student["remainingAlts"] = []
|
|
for i in range(1, 11): student["schedule"][f"block{i}"] = []
|
|
student["classes"] = 0
|
|
|
|
if noAnim:
|
|
timetable = {}
|
|
timetable["Version"] = 3
|
|
timetable["timetable"] = generateScheduleV3(sampleStudents, sampleCourses, blockClassLimit, "./output/students.json", "./output/conflicts.json")
|
|
else :
|
|
t = threading.Thread(target=processing, args=('Processing',))
|
|
t.start() # Start animation
|
|
timetable = {}
|
|
timetable["Version"] = 3
|
|
timetable["timetable"] = generateScheduleV3(sampleStudents, sampleCourses, blockClassLimit, "./output/students.json", "./output/conflicts.json")
|
|
done = True # End Animation
|
|
|
|
et = time() # End time
|
|
elapsed_time = round((et - st), 3) # Execution time
|
|
print(f'\n\nDone - Finished in {elapsed_time} seconds\n')
|
|
|
|
if showError:
|
|
errors, _, _ = errorOutput(sampleStudents)
|
|
print(errors)
|
|
|
|
with open("./output/timetable.json", "w") as outfile:
|
|
json.dump(timetable, outfile, indent=2)
|
|
|
|
elif sys.argv[1].lower() == "generate_data":
|
|
t = threading.Thread(target=processing, args=('Collection student requests',))
|
|
t.start() # Start animation
|
|
_ = getSampleStudents("./sample_data/course_selection_data.csv", True)
|
|
done = True # End Animation
|
|
print('\nStudent list generated.\n')
|
|
|
|
t = threading.Thread(target=processing, args=('Collection Course Information',))
|
|
done = False # reset animation
|
|
t.start() # Start animation
|
|
_ = getSampleCourses("./sample_data/course_selection_data.csv", True)
|
|
done = True # End Animation
|
|
print('\nCourse Information Collected.\n')
|
|
print('You can now use the following command\n./tinker.py no_refresh')
|
|
|
|
elif sys.argv[1].upper() == "ERRORS":
|
|
f = open('./output/students.json')
|
|
studentData = json.load(f)
|
|
f.close()
|
|
errors, critical, acceptable = errorOutput(studentData)
|
|
print()
|
|
print(errors)
|
|
|
|
print(f"\n{critical['Total']} critical errors")
|
|
for i in range(len(critical["Errors"])):
|
|
print(f"x{critical['Errors'][i]['Total']} {critical['Errors'][i]['Code']} Errors: Critical - {critical['Errors'][i]['Description']}")
|
|
|
|
print(f"\n{acceptable['Total']} acceptable errors")
|
|
for i in range(len(acceptable["Errors"])):
|
|
print(f"x{acceptable['Errors'][i]['Total']} {acceptable['Errors'][i]['Code']} Errors: Critical - {acceptable['Errors'][i]['Description']}")
|
|
|
|
exit()
|
|
|
|
else:
|
|
print("Invalid argument")
|
|
exit()
|
|
|