Skip to main content

An API for retrieving public data from the University of Ottawa and Carleton University.

Project description

Schedulo API

A REST API server for retrieving public data from Canadian universities, including the University of Ottawa and Carleton University.

This package features a completely refactored, clean architecture with improved maintainability and extensibility.

PyPI version Python 3.10+

โœจ Features

  • ๐Ÿซ Multi-University Support: University of Ottawa and Carleton University
  • ๐Ÿ“š Complete Course Data: Catalogs, timetables, prerequisites, components
  • ๐ŸŽ“ Academic Programs: 840+ programs with filtering, search, and bulk export
  • โšก Live Timetable Data: Real-time course availability and scheduling
  • โญ Rate My Professor Integration: Professor ratings for both universities
  • ๐Ÿš€ FastAPI REST API: Complete HTTP API with interactive documentation
  • ๐Ÿ“ฆ Laravel Integration: Bulk program export for database seeding
  • ๐Ÿ”ง Clean Architecture: Layered design with proper separation of concerns
  • ๐Ÿ Python Library: Programmatic access to services for building your own applications
  • ๐Ÿ“ Type Safety: Full type annotations with Pydantic models

๐Ÿ—๏ธ New Architecture

The package has been completely refactored with a clean layered architecture:

uoapi/
โ”œโ”€โ”€ core/                    # Domain models & interfaces
โ”œโ”€โ”€ universities/           # University-specific implementations
โ”œโ”€โ”€ services/              # Business logic layer
โ”œโ”€โ”€ server/                # REST API server
โ””โ”€โ”€ utils/                # Shared utilities

Key Benefits:

  • โœ… Single Responsibility: Each module has one clear purpose
  • โœ… Consistent Models: Unified data structures across universities
  • โœ… Easy Extension: Add new universities by implementing simple interfaces
  • โœ… Better Testing: Clear boundaries enable comprehensive testing
  • โœ… Type Safety: Full type annotations throughout

๐Ÿš€ Quick Start

Installation

# From PyPI (Recommended)
pip install schedulo-api

# From Source
pip install git+https://github.com/Rain6435/uoapi.git@dev

# Development Installation
git clone https://github.com/Rain6435/uoapi.git
cd uoapi
pip install -e .[tests]

Basic Usage

Starting the Server

# Start the REST API server
schedulo-server --port 8000

# With custom options
schedulo-server --host 0.0.0.0 --port 8080 --workers 4

# Development mode with auto-reload
schedulo-server --reload --log-level debug

# Interactive docs available at:
# http://localhost:8000/docs
# http://localhost:8000/redoc

Programmatic Usage

# Start server programmatically
from uoapi.server.app import create_app
import uvicorn

app = create_app()
uvicorn.run(app, host="127.0.0.1", port=8000)

Using the Python Library

from uoapi.core import University
from uoapi.services import DefaultCourseService, DefaultTimetableService

# Initialize services
course_service = DefaultCourseService()
timetable_service = DefaultTimetableService()

# Get all subjects for a university
subjects = course_service.get_subjects(University.CARLETON)
print(f"Found {len(subjects)} subjects")

# Get courses for a specific subject
courses = course_service.get_courses(University.CARLETON, "COMP")
print(f"Found {len(courses)} COMP courses")

# Search courses
search_result = course_service.search_courses(University.UOTTAWA, "programming")
print(f"Found {search_result.total_found} courses matching 'programming'")

# Get live timetable data
if University.CARLETON in timetable_service.get_supported_universities():
    live_data = timetable_service.get_live_courses(
        University.CARLETON,
        term_code="202501",
        subjects=["COMP"],
        max_courses_per_subject=10
    )
    print(f"Found {live_data.courses_offered} offered courses")

๐Ÿ“– Complete Usage Guide

University Data Access

Course Service

from uoapi.core import University
from uoapi.services import DefaultCourseService

service = DefaultCourseService()

# Get all supported universities
universities = service.get_all_universities()

# Get subjects
subjects = service.get_subjects(University.UOTTAWA)
for subject in subjects[:5]:
    print(f"{subject.code}: {subject.name}")

# Get courses with filtering
courses = service.get_courses(
    University.CARLETON, 
    subject_code="COMP",
    query="database"
)

# Get specific course
course = service.get_course_by_code(University.UOTTAWA, "CSI3140")
print(f"{course.title}: {course.credits} credits")

# Get course statistics
stats = service.get_course_statistics(University.CARLETON)
print(f"Total courses: {stats['total_courses']}")

