Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): convert last few routes to use token instead of old models #1195

Merged
merged 17 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ disable=import-error, # allow import from folder.file instea
missing-module-docstring, # allow for missing module docstrings, democratially ignored
missing-function-docstring, # TODO: be much more selective with these, and docstring most functions
missing-class-docstring, #
import-outside-toplevel, # allow non-top level imports. used to prevent circular dependencies
cyclic-import, # TODO: remove these when utility refactor + old user storage dicts removal
# parameter-unpacking,
# unpacking-in-except,
# old-raise-syntax,
Expand Down
21 changes: 14 additions & 7 deletions backend/algorithms/objects/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import json
import re
from itertools import chain
from typing import List, Literal, Optional, Tuple
from typing import List, Literal, Optional, Tuple, TypedDict

from algorithms.cache.cache_config import CACHED_EQUIVALENTS_FILE, CACHED_EXCLUSIONS_FILE
from algorithms.objects.categories import AnyCategory, Category
Expand All @@ -20,6 +20,13 @@
with open(CACHED_EXCLUSIONS_FILE, "r", encoding="utf8") as f:
CACHED_EXCLUSIONS: dict[str, dict[str, Literal[1]]] = json.load(f)

class UserJSON(TypedDict, total=False):
courses: dict[str, tuple[int, Optional[int]]]
program: Optional[str]
specialisations: list[str]
year: int
core_courses: list[str]

class User:
""" A user and their data which will be used to determine if they can take a course """
# pylint: disable=too-many-public-methods
Expand Down Expand Up @@ -119,18 +126,18 @@ def in_specialisation(self, specialisation: str):
for spec in self.specialisations
)

def load_json(self, data):
def load_json(self, data: UserJSON):
""" Given the user data, correctly loads it into this user class """

if "program" in data.keys():
if "program" in data:
self.program = copy.deepcopy(data["program"])
if "specialisations" in data.keys():
if "specialisations" in data:
self.specialisations = copy.deepcopy(data["specialisations"])
if "courses" in data.keys():
if "courses" in data:
self.courses = copy.deepcopy(data["courses"])
if "year" in data.keys():
if "year" in data:
self.year = copy.deepcopy(data["year"])
if "core_courses" in data.keys():
if "core_courses" in data:
self.core_courses = copy.deepcopy(data["core_courses"])

def get_grade(self, course: str):
Expand Down
12 changes: 6 additions & 6 deletions backend/algorithms/tests/test_autoplanning.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
from algorithms.autoplanning import autoplan, terms_between
from algorithms.objects.course import Course
from algorithms.objects.user import User
from algorithms.validate_term_planner import validate_terms
from algorithms.validate_term_planner import RawUserPlan, validate_terms
from pytest import raises
from server.routers.model import CONDITIONS, ValidPlannerData
from server.routers.model import CONDITIONS


