Skip to main content

Timeback OneRoster v1.2 client for rostering and gradebook APIs

Project description

timeback-oneroster

Python client for the OneRoster v1.2 API.

Installation

# pip
pip install timeback-oneroster

# uv (add to a project)
uv add timeback-oneroster

# uv (install into current environment)
uv pip install timeback-oneroster

Quick Start

from timeback_oneroster import OneRosterClient

async def main():
    client = OneRosterClient(
        env="staging",  # or "production"
        client_id="your-client-id",
        client_secret="your-client-secret",
    )

    # List all schools
    schools = await client.schools.list()
    for school in schools:
        print(school.name)

    # Get a specific user
    user = await client.users.get("user-sourced-id")
    print(f"{user.given_name} {user.family_name}")

    await client.close()

Client Structure

client = OneRosterClient(options)

# Rostering
client.users        # All users
client.students     # Students (filtered users)
client.teachers     # Teachers (filtered users)
client.classes      # Classes
client.schools      # Schools
# client.courses    # Coming soon
# client.enrollments # Coming soon
# client.terms      # Coming soon

Resource Operations

Each resource supports:

# List all items
users = await client.users.list()

# List with type-safe filtering (recommended)
active_teachers = await client.users.list(
    where={"status": "active", "role": "teacher"}
)

# With operators
teachers_or_aides = await client.users.list(
    where={"role": {"in_": ["teacher", "aide"]}}
)

# Not equal
non_deleted = await client.users.list(
    where={"status": {"ne": "deleted"}}
)

# Sorting
sorted_users = await client.users.list(
    where={"status": "active"},
    sort="familyName",
    order_by="asc",
)

# Legacy filter string (still supported)
active_users = await client.users.list(filter="status='active'")

# Get by sourcedId
user = await client.users.get("user-id")

# Create (where supported)
create_result = await client.classes.create({
    "title": "Math 101",
    "course": {"sourcedId": "course-id"},
    "school": {"sourcedId": "school-id"},
})
print(create_result.sourced_id_pairs.allocated_sourced_id)

# Update (where supported)
await client.classes.update("class-id", {"title": "Math 102"})

# Delete (where supported)
await client.classes.delete("class-id")

Nested Resources

# Schools
classes = await client.schools("school-id").classes()
students = await client.schools("school-id").students()
teachers = await client.schools("school-id").teachers()
courses = await client.schools("school-id").courses()

# Classes
students = await client.classes("class-id").students()
teachers = await client.classes("class-id").teachers()
enrollments = await client.classes("class-id").enrollments()

# Enroll a student
await client.classes("class-id").enroll({"sourcedId": "student-id", "role": "student"})

# Users
classes = await client.users("user-id").classes()
demographics = await client.users("user-id").demographics()

# Students / Teachers
classes = await client.students("student-id").classes()
classes = await client.teachers("teacher-id").classes()

Filtering

The client supports type-safe filtering with the where parameter:

# Simple equality
users = await client.users.list(where={"status": "active"})

# Multiple conditions (AND)
users = await client.users.list(
    where={"status": "active", "role": "teacher"}
)

# Operators
users = await client.users.list(where={"score": {"gte": 90}})        # >=
users = await client.users.list(where={"score": {"gt": 90}})         # >
users = await client.users.list(where={"score": {"lte": 90}})        # <=
users = await client.users.list(where={"score": {"lt": 90}})         # <
users = await client.users.list(where={"status": {"ne": "deleted"}}) # !=
users = await client.users.list(where={"email": {"contains": "@school.edu"}})  # substring

# Match any of multiple values (OR)
users = await client.users.list(
    where={"role": {"in_": ["teacher", "aide"]}}
)

# Exclude multiple values
users = await client.users.list(
    where={"status": {"not_in": ["deleted", "inactive"]}}
)

# Explicit OR across fields
users = await client.users.list(
    where={"OR": [{"role": "teacher"}, {"status": "active"}]}
)

Pagination

For large datasets, use streaming:

# Collect all users
all_users = await client.users.stream().to_list()

# With limits
first_100 = await client.users.stream(max_items=100).to_list()

# With filtering
active_users = await client.users.stream(
    where={"status": "active"}
).to_list()

# Get first item only
first_user = await client.users.stream().first()

Configuration

OneRosterClient(
    # Environment-based (recommended)
    env="production",  # or "staging"
    client_id="...",
    client_secret="...",

    # Or explicit URLs
    base_url="https://api.example.com",
    auth_url="https://auth.example.com/oauth2/token",
    client_id="...",
    client_secret="...",

    # Optional
    timeout=30.0,  # Request timeout in seconds
)

Environment Variables

If credentials are not provided explicitly, the client reads from:

  • ONEROSTER_CLIENT_ID
  • ONEROSTER_CLIENT_SECRET
  • ONEROSTER_BASE_URL (optional)
  • ONEROSTER_TOKEN_URL (optional)

Error Handling

from timeback_oneroster import OneRosterError, NotFoundError, AuthenticationError

try:
    user = await client.users.get("invalid-id")
except NotFoundError as e:
    print(f"User not found: {e.sourced_id}")
except AuthenticationError:
    print("Invalid credentials")
except OneRosterError as e:
    print(f"API error: {e}")

Async Context Manager

async with OneRosterClient(client_id="...", client_secret="...") as client:
    schools = await client.schools.list()
# Client is automatically closed

FastAPI Integration

from fastapi import FastAPI, Depends
from timeback_oneroster import OneRosterClient

app = FastAPI()

async def get_oneroster():
    client = OneRosterClient(
        env="production",
        client_id="...",
        client_secret="...",
    )
    try:
        yield client
    finally:
        await client.close()

@app.get("/schools")
async def list_schools(client: OneRosterClient = Depends(get_oneroster)):
    return await client.schools.list()

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

timeback_oneroster-0.1.10b20260226174051.tar.gz (41.2 kB view details)

Uploaded Source

Built Distribution

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

File details

Details for the file timeback_oneroster-0.1.10b20260226174051.tar.gz.

File metadata

  • Download URL: timeback_oneroster-0.1.10b20260226174051.tar.gz
  • Upload date:
  • Size: 41.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for timeback_oneroster-0.1.10b20260226174051.tar.gz
Algorithm Hash digest
SHA256 65eae8b103f6c1a816aad98e9d1ab6a885cd4bb631e0b19bd3743759d3153c5a
MD5 c553c02edb86a5f56f617a1bb2eeb4ce
BLAKE2b-256 5b93916cc209eb10ceeaf08f668aabd11cb646eb077152b44555dcfd8706000b

See more details on using hashes here.

File details

Details for the file timeback_oneroster-0.1.10b20260226174051-py3-none-any.whl.

File metadata

  • Download URL: timeback_oneroster-0.1.10b20260226174051-py3-none-any.whl
  • Upload date:
  • Size: 57.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for timeback_oneroster-0.1.10b20260226174051-py3-none-any.whl
Algorithm Hash digest
SHA256 8f09d051e857fc71981973e873c12511a04fa16bc04d2d22d91825a4a71849ca
MD5 29ef2c723f64436c48d2656e517cd017
BLAKE2b-256 d63f9554a85242f98ee6afec3b297c6a9b28c043f0b1eb0943ac928fc3460ab1

See more details on using hashes here.

Supported by

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