Timetable Service

from uoapi.services import DefaultTimetableService

service = DefaultTimetableService()

# Check which universities support live data
supported = service.get_supported_universities()
print(f"Live data supported by: {[u.value for u in supported]}")

# Get available terms
terms = service.get_available_terms(University.CARLETON)
for code, name in terms:
    print(f"{code}: {name}")

# Get live course data
result = service.get_live_courses(
    university=University.CARLETON,
    term_code="202501",
    subjects=["COMP", "MATH"],
    max_courses_per_subject=20
)

print(f"Processing time: {result.processing_time:.2f}s")
print(f"Offering rate: {result.offering_rate:.1f}%")

for course in result.courses:
    if course.is_offered:
        print(f"\n{course.course_code}: {course.title}")
        for section in course.sections:
            print(f"  {section.section}: {section.instructor} - {section.status}")
            for mt in section.meeting_times:
                print(f"    {mt.days} {mt.start_time}-{mt.end_time}")

Rating Service

from uoapi.services import DefaultRatingService

service = DefaultRatingService()

# Get individual instructor rating
rating = service.get_instructor_rating("John Smith", University.UOTTAWA)
if rating:
    print(f"Rating: {rating['rating']}/5.0")
    print(f"Difficulty: {rating['avg_difficulty']}/5.0")

# Get batch ratings
instructors = [("Jane", "Doe"), ("John", "Smith")]
ratings = service.get_batch_ratings(instructors, University.CARLETON)

# Enhance courses with ratings
enhanced_courses = service.inject_ratings_into_courses(courses, University.UOTTAWA)

REST API Server

The Schedulo API provides a comprehensive FastAPI-based REST server with interactive documentation, structured responses, and powerful filtering capabilities.

Starting the Server

# Start the server (Recommended)
schedulo-server --port 8000

# With additional options
schedulo-server --host 0.0.0.0 --port 8080 --workers 4

# Development mode
schedulo-server --reload --log-level debug

# Or programmatically
python -c "
from uoapi.server.app import create_app
import uvicorn
app = create_app()
uvicorn.run(app, host='127.0.0.1', port=8000)
"

Interactive Documentation: http://localhost:8000/docs ReDoc Documentation: http://localhost:8000/redoc

Core Endpoints

University Information
# List all supported universities
curl http://localhost:8000/universities

# Get university-specific information
curl http://localhost:8000/universities/carleton/info
curl http://localhost:8000/universities/uottawa/info
Subjects
# Get subjects (preview - first 20)
curl http://localhost:8000/universities/carleton/subjects
curl http://localhost:8000/universities/uottawa/subjects

# Get all subjects (complete catalog)
curl http://localhost:8000/universities/carleton/subjects/catalog
curl http://localhost:8000/universities/uottawa/subjects/catalog
Course Catalog (Static Data)
# Get catalog courses by subject
curl "http://localhost:8000/universities/carleton/courses/catalog?subjects=COMP,MATH&limit=10"
curl "http://localhost:8000/universities/uottawa/courses/catalog?subjects=CSI,MAT&limit=5"

# Get all catalog courses (warning: large response)
curl "http://localhost:8000/universities/carleton/courses/catalog"

# Get single course (catalog data only)
curl http://localhost:8000/universities/carleton/courses/COMP1005
curl http://localhost:8000/universities/uottawa/courses/CSI3140
Live Timetable Data
# Get available terms for live data
curl http://localhost:8000/universities/carleton/terms
curl http://localhost:8000/universities/uottawa/terms

# Multiple courses with live sections
curl "http://localhost:8000/universities/carleton/courses/live?term=fall&year=2025&subjects=COMP,MATH&limit=20&include_ratings=true"
curl "http://localhost:8000/universities/uottawa/courses/live?term=winter&year=2025&subjects=CSI,CEG&limit=10"

# Filter by specific course codes
curl "http://localhost:8000/universities/carleton/courses/live?term=fall&year=2025&subjects=COMP&course_codes=COMP1005,COMP1405"

# Single course with structured sections
curl "http://localhost:8000/universities/carleton/courses/COMP1005/live?term=fall&year=2025&include_ratings=true"

New Single Course Response Structure:

