# 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.**
[](https://badge.fury.io/py/schedulo-api)
[](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[](https://badge.fury.io/py/schedulo-api)\n[](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"
}