schedulo-api


Nameschedulo-api JSON
Version 3.4.1 PyPI version JSON
download
home_pagehttps://github.com/Rain6435/uoapi
SummaryAn API for retrieving public data from the University of Ottawa and Carleton University.
upload_time2025-09-07 02:32:56
maintainerNone
docs_urlNone
authorMohammed Elhasnaoui
requires_python>=3.10.0
licenseLGPL-3.0-or-later
keywords university ottawa carleton courses api scraping education
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Schedulo API

A Python CLI tool and library for retrieving public data from Canadian universities, including the University of Ottawa and Carleton University.

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

[![PyPI version](https://badge.fury.io/py/schedulo-api.svg)](https://badge.fury.io/py/schedulo-api)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)

## โœจ 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**: Comprehensive programmatic access
- **๐Ÿ“ Type Safety**: Full type annotations with Pydantic models
- **๐Ÿ”„ Backward Compatibility**: Existing code continues to work

## ๐Ÿ—๏ธ 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
โ”œโ”€โ”€ interfaces/            # CLI and API interfaces
โ””โ”€โ”€ 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

```bash
# 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

#### New Service-Based API (Recommended)
```python
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 (Carleton only)
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")
```

#### Command Line Interface
```bash
# List available terms
schedulo terms carleton

# List available subjects
schedulo subjects carleton

# Get courses for subjects
schedulo courses carleton fall2025 COMP MATH

# Get specific course details
schedulo course carleton COMP1005 fall2025

# Start FastAPI server
schedulo server --port 8000
```

#### FastAPI Server
```bash
# Start the server
schedulo server --port 8000

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

## ๐Ÿ“– Complete Usage Guide

### University Data Access

#### Course Service
```python
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
```python
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
```python
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)
```

### CLI Usage

The clean, unified CLI provides simple commands for accessing university data:

#### Basic Commands
```bash
# List available terms
schedulo terms carleton

# List available subjects
schedulo subjects carleton

# Get courses for specific subjects and term
schedulo courses carleton fall2025 COMP MATH --limit 20

# Get catalog courses (no term required, no live sections)
schedulo courses carleton COMP --catalog --limit 10        # Carleton: 4-letter subjects
schedulo courses uottawa CSI --catalog --limit 10          # UOttawa: 3-letter subjects  
schedulo courses carleton --catalog --limit 20             # All catalog courses

# Get detailed information for a specific course
schedulo course carleton COMP1005 fall2025

# Get professor ratings from Rate My Professor
schedulo professor John Smith carleton
schedulo professor Lucia Moura uottawa

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

#### Catalog Courses Access
Access complete course catalogs without needing term information:

```bash
# Get all catalog courses for a subject (university-specific formats)
schedulo courses carleton COMP --catalog --limit 10        # Carleton: 4-letter codes
schedulo courses uottawa CSI --catalog --limit 10          # UOttawa: 3-letter codes

# Get all catalog courses (with limit for performance)
schedulo courses carleton --catalog --limit 50             # First 50 courses
schedulo courses uottawa --catalog --limit 0               # All courses (no limit)

# Example output:
# Getting catalog courses from carleton...
# Found 10 catalog courses
#
# COMP - 10 courses:
#   COMP1001: Introduction to Computational Thinking for Arts and Social Science Students
#   COMP1005: Introduction to Computer Science I
#   COMP1006: Introduction to Computer Science II
#   COMP1405: Introduction to Computer Science I
#     Credits: 3
#   ...
```

**Key Features:**
- **No term required**: Catalog data is term-independent
- **Subject code validation**: Automatically detects valid subject codes based on university
- **Organized output**: Courses grouped by subject with credit information
- **Performance limits**: Built-in limits to handle large catalogs efficiently

#### Enhanced Section Parsing
The CLI now captures **complete section data** including all lectures, tutorials, and labs:

```bash
# Example: COMP 1005 retrieves all 13 sections (4 lectures + 9 tutorials)
schedulo course carleton COMP1005 fall2025

# Output shows:
#   Sections Summary:
#     Total: 13
#     Lectures: 4  
#     Tutorials: 9
#   
#   All Sections:
#     A (Lecture) - CRN 10001 - Open
#     B (Lecture) - CRN 10002 - Open
#     T01 (Tutorial) - CRN 10101 - Open
#     T02 (Tutorial) - CRN 10102 - Wait List
#     ...
```

#### Professor Rating Lookup
Get comprehensive Rate My Professor data for university instructors:

```bash
# Basic professor lookup
schedulo professor Rami Abielmona uottawa

# Output example:
# ๐Ÿ“Š Professor Rating: Rami Abielmona
# ==================================================
# Overall Rating: 4.5/5.0 โญ
# Number of Ratings: 57
# Department: Engineering
# Would Take Again: 85%
# Average Difficulty: 3.3/5.0
# Rate My Professor ID: 232123
# Profile URL: https://www.ratemyprofessors.com/professor/232123
#
# ๐Ÿ“ Rating Interpretation:
# ๐ŸŸข Excellent professor (4.0+ rating)

# Works with both universities
schedulo professor Bo Sun uottawa      # University of Ottawa
schedulo professor John Smith carleton # Carleton University

# Handles various name formats and provides helpful error messages
schedulo professor NonExistent Name uottawa
# No ratings found for NonExistent Name at University of Ottawa
# Tips:
# - Try different name variations (nicknames, middle names)  
# - Check spelling of first and last name
# - Some professors may not be on Rate My Professor
```

### REST API Server

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

#### Start Server
```bash
# Using CLI (Recommended)
schedulo server --port 8000

# 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
```bash
# 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
```bash
# 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)
```bash
# 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
```bash
# 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**:
```json
{
  "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
```bash
# 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
```bash
# 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
```python
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 CLI Command
```python
from uoapi.interfaces.cli.framework import UniversityCommand, registry
import argparse

class MyCommand(UniversityCommand):
    @property
    def name(self) -> str:
        return "mycmd"
    
    @property
    def help(self) -> str:
        return "My custom command"
    
    @property
    def description(self) -> str:
        return "Does something useful"
    
    def configure_command_parser(self, parser: argparse.ArgumentParser):
        parser.add_argument("--option", help="My option")
        return parser
    
    def execute_for_university(self, args, university):
        # Implement command logic
        return self.format_output({"result": "success"})

# Register command
registry.register(MyCommand())
```

### Configuration
```python
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
```python
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
```python
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
```python
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
```bash
git clone https://github.com/Rain6435/uoapi.git
cd uoapi
pip install -e .[tests]
```

### Testing
```bash
# 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
```python
# 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
```python
# 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
```python
import logging
logging.basicConfig(level=logging.DEBUG)

# Or via CLI
uoapi --verbose course -u carleton -c COMP
```

### Configuration Issues
```python
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 v3.2+

### Major Enhancements
- **๐Ÿ”ง Enhanced Section Parsing**: Complete retrieval of all course sections, lectures, tutorials, and labs
- **๐ŸŽจ Clean CLI Interface**: Simplified commands with intuitive structure (`schedulo` instead of complex nested commands)
- **โšก Improved Data Accuracy**: Fixed Banner system parsing to capture all available course sections  
- **๐Ÿš€ Better User Experience**: Streamlined commands and comprehensive section information
- **๐Ÿ‘จโ€๐Ÿซ Professor Ratings**: New `schedulo professor` command with Rate My Professor integration
- **๐Ÿ“š Catalog Access**: New `--catalog` option to browse complete course catalogs without term requirements
- **๐ŸŽฏ 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

### 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

## ๐Ÿค Contributing

We welcome contributions! The new architecture makes it much easier to contribute:

1. **Add Universities**: Implement `UniversityProvider` interface
2. **Add Features**: Extend service classes with new functionality  
3. **Add Interfaces**: Create new CLI commands or API endpoints
4. **Fix Bugs**: Clear modular structure makes debugging easier

### Contribution Process
```bash
# 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](https://github.com/andrewnags/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 with enhanced section parsing and clean CLI?** 
```bash
pip install schedulo-api
schedulo terms carleton  # Get started!
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/Rain6435/uoapi",
    "name": "schedulo-api",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10.0",
    "maintainer_email": "Mohammed Elhasnaoui <brosimo@outlook.fr>",
    "keywords": "university, ottawa, carleton, courses, api, scraping, education",
    "author": "Mohammed Elhasnaoui",
    "author_email": "Mohammed Elhasnaoui <brosimo@outlook.fr>",
    "download_url": "https://files.pythonhosted.org/packages/56/2d/e93dd4b35bb818e0b916b1260f62c26c261dbacc61eaf812d7505e248053/schedulo_api-3.4.1.tar.gz",
    "platform": null,
    "description": "# Schedulo API\n\nA Python CLI tool and library for retrieving public data from Canadian universities, including the University of Ottawa and Carleton University.\n\n**This package now features a completely refactored, clean architecture with improved maintainability and extensibility.**\n\n[![PyPI version](https://badge.fury.io/py/schedulo-api.svg)](https://badge.fury.io/py/schedulo-api)\n[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)\n\n## \u2728 Features\n\n- **\ud83c\udfeb Multi-University Support**: University of Ottawa and Carleton University\n- **\ud83d\udcda Complete Course Data**: Catalogs, timetables, prerequisites, components\n- **\ud83c\udf93 Academic Programs**: 840+ programs with filtering, search, and bulk export\n- **\u26a1 Live Timetable Data**: Real-time course availability and scheduling\n- **\u2b50 Rate My Professor Integration**: Professor ratings for both universities\n- **\ud83d\ude80 FastAPI REST API**: Complete HTTP API with interactive documentation\n- **\ud83d\udce6 Laravel Integration**: Bulk program export for database seeding\n- **\ud83d\udd27 Clean Architecture**: Layered design with proper separation of concerns\n- **\ud83d\udc0d Python Library**: Comprehensive programmatic access\n- **\ud83d\udcdd Type Safety**: Full type annotations with Pydantic models\n- **\ud83d\udd04 Backward Compatibility**: Existing code continues to work\n\n## \ud83c\udfd7\ufe0f New Architecture\n\nThe package has been completely refactored with a clean layered architecture:\n\n```\nuoapi/\n\u251c\u2500\u2500 core/                    # Domain models & interfaces\n\u251c\u2500\u2500 universities/           # University-specific implementations  \n\u251c\u2500\u2500 services/              # Business logic layer\n\u251c\u2500\u2500 interfaces/            # CLI and API interfaces\n\u2514\u2500\u2500 utils/                # Shared utilities\n```\n\n### Key Benefits:\n- \u2705 **Single Responsibility**: Each module has one clear purpose\n- \u2705 **Consistent Models**: Unified data structures across universities\n- \u2705 **Easy Extension**: Add new universities by implementing simple interfaces\n- \u2705 **Better Testing**: Clear boundaries enable comprehensive testing\n- \u2705 **Type Safety**: Full type annotations throughout\n\n## \ud83d\ude80 Quick Start\n\n### Installation\n\n```bash\n# From PyPI (Recommended)\npip install schedulo-api\n\n# From Source\npip install git+https://github.com/Rain6435/uoapi.git@dev\n\n# Development Installation\ngit clone https://github.com/Rain6435/uoapi.git\ncd uoapi\npip install -e .[tests]\n```\n\n### Basic Usage\n\n#### New Service-Based API (Recommended)\n```python\nfrom uoapi.core import University\nfrom uoapi.services import DefaultCourseService, DefaultTimetableService\n\n# Initialize services\ncourse_service = DefaultCourseService()\ntimetable_service = DefaultTimetableService()\n\n# Get all subjects for a university\nsubjects = course_service.get_subjects(University.CARLETON)\nprint(f\"Found {len(subjects)} subjects\")\n\n# Get courses for a specific subject\ncourses = course_service.get_courses(University.CARLETON, \"COMP\")\nprint(f\"Found {len(courses)} COMP courses\")\n\n# Search courses\nsearch_result = course_service.search_courses(University.UOTTAWA, \"programming\")\nprint(f\"Found {search_result.total_found} courses matching 'programming'\")\n\n# Get live timetable data (Carleton only)\nif University.CARLETON in timetable_service.get_supported_universities():\n    live_data = timetable_service.get_live_courses(\n        University.CARLETON,\n        term_code=\"202501\",\n        subjects=[\"COMP\"],\n        max_courses_per_subject=10\n    )\n    print(f\"Found {live_data.courses_offered} offered courses\")\n```\n\n#### Command Line Interface\n```bash\n# List available terms\nschedulo terms carleton\n\n# List available subjects\nschedulo subjects carleton\n\n# Get courses for subjects\nschedulo courses carleton fall2025 COMP MATH\n\n# Get specific course details\nschedulo course carleton COMP1005 fall2025\n\n# Start FastAPI server\nschedulo server --port 8000\n```\n\n#### FastAPI Server\n```bash\n# Start the server\nschedulo server --port 8000\n\n# Interactive docs available at:\n# http://localhost:8000/docs\n# http://localhost:8000/redoc\n```\n\n## \ud83d\udcd6 Complete Usage Guide\n\n### University Data Access\n\n#### Course Service\n```python\nfrom uoapi.core import University\nfrom uoapi.services import DefaultCourseService\n\nservice = DefaultCourseService()\n\n# Get all supported universities\nuniversities = service.get_all_universities()\n\n# Get subjects\nsubjects = service.get_subjects(University.UOTTAWA)\nfor subject in subjects[:5]:\n    print(f\"{subject.code}: {subject.name}\")\n\n# Get courses with filtering\ncourses = service.get_courses(\n    University.CARLETON, \n    subject_code=\"COMP\",\n    query=\"database\"\n)\n\n# Get specific course\ncourse = service.get_course_by_code(University.UOTTAWA, \"CSI3140\")\nprint(f\"{course.title}: {course.credits} credits\")\n\n# Get course statistics\nstats = service.get_course_statistics(University.CARLETON)\nprint(f\"Total courses: {stats['total_courses']}\")\n```\n\n#### Timetable Service\n```python\nfrom uoapi.services import DefaultTimetableService\n\nservice = DefaultTimetableService()\n\n# Check which universities support live data\nsupported = service.get_supported_universities()\nprint(f\"Live data supported by: {[u.value for u in supported]}\")\n\n# Get available terms\nterms = service.get_available_terms(University.CARLETON)\nfor code, name in terms:\n    print(f\"{code}: {name}\")\n\n# Get live course data\nresult = service.get_live_courses(\n    university=University.CARLETON,\n    term_code=\"202501\",\n    subjects=[\"COMP\", \"MATH\"],\n    max_courses_per_subject=20\n)\n\nprint(f\"Processing time: {result.processing_time:.2f}s\")\nprint(f\"Offering rate: {result.offering_rate:.1f}%\")\n\nfor course in result.courses:\n    if course.is_offered:\n        print(f\"\\n{course.course_code}: {course.title}\")\n        for section in course.sections:\n            print(f\"  {section.section}: {section.instructor} - {section.status}\")\n            for mt in section.meeting_times:\n                print(f\"    {mt.days} {mt.start_time}-{mt.end_time}\")\n```\n\n#### Rating Service\n```python\nfrom uoapi.services import DefaultRatingService\n\nservice = DefaultRatingService()\n\n# Get individual instructor rating\nrating = service.get_instructor_rating(\"John Smith\", University.UOTTAWA)\nif rating:\n    print(f\"Rating: {rating['rating']}/5.0\")\n    print(f\"Difficulty: {rating['avg_difficulty']}/5.0\")\n\n# Get batch ratings\ninstructors = [(\"Jane\", \"Doe\"), (\"John\", \"Smith\")]\nratings = service.get_batch_ratings(instructors, University.CARLETON)\n\n# Enhance courses with ratings\nenhanced_courses = service.inject_ratings_into_courses(courses, University.UOTTAWA)\n```\n\n### CLI Usage\n\nThe clean, unified CLI provides simple commands for accessing university data:\n\n#### Basic Commands\n```bash\n# List available terms\nschedulo terms carleton\n\n# List available subjects\nschedulo subjects carleton\n\n# Get courses for specific subjects and term\nschedulo courses carleton fall2025 COMP MATH --limit 20\n\n# Get catalog courses (no term required, no live sections)\nschedulo courses carleton COMP --catalog --limit 10        # Carleton: 4-letter subjects\nschedulo courses uottawa CSI --catalog --limit 10          # UOttawa: 3-letter subjects  \nschedulo courses carleton --catalog --limit 20             # All catalog courses\n\n# Get detailed information for a specific course\nschedulo course carleton COMP1005 fall2025\n\n# Get professor ratings from Rate My Professor\nschedulo professor John Smith carleton\nschedulo professor Lucia Moura uottawa\n\n# Start the API server\nschedulo server --port 8000\n```\n\n#### Catalog Courses Access\nAccess complete course catalogs without needing term information:\n\n```bash\n# Get all catalog courses for a subject (university-specific formats)\nschedulo courses carleton COMP --catalog --limit 10        # Carleton: 4-letter codes\nschedulo courses uottawa CSI --catalog --limit 10          # UOttawa: 3-letter codes\n\n# Get all catalog courses (with limit for performance)\nschedulo courses carleton --catalog --limit 50             # First 50 courses\nschedulo courses uottawa --catalog --limit 0               # All courses (no limit)\n\n# Example output:\n# Getting catalog courses from carleton...\n# Found 10 catalog courses\n#\n# COMP - 10 courses:\n#   COMP1001: Introduction to Computational Thinking for Arts and Social Science Students\n#   COMP1005: Introduction to Computer Science I\n#   COMP1006: Introduction to Computer Science II\n#   COMP1405: Introduction to Computer Science I\n#     Credits: 3\n#   ...\n```\n\n**Key Features:**\n- **No term required**: Catalog data is term-independent\n- **Subject code validation**: Automatically detects valid subject codes based on university\n- **Organized output**: Courses grouped by subject with credit information\n- **Performance limits**: Built-in limits to handle large catalogs efficiently\n\n#### Enhanced Section Parsing\nThe CLI now captures **complete section data** including all lectures, tutorials, and labs:\n\n```bash\n# Example: COMP 1005 retrieves all 13 sections (4 lectures + 9 tutorials)\nschedulo course carleton COMP1005 fall2025\n\n# Output shows:\n#   Sections Summary:\n#     Total: 13\n#     Lectures: 4  \n#     Tutorials: 9\n#   \n#   All Sections:\n#     A (Lecture) - CRN 10001 - Open\n#     B (Lecture) - CRN 10002 - Open\n#     T01 (Tutorial) - CRN 10101 - Open\n#     T02 (Tutorial) - CRN 10102 - Wait List\n#     ...\n```\n\n#### Professor Rating Lookup\nGet comprehensive Rate My Professor data for university instructors:\n\n```bash\n# Basic professor lookup\nschedulo professor Rami Abielmona uottawa\n\n# Output example:\n# \ud83d\udcca Professor Rating: Rami Abielmona\n# ==================================================\n# Overall Rating: 4.5/5.0 \u2b50\n# Number of Ratings: 57\n# Department: Engineering\n# Would Take Again: 85%\n# Average Difficulty: 3.3/5.0\n# Rate My Professor ID: 232123\n# Profile URL: https://www.ratemyprofessors.com/professor/232123\n#\n# \ud83d\udcdd Rating Interpretation:\n# \ud83d\udfe2 Excellent professor (4.0+ rating)\n\n# Works with both universities\nschedulo professor Bo Sun uottawa      # University of Ottawa\nschedulo professor John Smith carleton # Carleton University\n\n# Handles various name formats and provides helpful error messages\nschedulo professor NonExistent Name uottawa\n# No ratings found for NonExistent Name at University of Ottawa\n# Tips:\n# - Try different name variations (nicknames, middle names)  \n# - Check spelling of first and last name\n# - Some professors may not be on Rate My Professor\n```\n\n### REST API Server\n\nThe Schedulo API provides a comprehensive FastAPI-based REST server with interactive documentation, structured responses, and powerful filtering capabilities.\n\n#### Start Server\n```bash\n# Using CLI (Recommended)\nschedulo server --port 8000\n\n# Or programmatically\npython -c \"\nfrom uoapi.server.app import create_app\nimport uvicorn\napp = create_app()\nuvicorn.run(app, host='127.0.0.1', port=8000)\n\"\n```\n\n**Interactive Documentation**: http://localhost:8000/docs  \n**ReDoc Documentation**: http://localhost:8000/redoc\n\n#### Core Endpoints\n\n##### University Information\n```bash\n# List all supported universities\ncurl http://localhost:8000/universities\n\n# Get university-specific information\ncurl http://localhost:8000/universities/carleton/info\ncurl http://localhost:8000/universities/uottawa/info\n```\n\n##### Subjects\n```bash\n# Get subjects (preview - first 20)\ncurl http://localhost:8000/universities/carleton/subjects\ncurl http://localhost:8000/universities/uottawa/subjects\n\n# Get all subjects (complete catalog)\ncurl http://localhost:8000/universities/carleton/subjects/catalog\ncurl http://localhost:8000/universities/uottawa/subjects/catalog\n```\n\n##### Course Catalog (Static Data)\n```bash\n# Get catalog courses by subject\ncurl \"http://localhost:8000/universities/carleton/courses/catalog?subjects=COMP,MATH&limit=10\"\ncurl \"http://localhost:8000/universities/uottawa/courses/catalog?subjects=CSI,MAT&limit=5\"\n\n# Get all catalog courses (warning: large response)\ncurl \"http://localhost:8000/universities/carleton/courses/catalog\"\n\n# Get single course (catalog data only)\ncurl http://localhost:8000/universities/carleton/courses/COMP1005\ncurl http://localhost:8000/universities/uottawa/courses/CSI3140\n```\n\n##### Live Timetable Data\n```bash\n# Get available terms for live data\ncurl http://localhost:8000/universities/carleton/terms\ncurl http://localhost:8000/universities/uottawa/terms\n\n# Multiple courses with live sections\ncurl \"http://localhost:8000/universities/carleton/courses/live?term=fall&year=2025&subjects=COMP,MATH&limit=20&include_ratings=true\"\ncurl \"http://localhost:8000/universities/uottawa/courses/live?term=winter&year=2025&subjects=CSI,CEG&limit=10\"\n\n# Filter by specific course codes\ncurl \"http://localhost:8000/universities/carleton/courses/live?term=fall&year=2025&subjects=COMP&course_codes=COMP1005,COMP1405\"\n\n# Single course with structured sections\ncurl \"http://localhost:8000/universities/carleton/courses/COMP1005/live?term=fall&year=2025&include_ratings=true\"\n```\n\n**New Single Course Response Structure**:\n```json\n{\n  \"university\": \"carleton\",\n  \"term_code\": \"202530\",\n  \"term_name\": \"Fall 2025\",\n  \"course\": {\n    \"course_code\": \"COMP1005\",\n    \"subject_code\": \"COMP\",\n    \"title\": \"Programming Concepts\",\n    \"credits\": 0.5,\n    \"is_offered\": true,\n    \"sections_found\": 13\n  },\n  \"sections\": [\n    {\n      \"section\": \"A\",\n      \"components\": [\n        {\n          \"name\": \"A\",\n          \"crn\": \"31108\",\n          \"status\": \"Open\",\n          \"credits\": 0.5,\n          \"schedule_type\": \"Lecture\",\n          \"instructor\": \"Ava McKenney\",\n          \"meeting_times\": [\n            {\n              \"start_date\": \"Sep 03, 2025\",\n              \"end_date\": \"Dec 05, 2025\", \n              \"days\": \"Wed Fri\",\n              \"start_time\": \"13:05\",\n              \"end_time\": \"14:25\"\n            }\n          ],\n          \"notes\": [\"Also Register in: COMP 1005 A1 or A2 or A3\"],\n          \"rmp_rating\": {\n            \"instructor\": \"Ava McKenney\",\n            \"rating\": 4.2,\n            \"num_ratings\": 15\n          }\n        },\n        {\n          \"name\": \"A1\",\n          \"crn\": \"31109\",\n          \"status\": \"Open\",\n          \"schedule_type\": \"Tutorial\",\n          \"instructor\": \"Ava McKenney\"\n        },\n        {\n          \"name\": \"A2\", \n          \"crn\": \"31110\",\n          \"status\": \"Full, No Waitlist\",\n          \"schedule_type\": \"Tutorial\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n##### Professor Ratings\n```bash\n# Get Rate My Professor ratings\ncurl \"http://localhost:8000/universities/carleton/professors/John/Smith\"\ncurl \"http://localhost:8000/universities/uottawa/professors/Lucia/Moura\"\n```\n\n##### \ud83c\udf93 Academic Programs\n```bash\n# Get all programs for a university\ncurl \"http://localhost:8000/universities/carleton/programs?limit=10\"\ncurl \"http://localhost:8000/universities/uottawa/programs?limit=10\"\n\n# Filter programs by criteria\ncurl \"http://localhost:8000/universities/carleton/programs?faculty=engineering&limit=5\"\ncurl \"http://localhost:8000/universities/uottawa/programs?degree_type=bachelor&faculty=science\"\n\n# Search programs by name\ncurl \"http://localhost:8000/universities/carleton/programs/search?q=computer&limit=5\"\ncurl \"http://localhost:8000/universities/uottawa/programs/search?q=engineering&limit=5\"\n\n# Get available filter options\ncurl \"http://localhost:8000/universities/carleton/programs/filters\"\ncurl \"http://localhost:8000/universities/uottawa/programs/filters\"\n\n# \ud83d\ude80 BULK EXPORT - All programs for Laravel/database import\ncurl \"http://localhost:8000/universities/carleton/programs/export\"\ncurl \"http://localhost:8000/universities/uottawa/programs/export\"\n```\n\n**Programs Data Coverage:**\n- **\ud83c\udf93 Carleton University**: 129 programs across 5 faculties\n- **\ud83c\udf93 University of Ottawa**: 700+ programs across 9 faculties\n- **\ud83d\udcca Total**: 840+ academic programs available\n\n**Bulk Export Features:**\n- \ud83d\udce6 **One-shot export**: Complete university + faculty + program data\n- \ud83c\udfdb\ufe0f **Laravel-compatible**: Ready for direct database import\n- \ud83d\udd17 **Relational structure**: Proper university \u2192 faculty \u2192 program hierarchy\n- \ud83d\udccb **Rich metadata**: Export timestamps, counts, and import notes\n\n#### API Features\n\n- **\ud83c\udfd7\ufe0f Structured Responses**: Properly grouped course sections and components\n- **\ud83c\udf93 Academic Programs**: Complete program catalog with search and filtering\n- **\ud83d\udce6 Bulk Export**: Laravel-ready program data with relational structure\n- **\u2b50 Professor Integration**: Optional Rate My Professor ratings via `?include_ratings=true`\n- **\ud83d\udd0d Smart Filtering**: Filter by subjects, course codes, terms, faculties, disciplines\n- **\ud83d\udcca University-Specific**: Handles different term formats and subject code lengths\n- **\ud83d\udcda Comprehensive Data**: Course catalogs, live timetables, prerequisites, programs\n- **\ud83d\ude80 High Performance**: Direct single-course queries bypass bulk discovery\n- **\ud83d\udcd6 Interactive Docs**: Auto-generated OpenAPI documentation\n- **\ud83d\udee1\ufe0f Type Safety**: Full Pydantic validation and serialization\n- **\ud83c\udfaf RESTful Design**: Clean, predictable endpoint structure\n\n## \ud83d\udd27 Advanced Usage\n\n### Custom University Provider\n```python\nfrom uoapi.core import UniversityProvider, University, Subject, Course\nfrom uoapi.universities import BaseUniversityProvider\n\nclass MyUniversityProvider(BaseUniversityProvider):\n    @property\n    def university(self) -> University:\n        return University.MYUNI  # Add to enum first\n    \n    @property  \n    def name(self) -> str:\n        return \"My University\"\n    \n    def get_subjects(self) -> List[Subject]:\n        # Implement subject scraping/loading\n        return []\n    \n    def get_courses(self, subject_code: str = None) -> List[Course]:\n        # Implement course scraping/loading\n        return []\n\n# Register with service\nfrom uoapi.services import DefaultCourseService\nservice = DefaultCourseService()\nservice._providers[University.MYUNI] = MyUniversityProvider()\n```\n\n### Custom CLI Command\n```python\nfrom uoapi.interfaces.cli.framework import UniversityCommand, registry\nimport argparse\n\nclass MyCommand(UniversityCommand):\n    @property\n    def name(self) -> str:\n        return \"mycmd\"\n    \n    @property\n    def help(self) -> str:\n        return \"My custom command\"\n    \n    @property\n    def description(self) -> str:\n        return \"Does something useful\"\n    \n    def configure_command_parser(self, parser: argparse.ArgumentParser):\n        parser.add_argument(\"--option\", help=\"My option\")\n        return parser\n    \n    def execute_for_university(self, args, university):\n        # Implement command logic\n        return self.format_output({\"result\": \"success\"})\n\n# Register command\nregistry.register(MyCommand())\n```\n\n### Configuration\n```python\nfrom uoapi.utils import get_config\n\nconfig = get_config()\n\n# Adjust cache settings\nconfig.cache.ttl_seconds = 7200  # 2 hours\nconfig.cache.uottawa_ttl = 14400  # 4 hours for UOttawa\n\n# Adjust scraping settings\nconfig.scraping.timeout_seconds = 60\nconfig.scraping.concurrent_workers = 8\n\n# API settings\nconfig.api.port = 9000\nconfig.api.debug = True\n```\n\n## \ud83d\udcca Data Models\n\n### Unified Course Model\n```python\nfrom uoapi.core import Course\n\n# All universities use the same model\ncourse = Course(\n    course_code=\"COMP1001\",\n    subject_code=\"COMP\", \n    course_number=\"1001\",\n    title=\"Introduction to Computing\",\n    description=\"Basic computing concepts...\",\n    credits=3,\n    university=University.CARLETON,\n    components=[\"Lecture\", \"Laboratory\"],\n    prerequisites=\"None\",\n    sections=[...],  # Live sections if available\n    is_offered=True\n)\n```\n\n### Search Results\n```python\nfrom uoapi.core import SearchResult\n\nresult = SearchResult(\n    university=University.UOTTAWA,\n    query=\"programming\",\n    subject_filter=\"CSI\",\n    total_found=25,\n    courses=[...],\n    metadata={\"search_method\": \"text_search\"}\n)\n```\n\n### Live Course Discovery\n```python\nfrom uoapi.core import DiscoveryResult\n\nresult = DiscoveryResult(\n    term_code=\"202501\",\n    term_name=\"Winter 2025\",\n    university=University.CARLETON,\n    subjects_queried=[\"COMP\", \"MATH\"],\n    total_courses=150,\n    courses_offered=142,\n    offering_rate=94.7,\n    processing_time=25.3,\n    courses=[...]\n)\n```\n\n## \ud83e\uddea Development\n\n### Setup\n```bash\ngit clone https://github.com/Rain6435/uoapi.git\ncd uoapi\npip install -e .[tests]\n```\n\n### Testing\n```bash\n# Run all tests\nmake test     # or pytest\n\n# Test specific components\npytest tests/core/\npytest tests/services/\npytest tests/universities/\n\n# Test with coverage\npytest --cov=uoapi tests/\n\n# Type checking\nmake check    # or mypy src/\n\n# Linting  \nmake lint     # or flake8\n\n# All checks\nmake          # test + lint + typecheck\n```\n\n### Code Quality\nThe refactored codebase maintains high code quality with:\n- **100% type coverage** with mypy\n- **Comprehensive tests** for all components\n- **Consistent formatting** with black\n- **Clean imports** and modular design\n- **Documentation** for all public APIs\n\n## \ud83d\udd04 Migration Guide\n\n### From Old API\n```python\n# Old way\nfrom uoapi.course.course_info import scrape_subjects, get_courses\nsubjects = scrape_subjects()\ncourses = list(get_courses(subjects[0]['link']))\n\n# New way (recommended)\nfrom uoapi.core import University\nfrom uoapi.services import DefaultCourseService\n\nservice = DefaultCourseService()\nsubjects = service.get_subjects(University.UOTTAWA)\ncourses = service.get_courses(University.UOTTAWA, subjects[0].code)\n```\n\n### Legacy Compatibility\n```python\n# Old imports still work\nfrom uoapi.course import scrape_subjects, get_courses  # \u2705 Still works\nfrom uoapi.carleton.discovery import CarletonDiscovery  # \u2705 Still works\nfrom uoapi.server.app import create_app  # \u2705 Still works\n\n# But new imports are cleaner\nfrom uoapi.core import *  # \u2705 New unified models\nfrom uoapi.services import *  # \u2705 Business logic\nfrom uoapi.interfaces.api import create_app  # \u2705 Clean API\n```\n\n## \ud83d\udc1b Troubleshooting\n\n### Common Issues\n\n1. **Import errors**: Ensure Python 3.10+ and proper installation\n2. **University not supported**: Check `service.get_all_universities()`\n3. **Term validation**: Use `timetable_service.get_available_terms()` first\n4. **Rate limiting**: Reduce concurrent workers if getting blocked\n5. **Live data not available**: Both Carleton and UOttawa support live timetable data\n\n### Debug Mode\n```python\nimport logging\nlogging.basicConfig(level=logging.DEBUG)\n\n# Or via CLI\nuoapi --verbose course -u carleton -c COMP\n```\n\n### Configuration Issues\n```python\nfrom uoapi.utils import get_config, reload_config\n\n# Check current config\nconfig = get_config()\nprint(config.to_dict())\n\n# Reload with different environment\nreload_config(\"development\")\n```\n\n## \ud83c\udfaf What's New in v3.2+\n\n### Major Enhancements\n- **\ud83d\udd27 Enhanced Section Parsing**: Complete retrieval of all course sections, lectures, tutorials, and labs\n- **\ud83c\udfa8 Clean CLI Interface**: Simplified commands with intuitive structure (`schedulo` instead of complex nested commands)\n- **\u26a1 Improved Data Accuracy**: Fixed Banner system parsing to capture all available course sections  \n- **\ud83d\ude80 Better User Experience**: Streamlined commands and comprehensive section information\n- **\ud83d\udc68\u200d\ud83c\udfeb Professor Ratings**: New `schedulo professor` command with Rate My Professor integration\n- **\ud83d\udcda Catalog Access**: New `--catalog` option to browse complete course catalogs without term requirements\n- **\ud83c\udfaf Smart Subject Validation**: University-specific subject code validation (4-letter for Carleton, 3-letter for UOttawa)\n\n### Architecture Improvements\n- **\ud83c\udfd7\ufe0f Clean Architecture**: Proper layered design with separation of concerns\n- **\ud83d\udd27 Service Layer**: Business logic separated from data access\n- **\ud83c\udfaf Single Responsibility**: Each module has one clear purpose  \n- **\ud83d\udd04 Dependency Inversion**: High-level modules don't depend on low-level details\n\n### Developer Experience\n- **\u2705 Type Safety**: Complete type annotations with Pydantic\n- **\ud83e\uddea Better Testing**: Clear boundaries enable comprehensive testing\n- **\ud83d\udcda Better Documentation**: Comprehensive examples and API docs\n- **\ud83d\udd27 Easy Extension**: Add new universities via simple interfaces\n\n### User Experience  \n- **\ud83c\udfa8 Consistent APIs**: Same patterns across all universities\n- **\u26a1 Better Performance**: Improved caching and parallel processing\n- **\ud83d\udd0d Better Error Messages**: Structured exceptions with helpful details\n- **\ud83d\udcca Richer Data**: Enhanced models with metadata and validation\n\n## \ud83e\udd1d Contributing\n\nWe welcome contributions! The new architecture makes it much easier to contribute:\n\n1. **Add Universities**: Implement `UniversityProvider` interface\n2. **Add Features**: Extend service classes with new functionality  \n3. **Add Interfaces**: Create new CLI commands or API endpoints\n4. **Fix Bugs**: Clear modular structure makes debugging easier\n\n### Contribution Process\n```bash\n# 1. Fork and clone\ngit clone https://github.com/your-username/uoapi.git\n\n# 2. Create feature branch\ngit checkout -b feature/my-feature\n\n# 3. Make changes and test\nmake test\nmake lint\n\n# 4. Submit PR\ngit push origin feature/my-feature\n```\n\n## \ud83d\udcdc License\n\nGNU LGPLv3.0 - See the `COPYING` and `COPYING.LESSER` files for details.\n\n## \ud83d\ude4f Acknowledgments\n\n- Original [uoapi](https://github.com/andrewnags/uoapi) by Andrew Nagarajah\n- University of Ottawa and Carleton University for public data access\n- Rate My Professor for their API\n- The Python community for excellent libraries and tools\n\n---\n\n**Ready to explore university course data with enhanced section parsing and clean CLI?** \n```bash\npip install schedulo-api\nschedulo terms carleton  # Get started!\n```\n",
    "bugtrack_url": null,
    "license": "LGPL-3.0-or-later",
    "summary": "An API for retrieving public data from the University of Ottawa and Carleton University.",
    "version": "3.4.1",
    "project_urls": {
        "Bug Reports": "https://github.com/Rain6435/uoapi/issues",
        "Documentation": "https://github.com/Rain6435/uoapi#readme",
        "Homepage": "https://github.com/Rain6435/uoapi",
        "Issues": "https://github.com/Rain6435/uoapi/issues",
        "Release Notes": "https://github.com/Rain6435/uoapi/releases",
        "Repository": "https://github.com/Rain6435/uoapi",
        "Source Code": "https://github.com/Rain6435/uoapi"
    },
    "split_keywords": [
        "university",
        " ottawa",
        " carleton",
        " courses",
        " api",
        " scraping",
        " education"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "a9de63e8f1fb43111ee3651d2383310c825fc9a6fc524abf1840492fdd4786b0",
                "md5": "a185232a3f117ac79c82d83b86e64e7c",
                "sha256": "bed01a6a786055456cc1a28abf79f3dada4f2c119aa7a28dcab65d8f366e0a24"
            },
            "downloads": -1,
            "filename": "schedulo_api-3.4.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a185232a3f117ac79c82d83b86e64e7c",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10.0",
            "size": 1801470,
            "upload_time": "2025-09-07T02:32:53",
            "upload_time_iso_8601": "2025-09-07T02:32:53.829334Z",
            "url": "https://files.pythonhosted.org/packages/a9/de/63e8f1fb43111ee3651d2383310c825fc9a6fc524abf1840492fdd4786b0/schedulo_api-3.4.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "562de93dd4b35bb818e0b916b1260f62c26c261dbacc61eaf812d7505e248053",
                "md5": "97161b8b578ae4b7fb1f0ffbcccc0150",
                "sha256": "66546ac4aa3d665ab64d21bb3bfe7a3fd468bc0568f449ace50c24e40d9a632e"
            },
            "downloads": -1,
            "filename": "schedulo_api-3.4.1.tar.gz",
            "has_sig": false,
            "md5_digest": "97161b8b578ae4b7fb1f0ffbcccc0150",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10.0",
            "size": 4768871,
            "upload_time": "2025-09-07T02:32:56",
            "upload_time_iso_8601": "2025-09-07T02:32:56.530263Z",
            "url": "https://files.pythonhosted.org/packages/56/2d/e93dd4b35bb818e0b916b1260f62c26c261dbacc61eaf812d7505e248053/schedulo_api-3.4.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-09-07 02:32:56",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Rain6435",
    "github_project": "uoapi",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "schedulo-api"
}
        
Elapsed time: 1.39897s