{
  "university": "carleton",
  "term_code": "202530",
  "term_name": "Fall 2025",
  "course": {
    "course_code": "COMP1005",
    "subject_code": "COMP",
    "title": "Programming Concepts",
    "credits": 0.5,
    "is_offered": true,
    "sections_found": 13
  },
  "sections": [
    {
      "section": "A",
      "components": [
        {
          "name": "A",
          "crn": "31108",
          "status": "Open",
          "credits": 0.5,
          "schedule_type": "Lecture",
          "instructor": "Ava McKenney",
          "meeting_times": [
            {
              "start_date": "Sep 03, 2025",
              "end_date": "Dec 05, 2025", 
              "days": "Wed Fri",
              "start_time": "13:05",
              "end_time": "14:25"
            }
          ],
          "notes": ["Also Register in: COMP 1005 A1 or A2 or A3"],
          "rmp_rating": {
            "instructor": "Ava McKenney",
            "rating": 4.2,
            "num_ratings": 15
          }
        },
        {
          "name": "A1",
          "crn": "31109",
          "status": "Open",
          "schedule_type": "Tutorial",
          "instructor": "Ava McKenney"
        },
        {
          "name": "A2", 
          "crn": "31110",
          "status": "Full, No Waitlist",
          "schedule_type": "Tutorial"
        }
      ]
    }
  ]
}
Professor Ratings
# Get Rate My Professor ratings
curl "http://localhost:8000/universities/carleton/professors/John/Smith"
curl "http://localhost:8000/universities/uottawa/professors/Lucia/Moura"
๐ŸŽ“ Academic Programs
# Get all programs for a university
curl "http://localhost:8000/universities/carleton/programs?limit=10"
curl "http://localhost:8000/universities/uottawa/programs?limit=10"

# Filter programs by criteria
curl "http://localhost:8000/universities/carleton/programs?faculty=engineering&limit=5"
curl "http://localhost:8000/universities/uottawa/programs?degree_type=bachelor&faculty=science"

# Search programs by name
curl "http://localhost:8000/universities/carleton/programs/search?q=computer&limit=5"
curl "http://localhost:8000/universities/uottawa/programs/search?q=engineering&limit=5"

# Get available filter options
curl "http://localhost:8000/universities/carleton/programs/filters"
curl "http://localhost:8000/universities/uottawa/programs/filters"

# ๐Ÿš€ BULK EXPORT - All programs for Laravel/database import
curl "http://localhost:8000/universities/carleton/programs/export"
curl "http://localhost:8000/universities/uottawa/programs/export"

Programs Data Coverage:

  • ๐ŸŽ“ Carleton University: 129 programs across 5 faculties
  • ๐ŸŽ“ University of Ottawa: 700+ programs across 9 faculties
  • ๐Ÿ“Š Total: 840+ academic programs available

Bulk Export Features:

  • ๐Ÿ“ฆ One-shot export: Complete university + faculty + program data
  • ๐Ÿ›๏ธ Laravel-compatible: Ready for direct database import
  • ๐Ÿ”— Relational structure: Proper university โ†’ faculty โ†’ program hierarchy
  • ๐Ÿ“‹ Rich metadata: Export timestamps, counts, and import notes

API Features

  • ๐Ÿ—๏ธ Structured Responses: Properly grouped course sections and components
  • ๐ŸŽ“ Academic Programs: Complete program catalog with search and filtering
  • ๐Ÿ“ฆ Bulk Export: Laravel-ready program data with relational structure
  • โญ Professor Integration: Optional Rate My Professor ratings via ?include_ratings=true
  • ๐Ÿ” Smart Filtering: Filter by subjects, course codes, terms, faculties, disciplines
  • ๐Ÿ“Š University-Specific: Handles different term formats and subject code lengths
  • ๐Ÿ“š Comprehensive Data: Course catalogs, live timetables, prerequisites, programs
  • ๐Ÿš€ High Performance: Direct single-course queries bypass bulk discovery
  • ๐Ÿ“– Interactive Docs: Auto-generated OpenAPI documentation
  • ๐Ÿ›ก๏ธ Type Safety: Full Pydantic validation and serialization
  • ๐ŸŽฏ RESTful Design: Clean, predictable endpoint structure

๐Ÿ”ง Advanced Usage

Custom University Provider

from uoapi.core import UniversityProvider, University, Subject, Course
from uoapi.universities import BaseUniversityProvider

class MyUniversityProvider(BaseUniversityProvider):
    @property
    def university(self) -> University:
        return University.MYUNI  # Add to enum first
    
    @property  
    def name(self) -> str:
        return "My University"
    
    def get_subjects(self) -> List[Subject]:
        # Implement subject scraping/loading
        return []
    
    def get_courses(self, subject_code: str = None) -> List[Course]:
        # Implement course scraping/loading
        return []

