|
| 1 | +# CLAUDE.md — cognitive3dpy contributor guide |
| 2 | + |
| 3 | +## Dev environment |
| 4 | + |
| 5 | +The project uses `uv`. To set up: |
| 6 | + |
| 7 | +```bash |
| 8 | +uv sync --all-extras |
| 9 | +``` |
| 10 | + |
| 11 | +This installs the package, all dev dependencies, and the optional pandas extra. |
| 12 | + |
| 13 | +## Running tests |
| 14 | + |
| 15 | +```bash |
| 16 | +uv run pytest tests/ -q # all tests |
| 17 | +uv run pytest tests/test_transform.py -q # single file |
| 18 | +``` |
| 19 | + |
| 20 | +Tests use `respx` to mock HTTP calls — no real API credentials needed. |
| 21 | + |
| 22 | +## Architecture |
| 23 | + |
| 24 | +All data flows through a shared pipeline in `_transform.py`: |
| 25 | + |
| 26 | +``` |
| 27 | +API JSON → pl.DataFrame → normalize_columns() → coerce_types() → [join_scene_names()] → [select_compact()] → to_output() |
| 28 | +``` |
| 29 | + |
| 30 | +- `_transform.py` — all DataFrame transformation logic; the first place to look for type/schema issues |
| 31 | +- `_client.py` — HTTP client, auth headers, error handling |
| 32 | +- `_pagination.py` — session list pagination |
| 33 | +- `_lookups.py` — metadata resolution (scenes, objects, objectives); results are cached by project_id |
| 34 | +- `_filters.py` — builds the session filter payload sent to the API |
| 35 | +- Each public function lives in its own module (`sessions.py`, `events.py`, etc.) |
| 36 | + |
| 37 | +## Key conventions |
| 38 | + |
| 39 | +- **Polars internally, pandas only at the boundary** — all processing uses `polars.DataFrame`; `to_output()` is the only place pandas conversion happens |
| 40 | +- **Column naming** — `normalize_columns()` converts everything to `snake_case`; `c3d.*` API properties become `c3d_*` (e.g. `c3d.metrics.fps_score` → `c3d_metrics_fps_score`) |
| 41 | +- **Numeric metric columns are always Float64** — `coerce_types()` casts all `c3d_metrics_*`, `c3d_metric_components_*`, and `c3d_roomsize_meters` columns to `pl.Float64`, even when the API returns whole numbers as integers |
| 42 | +- **Empty DataFrames have explicit schemas** — functions return typed empty frames (not schema-less) when no data is found |
| 43 | +- **Date inputs** — all public functions accept `date`/`datetime` objects, epoch timestamps (int/float seconds), or `"YYYY-MM-DD"` strings |
| 44 | + |
| 45 | +## Release process |
| 46 | + |
| 47 | +Releases are fully automated via `python-semantic-release` on push to `main`. Use conventional commits: |
| 48 | + |
| 49 | +- `fix:` → patch bump |
| 50 | +- `feat:` → minor bump |
| 51 | +- `feat!:` or `BREAKING CHANGE:` footer → major bump |
| 52 | +- `chore:` / `docs:` → no release |
| 53 | + |
| 54 | +The CI workflow (`release.yml`) determines the next version, creates a git tag, builds with `uv`, and publishes to PyPI automatically. Do not manually edit the version in `pyproject.toml`. |
0 commit comments