Skip to content

Algorithms

JamesyJi edited this page Oct 15, 2021 · 5 revisions

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.

Contents

Prior Reading

In order to understand this, you should ensure you have read:

  • Tokenising Requirements
  • Manual Fixes to Course Prerequisites

Overview

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)

Classes

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:

  1. Create UOCCondition with 12UOC
  2. Attach LevelCourseCategory with L2 and MATH
  3. 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
  4. 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.

unselect_course() and problems loading condition objects

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.

Creating a Condition from Tokens

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.

Clone this wiki locally