-
Notifications
You must be signed in to change notification settings - Fork 12
Algorithms
Note to future teams: These algorithms will require a lot of tweaking to adapt to constantly changing requirements in the future. It is possible that some information is out of date so make sure to check the page history and constantly update this wiki.
In order to understand this, you should ensure you have read:
- Tokenising Requirements
- Manual Fixes to Course Prerequisites
The goal of the algorithms is to determine if a specific course has been unlocked for the user based on their taken program, specialisation, taken courses, uoc, wam, and much more. It has been implemented via a bunch of Condition and Category classes which we will go into more detail later.
The tokenised requirements for a course are passed into create_condition
along with the course the condition belongs to. A Condition object is returned from which we can pass in a User object to the is_unlocked() method to determine if the user can or cannot take this course. Here is an example:
user = User(some_user_data)
comp2521_tokens = ["(", "COMP1511", "||", "DPST1091", "||", "COMP1917", "||", "COMP1921", ")"]
comp2521_cond = create_condition(comp2521_tokens, "COMP2521")
res = comp2521_cond.is_unlocked(user)
res["result"] # True/False if this course can be taken by the user
res["warnings"] # List of warnings (for now, it only applies to GRADE and WAM requirements)
Inside conditions.py and categories.py, you can find implementations for many different Condition and Category classes. The exact details are best learnt via reading through the code and playing around with it on a separate branch.
Many courses have requirements with the "in" keyword. E.g. "12UOC in L2 MATH". For this reason, the Category class was developed. Some Condition might contain a Category. Again, it is best to read the code but a brief overview of this logic is:
- Create UOCCondition with 12UOC
- Attach LevelCourseCategory with L2 and MATH
- The Category Class has methods to get the UOC the user has taken belonging to that category (in this case, the UOC of courses beginning with MATH2
- If >= 12UOC in this category, the UOCCondition validates as true
There is also a User class inside conditions.py. This contains all the relevant data and methods that might be useful for a user. The frontend passes some data to our API which then creates a User from that data. We then use the User to check if a course can be taken or not.
This is the one tricky method in User. The idea is, when someone unselects a course on the frontend, we need to return a list of courses that could potentially be affected. E.g. unselecting COMP1511 would "affect" COMP2521 since COMP1511 is a prerequisite of COMP2521.
You might already realise that any implementation would involve having access to the Condition objects for the courses. But where do we get these Condition objects? There is a conditions.pkl file which stores all the Conditions but due to the problems with pickle and importing*. A workaround I've done is to load the condition tokens, then when the unselect_course() method is called, use create_condition() to create the necessary condition objects. Please have a read of the code.
*Read up on this if you're interested, I think the solution requires separating the User from conditions.py or with absolute path imports or something but then you'd have to make sure this works with docker and etc my brain is too small and there is no time someone in the future pls fix this.
Have a read through the create_condition
function. Tokens are read from left to right and then Conditions and Categories are created recursively. We can attach an optional parameter that is the course this condition belongs to. This is needed to check for exclusions since we must know the course in order to know what it excludes.