Files
Davide Scaini 395182649b improve docs
2026-04-15 23:07:52 +02:00

8.2 KiB

Developer Guide

This guide is for developers contributing to BincioActivity.

Prerequisites

  • Python 3.12+ with uv
  • Node 20+ with npm
  • Git

Local Setup

git clone https://github.com/brutsalvadi/bincio-activity.git
cd bincio-activity

# Install Python dependencies
uv sync

# Install optional extras for multi-user development
uv sync --extra serve --extra edit

# Install Node dependencies (for the site)
cd site && npm install && cd ..

Running Locally

Single-user (fastest for testing extract logic)

# Configure where to find your test activities
cp extract_config.example.yaml extract_config.yaml
$EDITOR extract_config.yaml  # set input.dirs and output.dir

# Extract activities
uv run bincio extract

# Start the dev server (no login, no API server)
uv run bincio dev --data-dir ~/bincio_data
# → http://localhost:4321/u/{handle}/

Multi-user (for testing auth, write API, admin features)

# Create a test instance with an admin user
uv run bincio init --data-dir /tmp/bincio_test --handle testadmin

# Extract activities
uv run bincio extract --output /tmp/bincio_test

# Start everything (bincio serve + astro dev)
uv run bincio dev --data-dir /tmp/bincio_test
# → http://localhost:4321 (login with testadmin/{password})

Ctrl+C stops both servers.

Running Tests

# All tests
uv run pytest

# Specific test file
uv run pytest tests/extract/test_parsers.py

# Specific test function
uv run pytest tests/extract/test_parsers.py::test_gpx_parser

# With verbose output
uv run pytest -vv

# With coverage report
uv run pytest --cov=bincio

Tests are in tests/ and use pytest + fixtures for DRY test data.

Project Structure

bincio/
  extract/           Python package for GPX/FIT/TCX parsing
    models.py        DataPoint, ParsedActivity, LapData
    parsers/         GPX, FIT, TCX parsers + factory
    sport.py         Sport name normalization
    metrics.py       Haversine-based stats (distance, elevation)
    timeseries.py    1Hz downsampling → BAS timeseries object
    simplify.py      RDP track simplification (no external deps)
    dedup.py         Exact + fuzzy duplicate detection
    strava_csv.py    Strava activities.csv importer
    writer.py        BAS JSON + GeoJSON output
    config.py        extract_config.yaml loader
    cli.py           `bincio extract` command
  render/
    cli.py           `bincio render` command
    merge.py         Sidecar edit overlay (produces _merged/)
  edit/
    cli.py           `bincio edit` FastAPI server
    server.py        Edit API endpoints
  serve/
    cli.py           `bincio serve` command
    server.py        Multi-user FastAPI server (auth, invites, admin)
    db.py            SQLite data layer
    init_cmd.py      `bincio init` bootstrap
  shared/            (if needed)

site/                Astro + Svelte + Tailwind frontend
  src/
    layouts/         Base.astro (auth wall, nav)
    pages/           Routes (activity feed, detail, login, etc.)
    components/      Svelte components (maps, charts, edit drawer)
    lib/             TypeScript utilities (types, format, dataloader)

tests/               pytest test suite
  extract/
  render/
  serve/
  fixtures/          Shared test data

Key Concepts

BAS (BincioActivity Schema)

Activity data flows as BAS JSON files in {user}/activities/. The format is specified in SCHEMA.md.

Key files:

  • {id}.json — activity metadata + timeseries
  • _merged/ symlink — sidecar edits overlaid on activities
  • edits/{id}.md — user-created sidecar (optional)

Shard model

Multi-user instances use a shard manifest (root index.json) that lists per-user shards. The browser fetches all shards concurrently and merges them. This allows:

  • Federation (remote shard URLs)
  • Yearly pagination
  • No data duplication

Extract pipeline

GPX/FIT/TCX files
    ↓ (parse)
ParsedActivity
    ↓ (calculate metrics)
BAS Activity JSON
    ↓ (downsample to 1Hz)
