Commit Graph

377 Commits

Author SHA1 Message Date
Davide Scaini b509db4940 nerd corner: cool-to-warm year color ramp (proposal, not pushed) 2026-05-14 18:47:08 +02:00
Davide Scaini 653db2428f nerd corner: add cumulative plot below the per-period chart 2026-05-14 18:43:05 +02:00
Davide Scaini 5167f2a988 explore: shard tracks into per-year files for progressive loading
bake_tracks now writes tracks_YYYY.json shards + tracks_index.json manifest
instead of a single monolithic tracks.json. API /api/me/tracks returns the
manifest; /api/me/tracks/{year} serves individual shards. Explore.svelte
fetches the two most recent years eagerly then streams the rest in the
background so the map renders immediately with recent data.
2026-05-14 18:34:53 +02:00
Davide Scaini 8af6b7b04e nav: always show upload button on all screen sizes 2026-05-14 18:24:44 +02:00
Davide Scaini 16965a7645 ActivityDetail: fetch timeseries in parallel with detail JSON to cut load time 2026-05-14 18:22:05 +02:00
Davide Scaini c36b95e041 segments detect: add --fresh flag to clear efforts before re-detecting 2026-05-14 17:11:11 +02:00
Davide Scaini 862226305a Fix segment avg_speed: derive from distance/time; tighten speed bounds to reject false matches 2026-05-14 17:09:41 +02:00
Davide Scaini 8ff781661e Fix feedback JSON encoding: use ensure_ascii=False to preserve accented characters 2026-05-14 17:04:44 +02:00
Davide Scaini 4d6859b927 NerdCorner: show per-period load instead of cumulative 2026-05-14 16:42:16 +02:00
Davide Scaini b32553b0b1 Fix NerdCorner: pass all activities, not only those with power data 2026-05-14 16:39:57 +02:00
Davide Scaini 8804bdec37 Add Nerd Corner tab with year-over-year cumulative progress chart 2026-05-14 16:37:01 +02:00
Davide Scaini 487ce42361 Explore: type pill dark/light theme split; freeze active pill on hover in dark theme 2026-05-14 16:24:00 +02:00
Davide Scaini 46445dd1cb Move Invites link from athlete page to settings; type pill active state contrast fix 2026-05-14 16:22:31 +02:00
Davide Scaini ab112788b4 Explore: grey pill background dark-theme only; transparent in light mode 2026-05-14 16:18:10 +02:00
Davide Scaini 8d799e8e64 Explore: active type pill solid color bg with auto black/white text contrast 2026-05-14 16:17:16 +02:00
Davide Scaini cfb7198d64 Explore: raise inactive type-pill background opacity to 70% for dark theme visibility 2026-05-14 16:11:48 +02:00
Davide Scaini 2b9e080b4c Explore: opacity slider heatmap-only; lines mode width-only at 100% opacity 2026-05-14 16:07:57 +02:00
Davide Scaini 20bb5bfb60 explore: grey background on inactive type pills for readability 2026-05-14 15:59:00 +02:00
Davide Scaini dc719a55d5 explore: show width/opacity sliders in lines mode too 2026-05-14 15:57:20 +02:00
Davide Scaini 5593764fdb explore: skip legacy bare-timestamp geojsons; type pill colors visible when inactive 2026-05-14 15:55:10 +02:00
Davide Scaini e7228c2be8 explore: plasma palette; width + opacity sliders for heatmap 2026-05-14 15:48:30 +02:00
Davide Scaini 298fe3ea39 explore: fix by-type layer visibility — only show selected types 2026-05-14 15:43:12 +02:00
Davide Scaini 4e32cf4f21 explore: grey map default; zoom-scaled heatmap lines; fix all/none type buttons 2026-05-14 15:35:40 +02:00
Davide Scaini a75fabecb9 explore: thicker heatmap lines (4px + blur) 2026-05-14 14:57:39 +02:00
Davide Scaini b3c41967f6 explore: replace gaussian heatmap with line-accumulation (strava-style) 2026-05-14 14:54:47 +02:00
Davide Scaini 6d13993f98 explore: default heatmap/by-type; month All button; bbox filtering on map move 2026-05-14 14:45:54 +02:00
Davide Scaini 537d1bb712 explore: exclude indoor/virtual activities from tracks.json 2026-05-14 14:34:44 +02:00
Davide Scaini 5307ae287c Explore: personal GPS heatmap tab under Athlete page
- bincio/explore.py: bake_tracks() simplifies GPS coords (RDP ε=0.0001),
  strips to [lng,lat], groups by sport type, writes per-handle tracks.json
