A Flask-based REST API for checking degree completion and managing degree audit data for Yale University academic programs.
All endpoints (except /health) require authentication via the X-Student-NetID header. The server validates that:
- The header is present in the request
- The NetID exists in the database
- The student is logged in (has an active session)
Example request:
curl -X GET \
'http://localhost:5000/api/student' \
-H 'X-Student-NetID: abc123'Failed authentication will result in one of these responses:
- Missing header: 401 Unauthorized with message "Authentication required"
- Invalid NetID: 401 Unauthorized with message "User not found"
- User not logged in: 401 Unauthorized with message "User is not logged in"
The application follows a layered architecture with clear separation of concerns:
-
Models (Schemas)
- Implemented using Pydantic
- Data validation and type checking
- API contract definition
-
Repositories
- Database access abstraction
- CRUD operations
- Query building
-
Services
- Business logic encapsulation
- Coordination between repositories
- Complex operations
-
API Routes
- HTTP request handling
- Input validation
- Response formatting
yale-degree-audit/
├── app.py # Application entry point
├── config.py # Configuration handling
├── Dockerfile # Docker configuration
├── docker-compose.yml # Docker Compose setup
├── requirements.txt # Dependencies
├── .env.example # Environment variable example
│
├── api/ # API routes
│ ├── __init__.py
│ ├── degree_audit.py # Degree audit endpoints (/api/degree-audit)
│ ├── majors.py # Major-related endpoints (/api/major/*)
│ ├── courses.py # Course-related endpoints (/api/course/*)
│ ├── students.py # Student-related endpoints (/api/student/*)
│ └── distributions.py # Distribution requirements endpoints (/api/distribution-requirement/*)
│
├── models/ # Pydantic models (schemas)
│ ├── __init__.py
│ ├── course.py # Course-related schemas
│ ├── degree_audit.py # Degree audit schemas
│ ├── major.py # Major-related schemas
│ └── student.py # Student-related schemas
│
├── repositories/ # Database access layer
│ ├── __init__.py
│ ├── base.py # Base repository class
│ ├── course_repository.py # Course data access
│ ├── major_repository.py # Major data access
│ ├── student_repository.py # Student data access
│ └── distribution_repository.py # Distribution requirements data access
│
├── services/ # Business logic layer
│ ├── __init__.py
│ ├── course_service.py # Course-related logic
│ ├── degree_audit_service.py # Degree audit logic
│ ├── major_service.py # Major-related logic
│ ├── student_service.py # Student-related logic
│ └── distribution_service.py # Distribution requirements logic
│
└── utils/ # Utility functions
├── __init__.py
├── grade_utils.py # Grade calculation utilities
└── auth.py # Authentication utilities
-
GET /health- Check if the service is running- No authentication required
- Returns service status and name
Example response:
{ "status": "healthy", "service": "Yale Degree Audit API", "version": "1.0.0" }
-
GET /api/degree-audit- Check if a student has completed their major requirements- Required Header:
X-Student-NetID - Returns completion status and unfulfilled requirements
Example response for student 'abc123' (Computer Science major):
{ "student_id": "abc123", "major": { "name": "Computer Science", "requirements": { "prerequisites": { "completed": true, "courses": ["CPSC 112", "CPSC 201", "MATH 112"] }, "core": { "completed": false, "courses": { "completed": ["CPSC 223", "CPSC 323"], "remaining": ["CPSC 365"] } }, "electives": { "completed": false, "courses": { "completed": ["CPSC 419"], "remaining_count": 5 } } } }, "distribution_requirements": { "year": "Sophomore", "status": "In Progress", "details": { "skills": { "QR": {"required": 2, "completed": 2}, "WR": {"required": 2, "completed": 1}, "L": {"required": 1, "completed": 0} }, "disciplinary": { "Hu": {"required": 2, "completed": 1}, "Sc": {"required": 2, "completed": 2}, "So": {"required": 2, "completed": 1} } } } } - Required Header:
-
GET /api/distribution-requirement- Get a student's overall distribution requirements status- Required Header:
X-Student-NetID
Example response for student 'abc123':
{ "student_id": "abc123", "current_year": "Sophomore", "overall_status": { "skills": { "QR": { "required": 2, "completed": 2, "courses": ["CPSC 112", "MATH 112"] }, "WR": { "required": 2, "completed": 1, "courses": ["ENGL 114"] }, "L": { "required": 1, "completed": 0, "courses": [] } }, "disciplinary": { "Hu": { "required": 2, "completed": 1, "courses": ["ENGL 115"] }, "Sc": { "required": 2, "completed": 2, "courses": ["CPSC 201", "CPSC 223"] }, "So": { "required": 2, "completed": 1, "courses": ["PLSC 101"] } } } } - Required Header:
-
GET /api/distribution-requirement/{year}- Get requirements for a specific year- Required Header:
X-Student-NetID
Example response for student 'abc123', year 'Freshman':
{ "student_id": "abc123", "year": "Freshman", "requirements": { "rule": "Complete courses in 2 of 3 skills categories", "progress": { "QR": { "required": 1, "completed": 1, "courses": ["CPSC 112"] }, "WR": { "required": 1, "completed": 1, "courses": ["ENGL 114"] }, "L": { "required": 1, "completed": 0, "courses": [] } }, "status": "Completed", "details": "Completed 2 of 3 required skills categories (QR, WR)" } } - Required Header:
-
GET /api/major- Get all available majors- Required Header:
X-Student-NetID
Example response:
{ "majors": [ { "major_id": 101, "major_name": "Computer Science", "major_code": "CPSC", "department": "Department of Computer Science", "description": "The Computer Science major is designed to develop skills in all major areas of computer science while permitting flexibility in exploring particular areas of interest." }, { "major_id": 102, "major_name": "English Language and Literature", "major_code": "ENGL", "department": "Department of English", "description": "The English major offers a rich and diverse curriculum exploring the history of literature written in English and introducing students to a variety of methods for critical analysis and interpretation." } ] } - Required Header:
-
GET /api/major/{major_id}/requirements- Get major requirements- Required Header:
X-Student-NetID
Example response for Computer Science (major_id: 101):
{ "major_name": "Computer Science", "catalog_year": 2023, "requirements": { "prerequisites": { "name": "Prerequisites", "courses": [ { "code": "CPSC 112", "name": "Introduction to Programming" }, { "code": "CPSC 201", "name": "Introduction to Computer Science" }, { "code": "MATH 112", "name": "Calculus I" } ] }, "core": { "name": "Core Requirements", "courses": [ { "code": "CPSC 223", "name": "Data Structures and Programming Techniques" }, { "code": "CPSC 323", "name": "Systems Programming and Computer Organization" }, { "code": "CPSC 365", "name": "Design and Analysis of Algorithms" } ] }, "electives": { "name": "Advanced Electives", "min_courses": 6, "rules": [ "At least 4 courses must be at 400-level or above", "At least one theory course is required" ] } } } - Required Header:
-
GET /api/course- Get list of courses- Required Header:
X-Student-NetID
Example response:
{ "page": 1, "per_page": 2, "total": 24, "courses": [ { "course_id": 401, "subject_code": "CPSC", "course_number": "112", "course_title": "Introduction to Programming", "description": "An introduction to the concepts, techniques, and applications of computer programming and software development.", "credits": 1.0, "distribution": "QR" }, { "course_id": 402, "subject_code": "CPSC", "course_number": "201", "course_title": "Introduction to Computer Science", "description": "Introduction to the concepts and techniques of computer science.", "credits": 1.0, "distribution": "QR" } ] } - Required Header:
-
GET /api/course/{course_id}- Get course details- Required Header:
X-Student-NetID
Example response for CPSC 223 (course_id: 406):
{ "course_id": 406, "subject_code": "CPSC", "course_number": "223", "course_title": "Data Structures and Programming Techniques", "description": "Organization of data, algorithms, techniques, and classes.", "credits": 1.0, "distribution": "QR", "prerequisites": [ { "course_id": 401, "code": "CPSC 112", "title": "Introduction to Programming" }, { "course_id": 402, "code": "CPSC 201", "title": "Introduction to Computer Science" } ] } - Required Header:
-
GET /api/student- Get student information- Required Header:
X-Student-NetID
Example response for 'abc123':
{ "student_id": 1001, "net_id": "abc123", "first_name": "Alice", "last_name": "Brown", "class_year": 2026, "email": "alice.brown@yale.edu", "majors": [ { "major_name": "Computer Science", "is_primary": true, "declaration_date": "2023-05-15" } ] } - Required Header:
-
GET /api/student/enrollments- Get student's course enrollments- Required Header:
X-Student-NetID
Example response for 'abc123':
{ "student_id": "abc123", "enrollments": { "completed": [ { "course_code": "CPSC 112", "title": "Introduction to Programming", "term": "Fall 2022", "grade": "A", "credits": 1.0 }, { "course_code": "MATH 112", "title": "Calculus I", "term": "Fall 2022", "grade": "A-", "credits": 1.0 } ], "current": [ { "course_code": "CPSC 414", "title": "Web Programming", "term": "Fall 2024", "status": "Enrolled" }, { "course_code": "CPSC 408", "title": "Algorithms", "term": "Fall 2024", "status": "Enrolled" } ] } } - Required Header:
-
GET /api/student/gpa- Get student's GPA- Required Header:
X-Student-NetID
Example response for 'abc123':
{ "student_id": "abc123", "overall_gpa": 3.83, "by_term": [ { "term": "Fall 2022", "gpa": 4.0, "credits": 2.0 }, { "term": "Spring 2023", "gpa": 3.67, "credits": 2.0 } ] } - Required Header:
- Python 3.10 or higher
- Supabase account and project
-
Clone the repository:
git clone https://github.com/yourusername/yale-degree-audit.git cd yale-degree-audit -
Create a virtual environment and install dependencies:
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate pip install -r requirements.txt
-
Create a
.envfile from the example:cp .env.example .env
-
Edit the
.envfile with your Supabase credentials and other configuration -
Run the application:
python app.py
-
Clone the repository:
git clone https://github.com/yourusername/yale-degree-audit.git cd yale-degree-audit -
Create a
.envfile from the example:cp .env.example .env
-
Edit the
.envfile with your Supabase credentials and other configuration -
Build and run with Docker Compose:
docker-compose up -d
-
Access the API at http://localhost:5000
The application uses a PostgreSQL database with the following structure:
https://docs.google.com/document/d/1NB3KsjMmYX4DS3ibNFB1wk3rresBxfEMYK4Wrq08TBw/edit?tab=t.0 https://docs.google.com/document/d/1ZIA1XXF8_w1Nh0yC7caXWX4dWAY3P1PXAfbwgTO5NHo/edit?tab=t.0
Students
- Primary table for student information
- Fields:
student_id(PK): Unique identifiernet_id: Yale NetID (unique)first_name,last_name: Student's nameclass_year: Expected graduation yearemail: Yale email address
- Used for authentication and basic student information
Majors
- Defines available majors at Yale
- Fields:
major_id(PK): Unique identifiermajor_name: Full name of the majormajor_code: Department code (e.g., "CPSC")department: Department namedescription: Detailed major description
MajorVersions
- Tracks different versions of major requirements over time
- Fields:
major_version_id(PK): Unique identifiermajor_id(FK): References Majorscatalog_year: Academic year of this versioneffective_term,valid_until_term: Validity periodis_active: Whether this version is currentnotes: Changes or special considerations
StudentMajors
- Links students to their declared majors
- Fields:
student_major_id(PK): Unique identifierstudent_id(FK): References Studentsmajor_version_id(FK): References MajorVersionsdeclaration_date: When the major was declaredis_primary_major: Boolean for primary/secondary status
Courses
- Comprehensive course catalog
- Fields:
course_id(PK): Unique identifiersubject_code: Department codecourse_number: Course number within departmentcourse_title: Full course namedescription: Course descriptioncredits: Number of creditsdistribution: Distribution requirement codes
StudentCourseEnrollments
- Tracks completed and current courses
- Fields:
enrollment_id(PK): Unique identifierstudent_id(FK): References Studentscourse_id(FK): References Coursesterm_taken: Academic termgrade: Course gradestatus: 'Completed', 'Enrolled', or 'Withdrawn'
StudentCoursePlans
- Future course planning
- Fields:
plan_id(PK): Unique identifierstudent_id(FK): References Studentscourse_id(FK): References Coursesintended_term: Planned termpriority: Student's priority levelnotes: Planning notes
MajorRequirements
- Top-level requirements for each major
- Fields:
requirement_id(PK): Unique identifiermajor_version_id(FK): References MajorVersionsrequirement_name: Name of requirementrequirement_type: 'Prerequisite', 'Core', 'Elective', or 'Capstone'min_courses,max_courses: Course count limitsmin_credits,max_credits: Credit limits
RequirementGroups
- Subdivides requirements into specific groups
- Fields:
requirement_group_id(PK): Unique identifierrequirement_id(FK): References MajorRequirementsgroup_name: Name of the groupgroup_operator: 'AND' or 'OR'min_courses_in_group,max_courses_in_group: Course limitsgroup_description: Detailed description
RequirementGroupCourses
- Maps courses to requirement groups
- Fields:
req_group_course_id(PK): Unique identifierrequirement_group_id(FK): References RequirementGroupscourse_id(FK): References Coursesis_required_in_group: Whether course is mandatory
CoursePrerequisites
- Defines prerequisite relationships
- Fields:
course_id(FK): The main courseprereq_course_id(FK): The prerequisite courseconcurrency_allowed: Can be taken simultaneously
EquivalenceGroups
- Defines groups of equivalent courses
- Fields:
eq_group_id(PK): Unique identifiergroup_name: Name of the equivalence groupgroup_notes: Additional information
EquivalenceGroupCourses
- Maps courses to equivalence groups
- Fields:
eq_group_course_id(PK): Unique identifiereq_group_id(FK): References EquivalenceGroupscourse_id(FK): References Courses
DistributionTypes
- Defines types of distribution requirements
- Fields:
distribution_id(PK): Unique identifiercode: Short code (e.g., "QR", "WR")name: Full namedescription: Detailed descriptioncategory: Either 'skills' or 'disciplinary'
AcademicYears
- Defines the four academic years
- Fields:
year_id(PK): Unique identifiername: Year name (e.g., "Freshman")display_order: Ordering (1-4)description: Year-specific information
DistributionRequirements
- Maps requirements to academic years
- Fields:
requirement_id(PK): Unique identifieryear_id(FK): References AcademicYearsdistribution_id(FK): References DistributionTypescourses_required: Number of courses neededactive: Whether requirement is currentcreated_at,updated_at: Timestamps
YearRequirementRules
- Special rules for distribution requirements
- Fields:
rule_id(PK): Unique identifieryear_id(FK): References AcademicYearsrule_type: Type of rulevalue: Numeric requirementcategory: Applies to 'skills' or 'disciplinary'active: Whether rule is current
- Foreign Key Constraints: Ensure referential integrity between tables
- Unique Constraints: Prevent duplicate entries (e.g., NetIDs)
- Check Constraints: Validate data (e.g., grade values)
- Default Values: Automatic timestamps and boolean flags
- Indexes: Optimized for common queries and joins
-
Create a new branch for your feature:
git checkout -b feature/your-feature-name
-
Follow the project structure:
- Add new models in
models/ - Add new repository methods in
repositories/ - Implement business logic in
services/ - Create API endpoints in
api/
- Add new models in
-
Write tests for your changes:
python -m pytest tests/
-
Submit a pull request with:
- Clear description of changes
- Test results
- Any database migrations
- Documentation updates
- Follow PEP 8 guidelines
- Use type hints
- Document functions and classes
- Keep functions focused and small
- Use meaningful variable names
-
Run unit tests:
python -m pytest tests/unit/
-
Run integration tests:
python -m pytest tests/integration/
-
Check code coverage:
python -m pytest --cov=app tests/
The API is deployed at: https://major-audit-api-c946fe6486c8.herokuapp.com/
-
Install Heroku CLI:
brew install heroku/brew/heroku
-
Login to Heroku:
heroku login
-
Create Heroku app:
heroku create major-audit-api
-
Set environment variables:
heroku config:set FLASK_ENV=production heroku config:set SECRET_KEY=your-secret-key heroku config:set SUPABASE_URL=your-supabase-url heroku config:set SUPABASE_KEY=your-supabase-key
-
Deploy:
git push heroku main
Required environment variables:
FLASK_ENV: Set to 'development' or 'production'SECRET_KEY: Flask secret keySUPABASE_URL: Supabase project URLSUPABASE_KEY: Supabase API keyPORT: Port number (set by Heroku)
-
Health Checks
- Endpoint:
/health - Monitors: Service status, database connection, Supabase connection
- Response time tracking
- Endpoint:
-
Error Tracking
- Automatic error logging
- Stack traces for debugging
- Error categorization
-
Performance Metrics
- Request duration
- Database query times
- Memory usage
- CPU utilization
-
Log Levels
- ERROR: Application errors
- WARNING: Potential issues
- INFO: General operations
- DEBUG: Detailed debugging
-
Log Format
[TIMESTAMP] [LEVEL] [REQUEST_ID] Message -
Log Storage
- Production: Heroku logging
- Development: Local files
- Log rotation enabled
-
Authentication
- NetID validation
- Session management
- Rate limiting
-
Data Protection
- HTTPS enforcement
- Input validation
- SQL injection prevention
- XSS protection
-
Access Control
- Role-based permissions
- API key management
- IP whitelisting
-
Technical Enhancements
- Implement caching for frequently accessed data
- Add WebSocket support for real-time updates
- Implement GraphQL API
- Add batch operations for better performance
-
Feature Additions
- Course scheduling optimization
- Graduation path recommendations
- Integration with Yale Course Search
- Mobile app support
- PDF report generation
-
Security Improvements
- OAuth 2.0 implementation
- Two-factor authentication
- Enhanced audit logging
- Automated security scanning
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests
- Submit a pull request
For major changes:
- Open an issue first
- Discuss the proposed changes
- Get approval before proceeding
For support:
- Check the documentation
- Search existing issues
- Open a new issue if needed
- Contact: [Your Contact Information]
[Add License Information]