add course dataclass

This commit is contained in:
SowinskiBraeden committed 2022-11-30 19:57:54 -08:00
1 parent 283c5e72ff
commit fe2561a543
4 files changed
+74 -59

No files matched your search

+24 -22
View File
@@ -6,6 +6,7 @@ from string import hexdigits
from app.util.globals import flex, Error
from app.util.estimateGrade import getGrade
from app.util.students import Student, studentsToDict
from app.util.courses import Course, coursesToDict
# Takes in information to create or add a new conflict
# Returns if the particular student has a previous error
@@ -45,17 +46,17 @@ def insertConflictSolutions(
# 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[Student], # Refer to /util/students.py to see the students list structure
courses: dict, # Refer to /util/courses.py to see the courses dictionary structure
minReq: int = 18, # minimum requests for a class to run
classCap: int = 30, # maximum students per class
blockClassLimit: int = 40, # Block class limit is the number of classrooms available per block. Default 40 classes per block
totalBlocks: int = 10, # total blocks between two semesters -> default is 10 for 5 per semester... or this can be 8 for 4 blocks per semester
students: list[Student], # Refer to /util/students.py to see the students list structure
courses: dict[str: Course], # Refer to /util/courses.py to see the courses dictionary structure
minReq: int = 18, # minimum requests for a class to run
classCap: int = 30, # maximum students per class
blockClassLimit: int = 40, # Block class limit is the number of classrooms available per block. Default 40 classes per block
totalBlocks: int = 10, # total blocks between two semesters -> default is 10 for 5 per semester... or this can be 8 for 4 blocks per semester
studentsDir: str = "../output/raw/students.json",
conflictsDir: str = "../output/raw/conflicts.json",
coursesDir: str = "../output/raw/courses.json"
) -> tuple[dict, Error]: # Returns the completed 'running' dictionary from above
) -> tuple[dict, Error]: # Returns the completed 'running' dictionary
# Return error that totalBlocks is invalid
if totalBlocks not in (10, 8):
totalBlockError = Error('Invalid totalBlocks', 'An invalid \'totalBlocks\' value was provided -> must be 10 or 8')
@@ -80,9 +81,9 @@ def generateScheduleV3(
# Tally class request
for request in (request for request in student.Requests if not request.Alt and request.CrsNo not in flex):
code = request.CrsNo
courses[code]["Requests"] += 1
courses[code].Requests += 1
# Add course to active list if enough requests
if courses[code]["Requests"] > minReq and courses[code]["CrsNo"] not in activeCourses:
if courses[code].Requests > minReq and courses[code].CrsNo not in activeCourses:
activeCourses[code] = courses[code]
@@ -94,14 +95,14 @@ def generateScheduleV3(
for i in range(len(activeCourses)):
index = list(activeCourses)[i]
if index not in emptyClasses: emptyClasses[index] = {}
classRunCount = activeCourses[index]["Requests"] // median
remaining = activeCourses[index]["Requests"] % median
classRunCount = activeCourses[index].Requests // median
remaining = activeCourses[index].Requests % median
# Put number of classRunCount classes in emptyClasses
for j in range(classRunCount):
emptyClasses[index][f"{index}-{hexdigits[j]}"] = {
"CrsNo": index,
"Description": activeCourses[index]["Description"],
"Description": activeCourses[index].Description,
"expectedLen": median # Number of students expected in this class / may be altered later
}
@@ -118,7 +119,7 @@ def generateScheduleV3(
# Create a class using remaining
emptyClasses[index][f"{index}-{hexdigits[classRunCount]}"] = {
"CrsNo": index,
"Description": activeCourses[index]["Description"],
"Description": activeCourses[index].Description,
"expectedLen": remaining
}
@@ -145,7 +146,7 @@ def generateScheduleV3(
# Create a class using remaining + required amount from existing classes
emptyClasses[index][f"{index}-{hexdigits[classRunCount]}"] = {
"CrsNo": index,
"Description": activeCourses[index]["Description"],
"Description": activeCourses[index].Description,
"expectedLen": remaining
}
@@ -225,7 +226,7 @@ def generateScheduleV3(
"index": student.StudentIndex
}],
"CrsNo": course,
"Description": courses[course]["Description"]
"Description": courses[course].Description
}
getAvailableCourse = False
break
@@ -550,13 +551,14 @@ def generateScheduleV3(
# Read timetable and collect data on courses
for block in running:
for course in running[block]:
sem = "Sem1" if int(block[5:]) <= blockPerSem else "Sem2"
courses[running[block][course]["CrsNo"]][sem] += 1
courses[running[block][course]["CrsNo"]]["Occupied"] += len(running[block][course]["students"])
c = running[block][course]["CrsNo"]
if int(block[5:]) <= blockPerSem: courses[c].Sem1 += 1
else: courses[c].Sem2 += 1
courses[c].Occupied += len(running[block][course]["students"])
for course in courses:
courses[course]["Total"] = courses[course]["Sem1"] + courses[course]["Sem2"]
courses[course]["Seats"] = courses[course]["Total"] * classCap
courses[course].Total = courses[course].Sem1 + courses[course].Sem2
courses[course].Seats = courses[course].Total * classCap
# Update/log new student records
with open(studentsDir, "w") as outfile:
@@ -564,7 +566,7 @@ def generateScheduleV3(
# Update/log new course records
with open(coursesDir, "w") as outfile:
json.dump(courses, outfile, indent=2)
json.dump(coursesToDict(courses), outfile, indent=2)
# Save timetable to json
with open('./output/raw/json/timetable.json', 'w') as outfile:
+7 -4
View File
@@ -4,8 +4,11 @@ from app.util.globals import flex
import xlsxwriter
import docx
from app.util.courses import Course
from app.util.students import Student
def putMasterTimetable(
table: dict,
table: dict[str: dict],
output_dir: str = './output/final'
) -> None:
@@ -36,8 +39,8 @@ def putMasterTimetable(
workbook.close()
def putScheduleToWord(
courses: dict,
student: dict,
courses: dict[str: Course],
student: Student,
output_dir: str = './output/final/student_schedules'
) -> None:
@@ -64,7 +67,7 @@ def putScheduleToWord(
for block in student['schedule']:
courseCode = student.Schedule[block][0]
if courseCode in flex: courseName = 'Study'
else: courseName = courses[courseCode[:-2]]["Description"]
else: courseName = courses[courseCode[:-2]].Description
blockNum = int(block.split('block')[1])
semester = 2 if blockNum > 5 else 1
if blockNum > (len(student.Schedule)/2): blockNum -= (len(student.Schedule)/2)
+35 -26
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3.11
import json
import csv
from dataclasses import dataclass
from dataclasses import dataclass, asdict
from app.util.estimateGrade import getGrade
@dataclass
@@ -10,45 +10,54 @@ class Request:
Description: str
Alt: bool
@dataclass
class Course:
CrsNo: str
Description: str
Grade: int
Requests: int = 0 # number of requests for this
Sem1: int = 0 # number of classes running in sem1
Sem2: int = 0 # number of classes running in sem2
Total: int = 0 # total number of classes running
Seats: int = 0 # total number of seats
Occupied: int = 0 # number of occupied seats
def coursesToDict(courses: dict[str: Course]) -> dict[str: dict]:
return [asdict(courses[c]) for c in courses]
# Get all requested courses from data
def getCourses(
data_dir: str,
log: int = False,
log_dir: str = './output/raw/courses.json',
) -> dict[dict[str: any]]:
) -> dict[str: Course]:
courses = {}
courses: dict[str: Course] = {}
with open(data_dir, newline='') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
exists = False
for course in courses:
exists = True if courses[course]["CrsNo"] == row["CrsNo"] else False
exists = True if courses[course].CrsNo == row["CrsNo"] else False
if exists: break
if not exists:
grade = getGrade(row["CrsNo"], row["Description"])
courses[row["CrsNo"]] = {
"CrsNo": row["CrsNo"],
"Description": row["Description"],
"Grade": grade,
"Requests": 0,
"Sem1": 0, # number of classes running in sem1
"Sem2": 0, # number of classes running in sem2
"Total": 0, # total number of classes running
"Seats": 0, # total number of seats
"Occupied": 0, # number of occupied seats
}
courses[row["CrsNo"]] = Course(
row["CrsNo"],
row["Description"],
grade
)
if log:
with open(log_dir, "w") as outfile:
json.dump(courses, outfile, indent=2)
json.dump(coursesToDict(courses), outfile, indent=2)
return courses
# Writes all course data to csv file
def writeCoursesToCSV(
courses: dict,
courses: dict[str: Course],
output_dir: str = './output/raw/csv/courses.csv'
) -> None:
with open(output_dir, 'w') as file:
@@ -69,14 +78,14 @@ def writeCoursesToCSV(
for course in courses:
courseData = (
courses[course]["CrsNo"],
courses[course]["Description"],
courses[course]["Grade"],
courses[course]["Requests"],
courses[course]["Sem1"],
courses[course]["Sem2"],
courses[course]["Total"],
courses[course]["Seats"],
courses[course]["Occupied"]
courses[course].CrsNo,
courses[course].Description,
courses[course].Grade,
courses[course].Requests,
courses[course].Sem1,
courses[course].Sem2,
courses[course].Total,
courses[course].Seats,
courses[course].Occupied
)
writer.writerow(courseData)
+8 -7
View File
@@ -6,7 +6,7 @@ import json
from app.util.convertRawData import putScheduleToWord, putMasterTimetable
from app.generator import generateScheduleV3
from app.util.globals import Error
from app.util.courses import getCourses, writeCoursesToCSV
from app.util.courses import getCourses, writeCoursesToCSV, Course
from app.util.students import getStudents, writeStudentsToCSV, Student
from app.util.errorCalculator import writeErrorsToCSV
from app.util.validator import validateInputData
@@ -41,8 +41,7 @@ def start(
# save raw file data to local file
eel.post_data('Saving raw data to local file...')
with open(raw_data_dir, 'w') as raw_file:
raw_file.write(raw_file_data)
with open(raw_data_dir, 'w') as raw_file: raw_file.write(raw_file_data)
# Ensure params are of correct type
min_req = int(min_req)
@@ -56,14 +55,14 @@ def start(
# call pre-algorithm functions read raw data into a processable format
eel.post_data('Collecting student information...')
students = getStudents(
students: list[Student] = getStudents(
raw_data_dir,
log = False,
totalBlocks = total_blocks,
log_dir = './output/raw/json/students.json'
)
eel.post_data('Collecting course information...')
courses = getCourses(
courses: dict[str: Course] = getCourses(
raw_data_dir,
log = True,
log_dir = f'{raw_json_dir}/courses.json'
@@ -91,8 +90,10 @@ def start(
eel.post_data('Gathering latest data...')
with open(f'{raw_json_dir}/students.json', 'r') as studentFile:
# Unpack dictionary to Student dataclass
students = [Student(**s) for s in json.load(studentFile)]
with open(f'{raw_json_dir}/courses.json', 'r') as cFile: courses = json.load(cFile)
students: list[Student] = [Student(**s) for s in json.load(studentFile)]
with open(f'{raw_json_dir}/courses.json', 'r') as cFile:
# Unpack dictionary to Course dataclass
courses: dict[str: Course] = {c["CrsNo"]: Course(**c) for c in json.load(cFile)}
# call post-algorithm functions to present sorted data
eel.post_data('Writing data to .csv files...')