pub struct Course {
pub csv_id: Option<String>,
pub id: Option<String>,
pub name: String,
pub prefix: String,
pub number: String,
pub prerequisites: Vec<String>,
pub corequisites: Vec<String>,
pub strict_corequisites: Vec<String>,
pub credit_hours: f32,
pub canonical_name: Option<String>,
}Expand description
Represents a course in a curriculum
§Note on Complex Prerequisites
Currently, prerequisites are stored as a flat list with implicit AND semantics. However, real curricula often have complex boolean expressions like:
- “CS101 OR CS102”
- “(CS101 AND MATH156) OR CS200”
- “CS101 OR (CS102 AND MATH156)”
Several approaches to handle complex prerequisites:
-
Disjunctive Normal Form (DNF): Store as
Vec<Vec<String>>where outer Vec is OR, inner Vec is AND. Example:[[\"CS101\"], [\"CS102\", \"MATH156\"]]means CS101 OR (CS102 AND MATH156). This is a standard form in logic. -
Prerequisite Expression Trees: Use a recursive enum:
ⓘenum PrereqExpr { Course(String), And(Vec<PrereqExpr>), Or(Vec<PrereqExpr>), }This can represent any boolean expression and is most flexible.
-
Virtual Courses: Create synthetic course keys like
\"CS101_OR_CS102\"or\"CS101_AND_MATH156\"in the DAG. Each virtual course represents a requirement that can be satisfied by its components. -
Hypergraph Representation: Extend DAG to support hyperedges where a single edge can connect to multiple prerequisite sets with boolean operators.
-
Choice Resolution at Plan Build Time (Recommended for Plan Analysis): The Course struct stores the full prerequisite expression (using one of the above approaches), but when building a DAG from a Plan, the plan specifies which alternative was chosen. This keeps plan DAGs simple while preserving the full requirement information in courses. Plans represent actual student selections where boolean logic has been resolved.
Note: Regardless of approach, the Course struct must be able to represent the full prerequisite expression. The choice resolution approach means that when analyzing a specific plan, we only include the paths the student actually took, not all possibilities.
Fields§
§csv_id: Option<String>Original course ID from the curriculum file
id: Option<String>Unique course identifier (optional, used for deduplication when multiple courses have same key)
name: StringCourse name (e.g., “Calculus for Physical Scientists I”)
prefix: StringCourse prefix (e.g., “MATH”, “CS”)
number: StringCourse number (e.g., “1342”, “2510”)
prerequisites: Vec<String>Prerequisites - stored as “PREFIX NUMBER” keys (e.g., “MATH 1341”) Currently assumes ALL prerequisites must be satisfied (AND semantics)
corequisites: Vec<String>Co-requisites - stored as “PREFIX NUMBER” keys
strict_corequisites: Vec<String>Strict co-requisites - stored as “PREFIX NUMBER” keys (must be taken together)
credit_hours: f32Credit hours (can be fractional)
canonical_name: Option<String>Canonical name for cross-institution lookup (e.g., “Calculus I”)
Implementations§
Source§impl Course
impl Course
Sourcepub const fn new(
name: String,
prefix: String,
number: String,
credit_hours: f32,
) -> Self
pub const fn new( name: String, prefix: String, number: String, credit_hours: f32, ) -> Self
Create a new course
§Arguments
name- Full course nameprefix- Course prefixnumber- Course numbercredit_hours- Credit hours (can be fractional)
Sourcepub fn key(&self) -> String
pub fn key(&self) -> String
Get the course key for lookups (prefix + number)
§Returns
A string in the format “PREFIXNUMBER” (e.g., “CS2510”)
Sourcepub fn add_prerequisite(&mut self, prereq_key: String)
pub fn add_prerequisite(&mut self, prereq_key: String)
Add a prerequisite by course key
Sourcepub fn add_corequisite(&mut self, coreq_key: String)
pub fn add_corequisite(&mut self, coreq_key: String)
Add a co-requisite by course key
Sourcepub fn add_strict_corequisite(&mut self, coreq_key: String)
pub fn add_strict_corequisite(&mut self, coreq_key: String)
Add a strict co-requisite by course key
Sourcepub fn set_canonical_name(&mut self, name: String)
pub fn set_canonical_name(&mut self, name: String)
Set the canonical name