- bake-tracks CLI command; render CLI calls _bake_tracks() after each build;
  strava_zip runs it once at end of batch
- /api/me/tracks endpoint serves the baked file; wipe_user cleans it up
- Explore.svelte: MapLibre full-screen map with sidebar — type pills,
  year/month date filter, Lines / Heatmap (global or by-type) view modes
- AthleteView: Explore tab visible only to profile owner (checks __bincioMe)
- Base.astro: fullscreen prop + Planner nav link
2026-05-14 14:31:21 +02:00
Davide Scaini 2daa66d7b0 about: add Satispay donation button + QR code; remove feedback button (all locales) 2026-05-14 11:11:34 +02:00
Davide Scaini 1a7d1dc8c3 serve: complete CurrentUserResponse model (add wiki_access, activity_access, dem_configured) 2026-05-14 11:06:35 +02:00
Davide Scaini e7c5af0d01 Nav: add Planner link for logged-in users (mirrors wiki link strategy) 2026-05-14 10:44:46 +02:00
Davide Scaini a10164b932 metrics: fall back to GPS-derived speed in compute_best_efforts when device speed absent
FIT files from older devices (and GPX/TCX files) often omit the speed field.
The sliding-window best-effort algorithm was treating all such points as speed=0,
so no records were ever produced for these activities.

Fix: when p.speed_kmh is None but consecutive lat/lon are available, compute
haversine segment speed and spread it evenly across the 1Hz interval slots.
This mirrors what _gps_stats already does for avg/max speed computation.
2026-05-14 09:16:01 +02:00
Davide Scaini c85d2edf39 dev: fix _start_serve to set deps.data_dir/site_dir (not srv.*) after Step 3 split 2026-05-14 00:09:41 +02:00
Davide Scaini 7ec91b0e6a refactoring.md: update Step 4 to reflect as-built (post-split routers) 2026-05-14 00:04:52 +02:00
Davide Scaini 27f6d141f7 Refactor step 4: narrow broad except Exception catches
Replaced 28 bare `except Exception` catches across 8 files with specific
exception types reflecting the actual failure modes:

- JSON file reads → (OSError, json.JSONDecodeError)
- datetime parsing → ValueError
- base64 decoding → ValueError
- YAML parsing → (OSError, yaml.YAMLError); import moved above try
- GeoJSON coord extraction → (TypeError, IndexError, AttributeError)
- Startup temp-file cleanup → OSError
- Single JSON line parsing (SSE batch) → json.JSONDecodeError

Kept broad catches only where intentional:
- Background thread top-level guards (tasks.py, admin.py) with log.exception
- SSE stream generator tops (strava.py, garmin.py, uploads.py)
- Per-item batch loops that must not abort the whole operation
- Explicitly non-fatal post-upload merge steps with log.warning
2026-05-13 23:58:14 +02:00
Davide Scaini 8380b1d2cc Refactor: split serve/server.py (3220 lines) into focused modules
serve/server.py is now 69 lines — app factory, middleware, and router
registration only.

