updated changelog
This commit is contained in:
+114
@@ -0,0 +1,114 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [Unreleased] — 2026-03-30
|
||||||
|
|
||||||
|
### Athlete page
|
||||||
|
|
||||||
|
- **`/athlete` page** — three-tab layout: Power Curve · Records · Profile
|
||||||
|
- **Mean Maximal Power (MMP) curve** — computed at extract time for each activity with power data
|
||||||
|
- Sliding-window O(n) algorithm over 1 Hz power timeseries; 15 standard durations (1 s → 1 h)
|
||||||
|
- Multi-curve overlay with range selector: All time / Last 365 d / Last 90 d / user-defined seasons
|
||||||
|
- Log-scale x-axis via Observable Plot; FTP reference line; per-point tooltips
|
||||||
|
- Seasons configurable in `extract_config.yaml` under `athlete.seasons`
|
||||||
|
- **Personal records (Records tab)** — sport-specific best efforts computed via sliding window
|
||||||
|
- Running: 400 m, 1 km, 1 mile, 5 km, 10 km, half marathon, marathon
|
||||||
|
- Cycling: 5 km, 10 km, 20 km, 50 km, 100 km
|
||||||
|
- Swimming: 100 m, 200 m, 500 m, 1 km, 2 km
|
||||||
|
- Table shows time, pace (running) or speed (cycling/swimming), date, activity link
|
||||||
|
- Hiking / Walking: longest distance and most elevation gain
|
||||||
|
- **Best climbs** — top 10 biggest single climbs (Kadane's algorithm on 1 Hz elevation deltas); ranked table with elevation, date, activity link
|
||||||
|
- **Profile tab** — max HR, FTP, HR zones, power zones
|
||||||
|
- **`bincio edit` athlete API** (`GET /api/athlete`, `POST /api/athlete`) — reads/writes `edits/athlete.yaml`
|
||||||
|
- **`AthleteDrawer.svelte`** — slide-in profile editor (gated behind `PUBLIC_EDIT_URL`)
|
||||||
|
- Max HR and FTP number inputs
|
||||||
|
- HR and power zone tables: changing a zone's upper bound auto-cascades to the next zone's lower bound
|
||||||
|
- Season list: name + date range, add/remove rows
|
||||||
|
- **`athlete.json`** — written at extract time; contains pre-aggregated MMP curves and records; symlinked into `_merged/` by `merge_all()`
|
||||||
|
|
||||||
|
### Extraction pipeline
|
||||||
|
|
||||||
|
- **MMP computation** — `compute_mmp()` added to `metrics.py`; stored in both detail JSON and index summary (enables client-side season filtering without extra fetches)
|
||||||
|
- **Best-effort computation** — `compute_best_efforts()` two-pointer sliding window on 1 Hz speed; `_best_climb()` Kadane's on elevation deltas
|
||||||
|
- **`write_athlete_json()`** — aggregates MMP and records from all summaries into `athlete.json`
|
||||||
|
|
||||||
|
### Scripts
|
||||||
|
|
||||||
|
- **`scripts/backfill.py`** — backfills `mmp`, `best_efforts`, and `best_climb_m` into existing activity JSONs from already-extracted 1 Hz timeseries; no FIT re-parsing needed (~20 s for 2500 activities)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.1.0] — 2026-03-29
|
||||||
|
|
||||||
|
### Extraction pipeline
|
||||||
|
|
||||||
|
- **Parallel extraction** — activities now processed with `ProcessPoolExecutor`; large shared state (Strava lookup, known hashes) sent once per worker via `initializer=` rather than once per task
|
||||||
|
- **TCX parser fixes** — handles both `http://` and `https://` Garmin namespace URIs
|
||||||
|
- **Sport classification overhaul**
|
||||||
|
- FIT parser now reads sport from the `session` frame as fallback when no separate `sport` frame is present (fixes Karoo and Strava-generated FIT files)
|
||||||
|
- Strava CSV `Activity Type` used as authoritative override when present
|
||||||
|
- Expanded sport mapping: e-bike variants (`ebikeride`, `e_bike_ride`), `ride`, `run`, date-prefix stripping, and more
|
||||||
|
- Skiing added as first-class sport: `cycling` | `running` | `hiking` | `walking` | `swimming` | `skiing` | `other`
|
||||||
|
- Nordic sub-sport: FIT sub_sport values `cross_country_skiing`, `nordic_skiing`, `skate_skiing`, `backcountry_skiing` → `"nordic"`
|
||||||
|
- **Distance calculation fix** — when a FIT device records `distance = 0.0` (not `null`), the extractor now falls back to haversine-computed GPS distance instead of using the zero value directly; fixes skiing activities that had valid tracks and speeds but showed 0 km
|
||||||
|
- **`metadata_csv` is fully optional** — omitting it from config works cleanly; only needed for Strava bulk exports
|
||||||
|
|
||||||
|
### Site — maps & charts
|
||||||
|
|
||||||
|
- **MapLibre GL map** fully working on the activity detail page
|
||||||
|
- Static import + `optimizeDeps.include` (not `exclude`) fixes silent tile worker failure
|
||||||
|
- `build.target: 'es2022'` required for MapLibre's ES2022 class field syntax
|
||||||
|
- MapLibre v5 requires explicit `center`/`zoom` in Map constructor and `setLngLat()` before `addTo()`
|
||||||
|
- **Observable Plot charts** (elevation, speed, HR, cadence) working
|
||||||
|
- Switched from dynamic `await import()` to static import — fixes unreliable Svelte reactivity
|
||||||
|
- Curve name is `"monotone-x"` not `"monotoneX"`
|
||||||
|
- **Power chart** added as fifth tab alongside elevation/speed/HR/cadence
|
||||||
|
- **HR and power zone histograms** — configurable zone boundaries via `athlete.hr_zones` / `athlete.power_zones` in `extract_config.yaml`; histogram x-axis capped at actual data max so sentinel values (`999`, `9999`) don't stretch the axis
|
||||||
|
- **Adjustable trim range** on histograms
|
||||||
|
|
||||||
|
### Site — activity feed
|
||||||
|
|
||||||
|
- **SVG track thumbnails** on feed cards — drawn from `preview_coords` (no extra fetch)
|
||||||
|
- **Sport filter bar** — pill buttons for All / Cycling / Running / Hiking / Walking / Swimming / Skiing / Other
|
||||||
|
|
||||||
|
### Site — stats page
|
||||||
|
|
||||||
|
- **Sport filter bar** — same pill UI as the feed; all stats and heatmap reflect the selected sport
|
||||||
|
- **Heatmap colour improvements**
|
||||||
|
- Blended colours in "All" mode: each cell's RGB is a weighted average of sport colours by distance
|
||||||
|
- Percentile-based intensity scaling (active): each day ranked against all active days, spreading colour evenly regardless of km outliers; configurable back to linear/max-relative (documented in CLAUDE.md)
|
||||||
|
- `applyIntensity()` lerps from zinc-800 background to full sport colour — dim cells fade into the background rather than going black
|
||||||
|
- `$: cellColors` precomputed as a reactive `Map<string, string>` — fixes Svelte not re-rendering cells when filter changes
|
||||||
|
- **Month label fix** — labels embedded in the week-column flex grid (no more absolute-positioning bugs); `getWeeks()` uses local date formatting (`localISO()`) instead of `toISOString()` to avoid UTC/local mismatch that produced a spurious "Dec" label at column 0
|
||||||
|
- **Cell tooltips** — hovering a cell shows a floating card with date, and for each activity: name, sport, distance, duration; each activity is a clickable link to its detail page; 120 ms grace period when moving from cell to tooltip
|
||||||
|
|
||||||
|
### Site — activity editing (`bincio edit`)
|
||||||
|
|
||||||
|
- **`bincio edit` write API** — FastAPI server (`--data-dir`, default port 4041)
|
||||||
|
- `GET /api/activity/{id}` — current values with sidecar overrides applied
|
||||||
|
- `POST /api/activity/{id}` — writes sidecar `.md`, triggers `merge_all()`
|
||||||
|
- `POST /api/activity/{id}/images` — multipart image upload
|
||||||
|
- `DELETE /api/activity/{id}/images/{filename}`
|
||||||
|
- **Activity sidecar system** (`bincio/render/merge.py`)
|
||||||
|
- Sidecars live in `edits/` alongside extracted data (never co-mingled with immutable BAS JSON)
|
||||||
|
- Fields: `title`, `sport`, `description`, `hide_stats`, `highlight`, `private`, `gear`
|
||||||
|
- `merge_all()` produces `_merged/` output; `public/data` → `_merged/` at runtime
|
||||||
|
- **`EditDrawer.svelte`** — slide-in drawer in the Astro site (no separate HTML from the server)
|
||||||
|
- Opens in-page via Edit button; only rendered when `PUBLIC_EDIT_URL` env var is set
|
||||||
|
- Title, sport dropdown, gear, markdown description textarea
|
||||||
|
- Image drag-and-drop with chip list + delete
|
||||||
|
- Hide-stats toggle buttons (elevation, speed, heart_rate, cadence, power)
|
||||||
|
- Highlight and private flags
|
||||||
|
- Optimistic local update on save — title and description update immediately without reload
|
||||||
|
- **Photo gallery + lightbox** on activity detail page — keyboard navigation (←/→/Esc), filename + counter overlay
|
||||||
|
- **Markdown descriptions** rendered with `marked`; local relative images suppressed from inline rendering (shown in gallery instead)
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- **README** rewritten — philosophy statement front and centre, clear two-stage architecture diagram, quick start
|
||||||
|
- **CHEATSHEET.md** added — daily workflow, all CLI commands, config reference, privacy table, patching snippets, diagnostic scripts, key files table
|
||||||
|
- **CLAUDE.md** updated — MapLibre GL v5 gotchas, Observable Plot curve names, heatmap colour scaling approaches (linear vs percentile), sidecar/edit architecture decisions
|
||||||
|
- **`extract_config.example.yaml`** cleaned up — personal paths removed, `metadata_csv` commented out with explanation
|
||||||
|
|
||||||
|
### Infrastructure
|
||||||
|
|
||||||
|
- `publish.sh` — builds and pushes static site to GitHub Pages via orphan branch
|
||||||
Reference in New Issue
Block a user