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.1b20260313233319.tar.gz (35.8 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.1b20260313233319-py3-none-any.whl (32.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: timeback_caliper-0.2.1b20260313233319.tar.gz
  • Upload date:
  • Size: 35.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","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.1b20260313233319.tar.gz
Algorithm Hash digest
SHA256 d52b80d1a4529b7ea0b08fa19093ec6da7189cbe8b67aa630cc859b82536f407
MD5 fe21576ccd3ea2428473f98164c79ba7
BLAKE2b-256 beb4e1c3046c8914750ed22428401a365346117234d189cd050d0a1ba7c1f178

See more details on using hashes here.

File details

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

File metadata

  • Download URL: timeback_caliper-0.2.1b20260313233319-py3-none-any.whl
  • Upload date:
  • Size: 32.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","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.1b20260313233319-py3-none-any.whl
Algorithm Hash digest
SHA256 ef274d4510bd1c2a0b1120a713c4cbd1b651b3af7ced528c7695e797f5d729b9
MD5 f5148af290bf1d01154a74edb281262d
BLAKE2b-256 4ec89cd54dca23bab471b072280cced8707fa347fb17676cd2334b832dfcd78f

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