Prerequisites are handled at the course level, allowing for flexible and accurate prerequisite validation that respects equivalencies across institutions.
Available types:
simple = pool of courses (choose any to meet credit goal)grouped = multiple mandatory subdivisions (must satisfy ALL groups)Prerequisites are specified per-course in the Course model, not as a requirement type.
Course Model (backend/models/course.py):
class Course(db.Model):
# ... other fields ...
prerequisites = db.Column(db.Text) # Comma-separated list of course codes
CSV Format (courses CSV):
code,title,description,credits,institution,department,prerequisites,has_lab,course_type
MATH 152,Calculus II,,3,State University,Math,MATH 151,false,lecture
MATH 251,Calculus III,,3,State University,Math,MATH 152,false,lecture
CSCI 2001,Data Structures,,3,State University,CS,"CSCI 1583, MATH 151",false,lecture
Location: backend/services/prerequisite_service.py
Key Features:
Handles various formats:
"MATH 101" → single prerequisite"MATH 101, BIOL 200" → multiple prerequisites (AND logic)"MATH101" → normalizes to "MATH 101"If Course A at School 1 = Course B at School 2 (equivalent), and Course B is a prerequisite for Course D, then Course A satisfies the prerequisite for Course D.
Example:
School 1: MATH 101 (Calc I)
School 2: CALC 1 (Calc I)
Equivalency: MATH 101 = CALC 1
School 2: CALC 2 requires CALC 1
Student completed: MATH 101 at School 1
Result: Student CAN take CALC 2 because MATH 101 is equivalent to CALC 1
Uses BFS (Breadth-First Search) to find all transitively equivalent courses:
Location: backend/routes/prerequisites.py
GET /api/prerequisites/check/<course_code>?plan_id=123
Returns:
{
"can_take": true,
"missing_prerequisites": [],
"satisfied_prerequisites": ["MATH 101"],
"all_prerequisites": ["MATH 101"]
}
GET /api/prerequisites/details/<course_code>
Returns:
{
"course_code": "MATH 201",
"course_title": "Calculus II",
"found": true,
"prerequisites": ["MATH 101"],
"prerequisite_details": [
{
"required_code": "MATH 101",
"equivalent_codes": ["MATH 101", "CALC 1"]
}
]
}
GET /api/prerequisites/validate-plan/<plan_id>
Returns list of courses with prerequisite violations.
GET /api/prerequisites/suggest-next-courses/<plan_id>?limit=20
Returns courses the student can take based on completed prerequisites.
program_name,category,requirement_type,group_name,course_code
"Biology B.S.","Math Sequence",simple,"Math Options",MATH 151
"Biology B.S.","Math Sequence",simple,"Math Options",MATH 152
"Biology B.S.","Math Sequence",simple,"Math Options",MATH 251
code,title,prerequisites
MATH 151,Calculus I,
MATH 152,Calculus II,MATH 151
MATH 251,Calculus III,MATH 152
Import the service:
from backend.services.prerequisite_service import PrerequisiteService
Validate prerequisites:
# Check if a student can take a course
PrerequisiteService.validate_prerequisites(course_code, completed_courses, institution)
# Get prerequisite details for display
PrerequisiteService.get_prerequisite_details(course_code)
Check prerequisites:
GET /api/prerequisites/check/<course_code>?plan_id=123
Display prerequisite information:
Get course suggestions:
GET /api/prerequisites/suggest-next-courses/<plan_id>
Test File: backend/tests/test_prerequisites.py
Coverage:
Run Tests:
cd backend
pytest tests/test_prerequisites.py -v
Requirements CSV:
requirement_type must be simple or groupedconditional is specified, upload will fail with error message:
'conditional' requirement type is no longer supported.
Prerequisites are now handled at the course level via the
'prerequisites' field in courses. Please use 'simple' or 'grouped'
requirement types.
Courses CSV:
prerequisites field is optional"MATH 101, CSCI 1583"Prerequisite Checking:
backend/services/prerequisite_service.py
└── PrerequisiteService (static methods)
├── parse_prerequisites()
├── get_equivalent_courses()
├── get_all_transitive_equivalents()
├── check_prerequisite_satisfied()
├── validate_prerequisites()
└── get_prerequisite_details()
backend/routes/prerequisites.py
└── Blueprint: /api/prerequisites
├── GET /check/<course_code>
├── GET /details/<course_code>
├── GET /validate-plan/<plan_id>
└── GET /suggest-next-courses/<plan_id>
Course
├── prerequisites: Text (comma-separated course codes)
└── Used by: PrerequisiteService
Equivalency
├── from_course_id → Course
└── to_course_id → Course
Used for: Transitive equivalency resolution
PlanCourse
├── status: 'completed', 'in_progress', 'planned'
└── Used for: Determining which prerequisites are satisfied
# Student completed MATH 101
# Want to check if they can take MATH 201
result = PrerequisiteService.validate_prerequisites(
'MATH 201',
completed_courses=[plan_course_with_math101]
)
# result = {
# 'can_take': True,
# 'satisfied_prerequisites': ['MATH 101'],
# 'missing_prerequisites': [],
# 'all_prerequisites': ['MATH 101']
# }
# Student completed CALC 1 at School 2
# CALC 1 is equivalent to MATH 101 at School 1
# Want to check if they can take MATH 201 at School 1
result = PrerequisiteService.validate_prerequisites(
'MATH 201', # Requires MATH 101
completed_courses=[plan_course_with_calc1]
)
# result = {
# 'can_take': True, # Because CALC 1 = MATH 101
# 'satisfied_prerequisites': ['MATH 101'],
# 'missing_prerequisites': [],
# 'all_prerequisites': ['MATH 101']
# }
# Student has no completed courses
# Want to check if they can take MATH 301
result = PrerequisiteService.validate_prerequisites(
'MATH 301', # Requires MATH 201
completed_courses=[]
)
# result = {
# 'can_take': False,
# 'satisfied_prerequisites': [],
# 'missing_prerequisites': ['MATH 201'],
# 'all_prerequisites': ['MATH 201']
# }
Potential additions: