Skip to main content

Timeback Caliper client for learning analytics events

Project description

timeback-caliper

Python client for sending Caliper learning analytics events to Timeback.

Installation

# pip
pip install timeback-caliper

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

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

Quick Start

from timeback_caliper import (
    CaliperClient,
    ActivityCompletedInput,
    TimebackUser,
    TimebackActivityContext,
    TimebackApp,
    TimebackActivityMetric,
)

# Initialize client
client = CaliperClient(
    env="staging",  # or "production"
    client_id="your-client-id",
    client_secret="your-client-secret",
)

# Send an activity completed event
result = await client.events.send_activity(
    sensor_id="https://myapp.example.com/sensors/main",
    input=ActivityCompletedInput(
        actor=TimebackUser(
            id="https://example.edu/users/123",
            email="student@example.edu",
        ),
        object=TimebackActivityContext(
            id="https://myapp.example.com/activities/456",
            subject="Math",
            app=TimebackApp(name="My Learning App"),
        ),
        metrics=[
            TimebackActivityMetric(type="totalQuestions", value=10),
            TimebackActivityMetric(type="correctQuestions", value=8),
            TimebackActivityMetric(type="xpEarned", value=150),
        ],
    ),
)

# Wait for processing
status = await client.jobs.wait_for_completion(result.job_id)
print(f"Processed {status.events_processed} events")

FastAPI Integration

from fastapi import FastAPI, HTTPException
from timeback_caliper import (
    CaliperClient,
    ActivityCompletedInput,
    APIError,
)

app = FastAPI()

# Initialize client (reuse across requests)
caliper = CaliperClient(
    env="staging",
    client_id="...",
    client_secret="...",
)


@app.post("/api/activity")
async def submit_activity(input: ActivityCompletedInput):
    """Submit a learning activity event."""
    try:
        result = await caliper.events.send_activity(
            sensor_id="https://myapp.example.com/sensors/main",
            input=input,
        )
        return {"success": True, "job_id": result.job_id}
    except APIError as e:
        raise HTTPException(status_code=e.status_code or 500, detail=str(e))


@app.on_event("shutdown")
async def shutdown():
    await caliper.close()

Event Types

ActivityCompletedEvent

Records when a student completes an activity with performance metrics:

from timeback_caliper import (
    ActivityCompletedInput,
    TimebackUser,
    TimebackActivityContext,
    TimebackApp,
    TimebackCourse,
    TimebackActivityMetric,
)

input = ActivityCompletedInput(
    actor=TimebackUser(
        id="https://example.edu/users/123",
        email="student@example.edu",
        name="Jane Doe",
        role="student",
    ),
    object=TimebackActivityContext(
        id="https://myapp.example.com/activities/456",
        subject="Math",
        app=TimebackApp(name="My Learning App"),
        course=TimebackCourse(name="Algebra 101"),
    ),
    metrics=[
        TimebackActivityMetric(type="totalQuestions", value=10),
        TimebackActivityMetric(type="correctQuestions", value=8),
        TimebackActivityMetric(type="xpEarned", value=150),
        TimebackActivityMetric(type="masteredUnits", value=1),
    ],
)

result = await client.events.send_activity(sensor_id, input)

TimeSpentEvent

Records time spent on an activity:

from timeback_caliper import TimeSpentInput, TimeSpentMetric

input = TimeSpentInput(
    actor=TimebackUser(id="...", email="..."),
    object=TimebackActivityContext(id="...", subject="Reading", app=TimebackApp(name="...")),
    metrics=[
        TimeSpentMetric(type="active", value=1800),    # 30 minutes
        TimeSpentMetric(type="inactive", value=300),   # 5 minutes
    ],
)

result = await client.events.send_time_spent(sensor_id, input)

Job Tracking

Events are processed asynchronously. Track processing status:

# Get job status
status = await client.jobs.get_status(job_id)
print(f"Status: {status.status}")

# Wait for completion (with timeout)
status = await client.jobs.wait_for_completion(
    job_id,
    timeout=60.0,      # Max wait time
    poll_interval=1.0, # Check every second
)

if status.status == "completed":
    print(f"Processed {status.events_processed} events")
elif status.status == "failed":
    print(f"Failed: {status.error}")

Context Manager

Use the client as an async context manager for automatic cleanup:

async with CaliperClient(client_id="...", client_secret="...") as client:
    await client.events.send_activity(sensor_id, input)
# Client is automatically closed

Error Handling

from timeback_caliper import (
    CaliperClient,
    AuthenticationError,
    APIError,
)

try:
    result = await client.events.send_activity(sensor_id, input)
except AuthenticationError:
    print("Invalid credentials")
except APIError as e:
    print(f"API error ({e.status_code}): {e}")

Configuration

Parameter Type Default Description
env str None "staging" or "production"
base_url str auto Override API URL
auth_url str auto Override auth URL
client_id str env var OAuth2 client ID
client_secret str env var OAuth2 client secret
timeout float 30.0 Request timeout in seconds

Environment Variables

CALIPER_CLIENT_ID=your-client-id
CALIPER_CLIENT_SECRET=your-client-secret
CALIPER_BASE_URL=https://api.staging.timeback.com
CALIPER_TOKEN_URL=https://auth.staging.timeback.com/oauth2/token

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_caliper-0.2.1b20260311201424.tar.gz (30.6 kB view details)

Uploaded Source

Built Distribution

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

timeback_caliper-0.2.1b20260311201424-py3-none-any.whl (29.3 kB view details)

Uploaded Python 3

File details

Details for the file timeback_caliper-0.2.1b20260311201424.tar.gz.

File metadata

  • Download URL: timeback_caliper-0.2.1b20260311201424.tar.gz
  • Upload date:
  • Size: 30.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","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_caliper-0.2.1b20260311201424.tar.gz
Algorithm Hash digest
SHA256 aae7c5cebd4bc0041477f11de611b3e87888bac51dd9e8745b3618f431c4d979
MD5 fea0ef9650c744e5ce793f527be5707c
BLAKE2b-256 51aff81d80e3e8b31cdf1fb9a2f1475178c12d0cc231d8a7dc9bb3ef0c042021

See more details on using hashes here.

File details

Details for the file timeback_caliper-0.2.1b20260311201424-py3-none-any.whl.

File metadata

  • Download URL: timeback_caliper-0.2.1b20260311201424-py3-none-any.whl
  • Upload date:
  • Size: 29.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","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_caliper-0.2.1b20260311201424-py3-none-any.whl
Algorithm Hash digest
SHA256 a222b92419ad3f963dddb5e73088dfa666f502d42742edf68fdcc71a05601900
MD5 1fb7e650a8510c7bd6dee5e928648eff
BLAKE2b-256 e3572e9f74ae560f3bf62c0a54ba2c4f70d0af5c8d6141f9dbeb3e0454972b84

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