# Register with service
from uoapi.services import DefaultCourseService
service = DefaultCourseService()
service._providers[University.MYUNI] = MyUniversityProvider()

Custom API Endpoints

from fastapi import APIRouter
from uoapi.server.app import create_app

# Create custom router
custom_router = APIRouter()

@custom_router.get("/custom/endpoint")
async def custom_endpoint():
    return {"message": "Custom functionality"}

# Add to app
app = create_app()
app.include_router(custom_router, prefix="/api/v1", tags=["custom"])

Configuration

from uoapi.utils import get_config

config = get_config()

# Adjust cache settings
config.cache.ttl_seconds = 7200  # 2 hours
config.cache.uottawa_ttl = 14400  # 4 hours for UOttawa

# Adjust scraping settings
config.scraping.timeout_seconds = 60
config.scraping.concurrent_workers = 8

# API settings
config.api.port = 9000
config.api.debug = True

๐Ÿ“Š Data Models

Unified Course Model

from uoapi.core import Course

# All universities use the same model
course = Course(
    course_code="COMP1001",
    subject_code="COMP", 
    course_number="1001",
    title="Introduction to Computing",
    description="Basic computing concepts...",
    credits=3,
    university=University.CARLETON,
    components=["Lecture", "Laboratory"],
    prerequisites="None",
    sections=[...],  # Live sections if available
    is_offered=True
)

Search Results

from uoapi.core import SearchResult

result = SearchResult(
    university=University.UOTTAWA,
    query="programming",
    subject_filter="CSI",
    total_found=25,
    courses=[...],
    metadata={"search_method": "text_search"}
)

Live Course Discovery

from uoapi.core import DiscoveryResult

result = DiscoveryResult(
    term_code="202501",
    term_name="Winter 2025",
    university=University.CARLETON,
    subjects_queried=["COMP", "MATH"],
    total_courses=150,
    courses_offered=142,
    offering_rate=94.7,
    processing_time=25.3,
    courses=[...]
)

๐Ÿงช Development

Setup

git clone https://github.com/Rain6435/uoapi.git
cd uoapi
pip install -e .[tests]

Testing

# Run all tests
make test     # or pytest

# Test specific components
pytest tests/core/
pytest tests/services/
pytest tests/universities/

# Test with coverage
pytest --cov=uoapi tests/

# Type checking
make check    # or mypy src/

# Linting  
make lint     # or flake8

# All checks
make          # test + lint + typecheck

Code Quality

The refactored codebase maintains high code quality with:

  • 100% type coverage with mypy
  • Comprehensive tests for all components
  • Consistent formatting with black
  • Clean imports and modular design
  • Documentation for all public APIs

๐Ÿ”„ Migration Guide

From Old API

# Old way
from uoapi.course.course_info import scrape_subjects, get_courses
subjects = scrape_subjects()
courses = list(get_courses(subjects[0]['link']))

# New way (recommended)
from uoapi.core import University
from uoapi.services import DefaultCourseService

service = DefaultCourseService()
subjects = service.get_subjects(University.UOTTAWA)
courses = service.get_courses(University.UOTTAWA, subjects[0].code)

Legacy Compatibility

# Old imports still work
from uoapi.course import scrape_subjects, get_courses  # โœ… Still works
from uoapi.carleton.discovery import CarletonDiscovery  # โœ… Still works
from uoapi.server.app import create_app  # โœ… Still works

# But new imports are cleaner
from uoapi.core import *  # โœ… New unified models
from uoapi.services import *  # โœ… Business logic
from uoapi.interfaces.api import create_app  # โœ… Clean API

๐Ÿ› Troubleshooting

Common Issues

  1. Import errors: Ensure Python 3.10+ and proper installation
  2. University not supported: Check service.get_all_universities()
  3. Term validation: Use timetable_service.get_available_terms() first
  4. Rate limiting: Reduce concurrent workers if getting blocked
  5. Live data not available: Both Carleton and UOttawa support live timetable data

Debug Mode

import logging
logging.basicConfig(level=logging.DEBUG)

# Or start server with debug logging
schedulo-server --log-level debug

Configuration Issues

from uoapi.utils import get_config, reload_config

# Check current config
config = get_config()
print(config.to_dict())

# Reload with different environment
reload_config("development")

๐ŸŽฏ What's New in v4.0+