Timeseries
    ↓ (simplify with RDP)
GeoJSON
    ↓ (write)
activities/{id}.json + activities/{id}.geojson

Render pipeline

{user}/
  activities/*.json (extracted)
  edits/*.md (user sidecars)
    ↓ (merge_all)
_merged/
  index.json (sidecar edits applied)
  activities/{id}.json
  {id}.geojson
    ↓ (astro build)
site/dist/

Editing does not require re-extraction.

Making Changes

Adding a new endpoint

  1. Add a route in bincio/serve/server.py (or bincio/edit/server.py for single-user)
  2. Add Pydantic models for request/response if needed
  3. Add tests in tests/serve/
  4. Update docs/reference/api.md with the new endpoint
  5. If admin-only, protect it with await _require_admin(bincio_session)

Adding a parser for a new format

  1. Create bincio/extract/parsers/myformat.py
  2. Implement a parser class with parse(file_path: Path) -> ParsedActivity
  3. Register it in bincio/extract/parsers/__init__.py
  4. Add tests in tests/extract/test_parsers.py

Modifying BAS schema

  1. Edit schema/bas-v1.schema.json (JSON Schema)
  2. Update SCHEMA.md (human-readable spec)
  3. Update TypeScript types in site/src/lib/types.ts
  4. Add a migration if the change is breaking

Frontend changes

Svelte components are in site/src/components/. Key ones:

  • ActivityFeed.svelte — activity grid + filters
  • ActivityDetail.svelte — activity page (maps, charts, photos)
  • EditDrawer.svelte — sidecar editor

Use uv run bincio dev to test changes live. The site hot-reloads on file changes.

Code Style

  • Python: PEP 8, type hints where possible
  • JavaScript/TypeScript: ESLint + Prettier (configured in site/)
  • Svelte: No self-closing non-void tags; interactive divs need role + keyboard handler

Git Workflow

  1. Create a branch: git checkout -b feature/my-feature
  2. Make changes and test locally
  3. Commit: git commit -m "Clear, specific commit message"
  4. Push: git push origin feature/my-feature
  5. Open a pull request

Commit message style:

  • Imperative mood ("add feature", not "added feature")
  • Reference issues if relevant: "fix #123"
  • First line ≤ 50 characters
  • Blank line, then detailed explanation if needed

Performance Considerations

Extract speed

  • ProcessPoolExecutor with initializer — large data (Strava lookups, hash sets) is sent once per worker, not per task
  • Haversine — 10x faster than geopy for distance calculations
  • Lazy parsing — FIT files decoded only once per task

Render speed

  • RDP simplification — custom implementation (no external wheels for Pyodide)
  • Gzip compression — activity JSON and geojson are served gzipped
  • Concurrent shard fetch — browser loads all shards in parallel

Frontend

  • MapLibre GL v5 — requires explicit center/zoom and workarounds
  • Observable Plot — use hyphenated curve names (e.g. "monotone-x")
  • Client-only for complex components — use client:only="svelte" for activity detail to avoid hydration mismatches

Debugging

Python

# Interactive debugger
uv run python -m pdb -m bincio.extract.cli

# Or use breakpoint() in code
breakpoint()
uv run bincio extract

TypeScript

Check your editor's TypeScript integration. The site has strict tsconfig.json.

Frontend

  • Open DevTools (F12)
  • Check the Network tab for API calls
  • Check Console for client-side errors

Database

# Inspect the SQLite database directly
sqlite3 /tmp/bincio_test/instance.db
> SELECT * FROM users;

Documentation

  • User-facing docs go in docs/
  • API docs are auto-generated from FastAPI routes (and should be typed with Pydantic models)
  • Code comments should explain why, not what

Known Issues & Limitations

See the GitHub repository for known issues and planned features.

Contributing

Contributions are welcome! Please:

  1. Check existing issues/PRs so you're not duplicating work
  2. Open an issue first for large changes
  3. Include tests for new features
  4. Update docs (user guide, API ref, or developer guide)
  5. Follow the code style guidelines

See also