Metadata-Version: 2.4
Name: babon
Version: 1.2.0
Summary: Babon kinematics-as-a-service client. Video in, movement data out.
Author-email: "Babon Innovations B.V." <daan@babon.eu>
License: MIT
Project-URL: Homepage, https://babon.eu
Project-URL: Documentation, https://api.babon.eu/api/v1/docs
Keywords: biomechanics,kinematics,gait,video-analysis
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28
Provides-Extra: async
Requires-Dist: httpx>=0.27; extra == "async"
Provides-Extra: pandas
Requires-Dist: pandas>=2.0; extra == "pandas"
Provides-Extra: numpy
Requires-Dist: numpy>=1.24; extra == "numpy"
Provides-Extra: stream
Requires-Dist: websockets>=13; extra == "stream"

# babon

Kinematics-as-a-service client. Video in, movement data out.

```bash
pip install babon
```

```python
from babon import Babon

client = Babon()                                              # picks up BABON_API_KEY
analysis = client.analyses.create(video="trial.mp4", wait=True)
print(analysis.metrics["gait_speed_m_s"])                     # 1.32
print(analysis.angles)                                        # DataFrame
```

That's it. Three lines.

## A bit more

```python
# Fire and forget, retrieve later
analysis = client.analyses.create(video="trial.mp4")
# ... save analysis.id somewhere, exit, come back ...
analysis = client.analyses.retrieve("YOUR_ANALYSIS_ID")
print(analysis.status)                                        # queued|processing|completed|failed|canceled

# Cohort
batch = client.batches.create(requests=[
    {"video": "a.mp4"},
    {"video": "b.mp4", "label": "cohort-3"},
    {"video": "c.mp4", "model": "gemx"},
])
for analysis in batch.results(as_completed=True):             # yields as each finishes
    save(analysis.angles)

# Lazy projections (each is one cheap HTTP GET, cached per instance)
analysis.angles                       # DataFrame, all joints
analysis.angles(joint="knee_R")       # filtered
analysis.grf                          # GRF waveform, None for non-walking clips
analysis.events                       # heel-strike / toe-off
analysis.metrics                      # cadence, gait_speed, symmetry_index, ...
analysis.quality                      # tier + z-scores
analysis.skeleton                     # 3D viewer payload
analysis.download("out.zip", include=["angles", "grf"])

# Big files? The SDK auto-switches to presigned PUT above 100 MB.
client.analyses.create(video="4gb_video.mp4")                 # transparent
```

## CLI

```bash
babon login                                                   # interactive: paste key
babon analyses create trial.mp4 --wait
babon analyses get YOUR_ANALYSIS_ID
babon analyses list --status completed --limit 20
babon batches create ./study/ --label baseline
babon usage                                                   # seconds used this month
```

## Errors

The client raises typed exceptions:

- `BabonAuthError` — bad key, wrong environment, DPA not accepted.
- `BabonQuotaExceeded` — monthly limit, concurrent limit, or rate-limited.
- `BabonInvalidVideo` — file is not a recognised container.
- `BabonInvalidLabel` — label looks like a real name (ADR-0002).
- `BabonRunFailed` — pipeline failed.
- `BabonTimeout` — `wait()` exceeded its timeout.

All carry `.code` (the API error code) and `.request_id` (the support handle).

The HTTP transport retries 429 (honoring `Retry-After`), 5xx, and network errors with exponential backoff. POST requests are only retried when sent with `Idempotency-Key` (the SDK auto-generates one per file). 4xx errors raise immediately.

## What we are and are not

- Measurement only. Output is kinematics; clinical interpretation is yours.
- EU-cloud (Scaleway fr-par). Videos never leave the EU.
- Don't put real patient names in `label` or in your filenames. We reject name-shaped labels server-side.

## Configure

```python
client = Babon(
    key="YOUR_KEY",                       # optional, else BABON_API_KEY env
    base_url="https://api.babon.eu",      # optional, override for staging
    timeout_s=600,                        # optional, per-request timeout
    max_retries=5,                        # optional, set 0 to disable retries
)
```

## v0.x → v1.0 migration

The v0.x surface (`bb.submit`, `bb.runs.list`, `Run.data`, `Run.angles("knee")`) is preserved as a thin shim — your existing code keeps working — but new code should target the v1 resource verbs (`client.analyses.create`, `client.analyses.list`, `Analysis.metrics`, `Analysis.angles(joint="knee_R")`). The v0.x shims will be removed in v2.0.

## Contact

daan@babon.eu — onboarding, quota, bugs.

Docs: <https://app.babon.eu/developers/docs>