Breaking Changes

  • ๐Ÿšจ CLI Removed: The package is now server-only. Use schedulo-server to start the REST API
  • ๐Ÿ”ง Server-First Architecture: All functionality now accessed via HTTP API or Python library

Major Enhancements

  • ๐Ÿ”ง Enhanced Section Parsing: Complete retrieval of all course sections, lectures, tutorials, and labs
  • โšก Improved Data Accuracy: Fixed Banner system parsing to capture all available course sections
  • ๐Ÿš€ Better Performance: Optimized server with worker support
  • ๐Ÿ‘จโ€๐Ÿซ Professor Ratings: Rate My Professor integration via API endpoints
  • ๐Ÿ“š Complete REST API: All functionality available via HTTP endpoints
  • ๐ŸŽฏ Smart Subject Validation: University-specific subject code validation (4-letter for Carleton, 3-letter for UOttawa)

Architecture Improvements

  • ๐Ÿ—๏ธ Clean Architecture: Proper layered design with separation of concerns
  • ๐Ÿ”ง Service Layer: Business logic separated from data access
  • ๐ŸŽฏ Single Responsibility: Each module has one clear purpose
  • ๐Ÿ”„ Dependency Inversion: High-level modules don't depend on low-level details

Developer Experience

  • โœ… Type Safety: Complete type annotations with Pydantic
  • ๐Ÿงช Better Testing: Clear boundaries enable comprehensive testing
  • ๐Ÿ“š Better Documentation: Comprehensive examples and API docs
  • ๐Ÿ”ง Easy Extension: Add new universities via simple interfaces
  • ๐ŸŒ API-First: Build your own clients or integrate with any platform

User Experience

  • ๐ŸŽจ Consistent APIs: Same patterns across all universities
  • โšก Better Performance: Improved caching and parallel processing
  • ๐Ÿ” Better Error Messages: Structured exceptions with helpful details
  • ๐Ÿ“Š Richer Data: Enhanced models with metadata and validation
  • ๐ŸŒ Universal Access: Use any HTTP client to consume the API

๐Ÿค Contributing

We welcome contributions! The architecture makes it easy to contribute:

  1. Add Universities: Implement UniversityProvider interface
  2. Add Features: Extend service classes with new functionality
  3. Add API Endpoints: Create new REST API endpoints
  4. Fix Bugs: Clear modular structure makes debugging easier

Contribution Process

# 1. Fork and clone
git clone https://github.com/your-username/uoapi.git

# 2. Create feature branch
git checkout -b feature/my-feature

# 3. Make changes and test
make test
make lint

# 4. Submit PR
git push origin feature/my-feature

๐Ÿ“œ License

GNU LGPLv3.0 - See the COPYING and COPYING.LESSER files for details.

๐Ÿ™ Acknowledgments

  • Original uoapi by Andrew Nagarajah
  • University of Ottawa and Carleton University for public data access
  • Rate My Professor for their API
  • The Python community for excellent libraries and tools

Ready to explore university course data via REST API?

pip install schedulo-api
schedulo-server --port 8000  # Start the server
# Visit http://localhost:8000/docs for interactive documentation

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

schedulo_api-4.0.0.tar.gz (4.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

schedulo_api-4.0.0-py3-none-any.whl (1.8 MB view details)

Uploaded Python 3

File details

Details for the file schedulo_api-4.0.0.tar.gz.

File metadata

  • Download URL: schedulo_api-4.0.0.tar.gz
  • Upload date:
  • Size: 4.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for schedulo_api-4.0.0.tar.gz
Algorithm Hash digest
SHA256 4e07ac2a0c0c3eb6832b4ad3bdb96597561a6d89f55ac6319dd7ce9b465fc6d7
MD5 82ed2c4ef396db46e0e209e9a8a95c23
BLAKE2b-256 b860cedcc42c577550c2c4bc3847294f92ea6ab0b8c7097608b1c2e4e903c466

See more details on using hashes here.

File details

Details for the file schedulo_api-4.0.0-py3-none-any.whl.

File metadata

  • Download URL: schedulo_api-4.0.0-py3-none-any.whl
  • Upload date:
  • Size: 1.8 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for schedulo_api-4.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6ee7c45698a851db56090d04c483401053dc0031e32ff3f0547d6ac41c72c338
MD5 ae091ff785be2bfded3a8ebca7aaec59
BLAKE2b-256 ebc2f626fd3df4d41e435ad5f09a0487ba089d74f829358633af41758252b42e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page