def get_uoc(course_name: str, courses: list[Course]):
Expand Down Expand Up @@ -132,11 +132,11 @@ def assert_autoplanning_guarantees(uoc_max: list[int], courses: list[Course], pr
course_names = [course[0] for course in res if terms_between((2020, 0), course[1]) == index]
assert number >= sum(get_uoc(course_name, courses) for course_name in course_names)
# all courses valid
plan: list[list[dict[str, tuple[int, Optional[int]]]]] = [[{}, {}, {}, {}] for _ in range(2023-2020)]
plan: RawUserPlan = [[{}, {}, {}, {}] for _ in range(2023-2020)]
for course_name, (course_year, course_term) in res:
plan[course_year - 2020][course_term][course_name] = (get_uoc(course_name, courses), get_mark(course_name, courses))
assert all(course_state['is_accurate'] for course_state in validate_terms(ValidPlannerData(
assert all(course_state.is_accurate for course_state in validate_terms(
programCode="3778",
specialisations=["COMPA1"],
specs=["COMPA1"],
plan=plan
)).values())
).values())
29 changes: 17 additions & 12 deletions backend/algorithms/validate_term_planner.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
from typing import Optional
from algorithms.objects.user import User
from server.routers.model import CACHED_HANDBOOK_NOTE, CONDITIONS, ValidPlannerData
from server.routers.model import CACHED_HANDBOOK_NOTE, CONDITIONS, CourseState


def validate_terms(data: ValidPlannerData):
RawUserPlan = list[list[dict[str, tuple[int, Optional[int]]]]]

def validate_terms(programCode: str, specs: list[str], plan: RawUserPlan) -> dict[str, CourseState]:
"""Validates the term planner, returning all warnings."""
emptyUserData = {
"program": data.programCode,
"specialisations": data.specialisations,
"program": programCode,
"specialisations": specs[:],
"courses": {}, # Start off the user with an empty year
}
user = User(emptyUserData)
# State of courses on the term planner
coursesState = {}
coursesState: dict[str, CourseState] = {}

for year in data.plan:
for year in plan:
# Go through all the years
for term in year:
user.add_current_courses(term)
Expand All @@ -24,12 +28,13 @@ def validate_terms(data: ValidPlannerData):
if is_answer_accurate
else (True, [])
)
coursesState[course] = {
"is_accurate": is_answer_accurate,
"handbook_note": CACHED_HANDBOOK_NOTE.get(course, ""),
"unlocked": unlocked,
"warnings": warnings
}
coursesState[course] = CourseState(
is_accurate=is_answer_accurate,
handbook_note=CACHED_HANDBOOK_NOTE.get(course, ""),
unlocked=unlocked,
warnings=warnings
)

# Add all these courses to the user in preparation for the next term
user.empty_current_courses()
user.add_courses(term)
Expand Down
136 changes: 136 additions & 0 deletions backend/server/example_input/example_local_storage_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,5 +185,141 @@
"isSummerEnabled": false,
"lockedTerms": {}
}
},
"sample_user_1": {
"degree": {
"programCode": "3778",
"specs": [
"COMPA1",
"ACCTA2"
]
},
"planner": {
"years": [ {
"T0": [],
"T1": ["COMP1511"],
"T2": ["COMP1521"],
"T3": []
},
{
"T0": [],
"T1": [],
"T2": [],
"T3": []
},
{
"T0": [],
"T1": [],
"T2": [],
"T3": []
}
],
"unplanned": [],
"startYear": 2020,
"isSummerEnabled": false,
"lockedTerms": {}
}
},
"sample_user_2": {
"degree": {
"programCode": "3789",
"specs": [
"COMPJ1",
"FOODH1"
]
},
"planner": {
"years": [ {
"T0": [],
"T1": ["COMP1511"],
"T2": ["COMP1521", "COMP1531"],
"T3": ["COMP2521"]
},
{
"T0": [],
"T1": ["COMP3231"],
"T2": ["COMP9242"],
"T3": []
},
{
"T0": [],
"T1": [],
"T2": [],
"T3": []
}
],
"unplanned": [],
"startYear": 2020,
"isSummerEnabled": false,
"lockedTerms": {}
}
},
"sample_user_one_math": {
"degree": {
"programCode": "3707",
"specs": [
"COMPA1",
"ACCTA2"
]
},
"planner": {
"years": [ {
"T0": [],
"T1": ["MATH1131"],
"T2": [],
"T3": []
},
{
"T0": [],
"T1": [],
"T2": [],
"T3": []
},
{
"T0": [],
"T1": [],
"T2": [],
"T3": []
}
],
"unplanned": [],
"startYear": 2020,
"isSummerEnabled": false,
"lockedTerms": {}
}
},
"sample_user_no_courses": {
"degree": {
"programCode": "3707",
"specs": [
"COMPA1",
"ACCTA2"
]
},
"planner": {
"years": [ {
"T0": [],
"T1": [],
"T2": [],
"T3": []
},
{
"T0": [],
"T1": [],
"T2": [],
"T3": []
},
{
"T0": [],
"T1": [],
"T2": [],
"T3": []
}
],
"unplanned": [],
"startYear": 2020,
"isSummerEnabled": false,
"lockedTerms": {}
}
}
}
Loading
Loading