New modules:
  deps.py    (168 lines) — module-level globals + auth dependency functions
  models.py   (85 lines) — all Pydantic request/response models
  tasks.py   (136 lines) — background workers and job tracker
  routers/               — one file per domain (10 routers, ~2750 lines total)
    auth.py, me.py, admin.py, activities.py, uploads.py,
    segments.py, strava.py, garmin.py, ideas.py, feed.py

cli.py updated to set globals on deps instead of server.

88 new regression tests in tests/serve/ cover auth guards and key
behaviours for every router; 294 total passing after the split.
2026-05-13 23:47:19 +02:00
Davide Scaini 2ec4d9157c Refactor: extract edit UI HTML into bincio/edit/templates/edit.html
The 285-line _HTML string literal in edit/server.py is replaced by a
template file loaded at request time. The route handler is unchanged in
behaviour — it still substitutes __SITE_URL__, __SPORT_OPTIONS__, and
__STAT_CHECKBOXES__ before returning the response.

Five new tests cover: 200 response, form presence, site_url injection,
no unresolved placeholders, and template file existence on disk.
2026-05-13 23:19:19 +02:00
Davide Scaini 9dd533825f Fix pre-existing test failures in test_writer and test_metrics
test_writer: _dummy_metrics() and test_build_summary_required_fields were
missing np_power_w=None after the field was added to ComputedMetrics.

test_metrics: the leading-zero elevation heuristic fired on a single 0.0
start value, incorrectly skipping the first legitimate elevation step.
Guard now requires at least 2 consecutive near-zero leading values before
activating the Apple Watch lock-acquisition workaround.
2026-05-13 23:15:26 +02:00
Davide Scaini e61d05fc41 Refactor: extract shared image upload utilities into bincio/shared/images.py
ALLOWED_IMAGE_TYPES, MAX_IMAGE_BYTES, and unique_image_name() were
duplicated identically in both the edit and serve servers. Centralising
them means a single change point for any future extension (e.g. adding
image/avif support).

Tests added in tests/test_shared_images.py cover no-collision, single
and chained collisions, no-suffix filenames, and constant values.
2026-05-13 23:13:08 +02:00
Davide Scaini cd97e4cc87 CORS: allow all *.bincio.org origins (for planner.bincio.org) 2026-05-13 22:51:00 +02:00
Davide Scaini 58a5d5b450 Strava sync: skip import when a FIT-file upload already covers the same start time
Before importing each Strava activity, build a set of existing timestamp
prefixes (YYYY-MM-DDTHHMMSSZ) from the activities directory. If the incoming
Strava activity matches an existing prefix, record its Strava ID as done and
skip — preventing duplicate entries when a FIT file and a Strava sync both
cover the same ride.

Also reports skipped-existing count in the summary line.
2026-05-13 21:52:07 +02:00
Davide Scaini fb033e3da2 Search: auto-load all year shards when a query is entered so full history is searched 2026-05-13 21:17:55 +02:00
Davide Scaini e4b53dde44 Nav: move Wiki to right-side menu (desktop + hamburger); fix missing element when PUBLIC_WIKI_URL unset in rebuilds 2026-05-13 20:50:04 +02:00
Davide Scaini a4b4d11fc0 Nav: move Ideas and About to right-side menu (desktop + hamburger) 2026-05-13 20:44:54 +02:00
Davide Scaini fc012b5311 pull_feedback: highlight new submissions since last pull 2026-05-13 20:36:40 +02:00
Davide Scaini b5a1e881fb delete plan 2026-05-13 20:07:26 +02:00
Davide Scaini b9a21e8bcc ideas: add inline edit for own ideas (author + admin) 2026-05-13 19:52:25 +02:00
Davide Scaini aa1c0b38c0 ideas: add Feedback button to header 2026-05-13 19:48:34 +02:00
Davide Scaini c2c4cb9f3a segments: fit initial map view to all existing segments 2026-05-13 19:41:59 +02:00
Davide Scaini d82033fd84 ideas: update bug report link text 2026-05-13 19:34:02 +02:00