Files
bincio-activity/CHEATSHEET.md
T
Davide Scaini d2149bf77a docs: fix install instructions to use git clone + uv sync
bincio is not published to PyPI. All CLI invocations use `uv run bincio`.
2026-03-29 16:26:13 +02:00

243 lines
7.0 KiB
Markdown

# BincioActivity — Cheatsheet
## Daily workflow
```bash
# 1. Drop new .fit / .gpx / .tcx files into your input dir, then:
uv run bincio extract
# 2. Rebuild the site (merges any sidecar edits, then builds)
uv run bincio render
# 3. Done — copy site/dist/ to your host
```
---
## Extract
```bash
uv run bincio extract # full run using extract_config.yaml
uv run bincio extract --since 2025-01-01 # only files newer than date
uv run bincio extract --file ride.gpx # single file → JSON on stdout
uv run bincio extract --input ~/rides \
--output ~/bincio_data # override config paths
```
Re-extraction is safe — unchanged files are skipped (hash-based dedup).
To force a full re-extract: `rm -rf ~/bincio_data && uv run bincio extract`
---
## Render
```bash
uv run bincio render # merge edits + production build → site/dist/
uv run bincio render --serve # merge edits + dev server → http://localhost:4321
uv run bincio render --data-dir ~/bincio_data # explicit data dir
```
`bincio render` always runs `merge_all()` first (applies sidecar edits, produces `_merged/`),
then symlinks `site/public/data``_merged/` and runs the Astro build or dev server.
```bash
# Direct npm (skips merge step — use for quick site-only iteration)
cd site
npm run dev
npm run build
npm run preview
```
## Edit
```bash
# Start the edit server (port 4041 by default)
uv run bincio edit --data-dir ~/bincio_data
# Set PUBLIC_EDIT_URL=http://localhost:4041 in site/.env to enable the Edit button
# Then browse to any activity and click Edit — a drawer opens in the same page
```
Saves write a sidecar `.md` to `~/bincio_data/edits/{id}.md` and immediately
trigger a merge. Refresh the page to see the updated content.
### Sidecar format
```markdown
---
title: "Renamed title"
sport: cycling
gear: "Trek Domane"
highlight: true # sort to top of feed
private: false # true = hidden from feed
hide_stats: [cadence] # suppress stat panels
---
Description in **markdown**. Images go in the gallery — drag & drop in the Edit drawer.
```
---
## Python / tests
```bash
uv sync # install / update deps
uv run bincio --help # CLI reference
uv run pytest # full test suite
uv run pytest tests/test_fit.py -x # single file, stop on first fail
uv run pytest -k "sport" # run tests matching keyword
uv run pytest -v # verbose output
```
---
## Data store layout
```
~/bincio_data/
index.json ← feed index (all activities, summaries)
activities/
2024-05-15T08:30:00Z.json ← full detail + 1Hz timeseries
2024-05-15T08:30:00Z.geojson ← simplified GPS track
```
Activity ID format: `YYYY-MM-DDTHH:MM:SSZ` (UTC, always Z suffix).
IDs are stable — safe to use in bookmarks and links.
---
## extract_config.yaml — key fields
```yaml
owner:
handle: yourname
display_name: Your Name
input:
dirs:
- ~/Activities # scanned recursively for GPX/FIT/TCX/.gz
metadata_csv: ~/strava_export/activities.csv # optional
output:
dir: ~/bincio_data
default_privacy: public # public | blur_start | no_gps | private
incremental: true # false = re-process everything
track:
rdp_epsilon: 0.0001 # GPS simplification — larger = fewer points
timeseries_hz: 1 # samples/sec in stored JSON (1 = 1 Hz)
```
---
## Privacy
| Value | Track served | Stats | In index |
|---|---|---|---|
| `public` | Full GPS | ✓ | ✓ |
| `blur_start` | First/last 200 m removed | ✓ | ✓ |
| `no_gps` | None | ✓ | ✓ |
| `private` | None | ✗ | ✗ |
Set per-activity in a sidecar `.md` file, or globally via `default_privacy`.
---
## Sports
Canonical sport values: `cycling` `running` `hiking` `walking` `swimming` `skiing` `other`
Sub-sports: `road` `mountain` `gravel` `indoor` `trail` `track` `nordic`
FIT files: sport is read from the `sport` frame, with `session` frame as fallback.
Strava CSV: `Activity Type` column overrides the FIT-detected sport (authoritative).
Mapping lives in `bincio/extract/sport.py`.
---
## Patching activities (manual fixes)
Prefer the Edit drawer for title/sport/description/photo changes — it writes a sidecar
and keeps extracted data pristine. For bulk fixes or fields not exposed in the UI,
patch the JSON directly:
```bash
# Fix sport for a single activity
python3 -c "
import json
p = 'site/public/data/activities/2025-03-16T113005Z.json'
d = json.load(open(p))
d['sport'] = 'skiing'
d['sub_sport'] = 'nordic'
json.dump(d, open(p,'w'), separators=(',',':'))
"
# Then update the index.json to match
python3 -c "
import json
idx = json.load(open('site/public/data/index.json'))
for a in idx['activities']:
if a['id'] == '2025-03-16T113005Z':
a['sport'] = 'skiing'
a['sub_sport'] = 'nordic'
json.dump(idx, open('site/public/data/index.json','w'), separators=(',',':'))
"
```
---
## Common diagnostics
```bash
# Count activities by sport in the data store
python3 -c "
import json, glob
from collections import Counter
files = glob.glob('site/public/data/activities/*.json')
c = Counter(json.load(open(f))['sport'] for f in files)
print(dict(c.most_common()))
"
# Find activities with 0 distance
python3 -c "
import json, glob
for f in glob.glob('site/public/data/activities/*.json'):
d = json.load(open(f))
if (d.get('distance_m') or 0) == 0 and d.get('sport') != 'other':
print(d['id'], d['sport'], d['title'])
"
# Find activities still tagged 'other'
python3 -c "
import json
idx = json.load(open('site/public/data/index.json'))
others = [a for a in idx['activities'] if a['sport'] == 'other']
for a in others[:20]:
print(a['started_at'][:10], a.get('source','?'), a['title'])
print(len(others), 'total')
"
```
---
## Key files
| File | Purpose |
|---|---|
| `extract_config.yaml` | Main config (input dirs, output dir, privacy) |
| `site/.env` | Site env vars (`BINCIO_DATA_DIR`, `PUBLIC_EDIT_URL`) — copy from `.env.example` |
| `SCHEMA.md` | BAS format specification |
| `CLAUDE.md` | Dev notes, gotchas, design decisions |
| `bincio/render/merge.py` | Sidecar overlay logic — `parse_sidecar`, `merge_all` |
| `bincio/edit/server.py` | FastAPI edit API — GET/POST activity, image upload |
| `bincio/extract/sport.py` | Sport name normalisation + mapping |
| `bincio/extract/metrics.py` | Distance, speed, HR, elevation computation |
| `bincio/extract/parsers/fit.py` | FIT file parser |
| `site/src/components/ActivityFeed.svelte` | Feed page — card grid + sport filter |
| `site/src/components/StatsView.svelte` | Stats page — heatmap + year totals |
| `site/src/components/ActivityMap.svelte` | MapLibre GL map |
| `site/src/components/ActivityCharts.svelte` | Observable Plot charts |
| `site/src/lib/format.ts` | `formatDistance`, `formatDuration`, sport icons/colors |
| `site/src/lib/types.ts` | TypeScript types mirroring BAS schema |
| `site/astro.config.mjs` | Astro + Vite config (MapLibre GL workarounds) |