A lightweight wrapper for the Aalto University Sisu API
Project description
Sisu Wrapper
A Python library and REST API for the Aalto University Sisu system. Access course data, schedules, and study groups programmatically or via HTTP endpoints.
Overview
Sisu Wrapper provides two ways to interact with Aalto University's course data:
- Python Library - Import and use directly in your Python projects
- REST API - Run as a web service with FastAPI for HTTP access
Both components share the same robust core, providing clean access to course units, realisations, and study schedules.
Table of Contents
Features
Core Functionality
- Fetch course units, offerings, and study groups
- Access lecture and exercise schedules
- Connection pooling for efficient API usage
- Robust error handling
- Modern Python with type hints
Python Library
- Zero dependencies except
requests - Clean, intuitive API
- Context manager support
- Comprehensive data models
REST API
- Fast and async with FastAPI
- Auto-generated interactive documentation
- Request validation with Pydantic
- Easy to deploy and integrate
Python Library
Installation
pip install sisu-wrapper
For development:
git clone https://github.com/kctong529/sisukas.git
cd sisu-wrapper
pip install -e ".[dev]"
Usage
Basic Example
from sisu_wrapper import SisuClient, SisuService, SisuAPIError
# Initialize client with custom timeout
client = SisuClient(timeout=15)
service = SisuService(client)
try:
# Fetch complete course offering data
offering = service.fetch_course_offering(
course_unit_id="aalto-OPINKOHD-1125839311-20210801",
offering_id="aalto-CUR-206690-3122470"
)
print(f"Course: {offering.name}")
print(f"Total study groups: {len(offering.study_groups)}")
# Filter by group type
lectures = offering.get_groups_by_type("Lecture")
exercises = offering.get_groups_by_type("Exercise")
except SisuAPIError as e:
print(f"API Error: {e}")
finally:
client.close()
Working with Study Events
# Get all events from a study group
for group in groups:
for event in group.sorted_events:
# Access as datetime objects
start = event.start_datetime
end = event.end_datetime
# Or use the formatted representation
print(event) # "24.02.2026 (Tue) 12:15 - 14:00"
# Raw ISO strings are also available
print(event.start) # "2026-02-24T12:15:00+02:00"
Context Manager (Recommended)
with SisuClient() as client:
service = SisuService(client)
groups = service.fetch_study_groups(
"aalto-OPINKOHD-1125839311-20210801",
"aalto-CUR-206690-3122470"
)
# Connection automatically closed
API Reference
SisuClient
Low-level HTTP client for the Sisu API.
Constructor:
SisuClient(base_url: str | None = None, timeout: int = 10)
Methods:
fetch_course_unit(course_unit_id: str) -> Dict- Fetch course unit metadatafetch_course_realisations(assessment_item_id: str) -> List[Dict]- Fetch course realisationsfetch_study_events(study_event_ids: List[str]) -> List- Fetch study eventsclose()- Close the session
SisuService
High-level service for working with course data.
Constructor:
SisuService(client: SisuClient)
Methods:
fetch_course_offering(course_unit_id: str, offering_id: str) -> CourseOffering- Fetch complete course datafetch_study_groups(course_unit_id: str, offering_id: str) -> List[StudyGroup]- Fetch only study groups
Data Models
StudyEvent
@dataclass
class StudyEvent:
start: str # ISO format datetime
end: str # ISO format datetime
start_datetime: datetime # Property: parsed datetime
end_datetime: datetime # Property: parsed datetime
StudyGroup
@dataclass
class StudyGroup:
group_id: str # Unique group ID
name: str # Group name (e.g., "L01")
type: str # Group type (e.g., "Lecture")
study_events: List[StudyEvent] # List of events
sorted_events: List[StudyEvent] # Property: events sorted by time
CourseOffering
@dataclass
class CourseOffering:
course_unit_id: str # Course unit ID
offering_id: str # Offering/realisation ID
name: str # Course name
assessment_items: List[str] # Assessment item IDs
study_groups: List[StudyGroup] # All study groups
get_groups_by_type(type: str) -> List[StudyGroup] # Filter by type
Development
Architecture Overview
The library follows a layered architecture:
client.py- Low-level HTTP communication with Sisu APIservice.py- Business logic and orchestrationmodels.py- Domain objects (dataclasses)exceptions.py- Custom error types
The FastAPI application (api/) is a thin wrapper that exposes the service layer via HTTP endpoints.
Project Structure
sisu-wrapper/
├── sisu_wrapper/ # Python library (core package)
│ ├── __init__.py # Package exports
│ ├── client.py # HTTP client
│ ├── service.py # Business logic
│ ├── models.py # Data models
│ └── exceptions.py # Custom exceptions
├── tests/ # Test suite
│ └── test_client.py # Client unit tests
├── api/ # FastAPI application
│ ├── __init__.py
│ └── main.py # FastAPI app and routes
├── examples/ # Usage examples
│ └── demo.py # Library demo
├── pyproject.toml # Package configuration
└── README.md # This file
Code Quality
- Type hints: Full type annotation coverage for better IDE support
- Documentation: Comprehensive module and function docstrings
- Error handling: Custom exception hierarchy for granular error handling
- Logging: Structured logging throughout with configurable levels
- Testing: Unit tests with pytest and mock objects
- Standards: Follows PEP 8 and modern Python best practices
Running Tests
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run with verbose output
pytest -v
# Run with coverage
pytest --cov=sisu_wrapper
# Run specific test file
pytest tests/test_client.py
Finding Course IDs
Course unit IDs and offering IDs can be found in courses.json:
{
"id": "aalto-CUR-206690-3122470",
└───────────┬────────────┘
offering_id
"code": "MS-A0108",
"startDate": "2026-02-23",
"endDate": "2026-04-17",
...,
"courseUnitId": "aalto-OPINKOHD-1125839311-20210801",
└─────────────────┬────────────────┘
course_unit_id
...,
"enrolmentStartDate": "2026-01-26",
"enrolmentEndDate": "2026-03-02",
...
}
Limitations
- No location data: Venue/room information is not available through the public API. Multiple events with identical times typically indicate different exam venues.
- Recent offerings only: The published realisations endpoint only returns upcoming or recently active offerings. Historical data requires different endpoints.
- Read-only: This wrapper only supports fetching data, not modifying it.
- Rate limiting: No built-in rate limiting - be respectful of the Sisu API
Requirements
- Python 3.10+
- requests >= 2.32.5
Development Requirements
- pytest >= 7.0
License
This project is licensed under the MIT License. See the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sisu_wrapper-0.1.0.tar.gz.
File metadata
- Download URL: sisu_wrapper-0.1.0.tar.gz
- Upload date:
- Size: 9.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
afb1c21eedebdc3d81614fa324db95ef81eb1d1af65695006d5e02b3a15c7772
|
|
| MD5 |
748d65ff358ea1b7cec262d2b1d93a51
|
|
| BLAKE2b-256 |
a646e611cf4dc95581fb3a89a784c11ab1aca2280554d3b68177d20762d5a86d
|
File details
Details for the file sisu_wrapper-0.1.0-py3-none-any.whl.
File metadata
- Download URL: sisu_wrapper-0.1.0-py3-none-any.whl
- Upload date:
- Size: 10.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4b380e5dcbf5fb50680bed79023df6d8f1ef98e185e0104f72ea9f25a8382522
|
|
| MD5 |
2e2f5736f67857db5625c3e70c124809
|
|
| BLAKE2b-256 |
6f6e4da888c8d9c3e2246916013ea11b63f3dde697a66c650cf0decdb420aea2
|