Commit Graph

436 Commits

Author SHA1 Message Date
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
Davide Scaini c30a15d295 ideas: add done/reopen status toggle for admins
Admin-only POST /api/ideas/{id}/status toggles status between open and
done. Done ideas are greyed out (opacity 0.55), show a green checkmark,
and sink to the bottom of the list. Admins see done/reopen buttons on
each card.
2026-05-13 19:32:30 +02:00
Davide Scaini 38f2e51788 ideas: add Ideas page, nav link; remove feedback button from About
New /ideas/ page with Svelte component: card list sorted by votes,
inline submit form, optimistic vote toggling, delete for own/admin.
Bug report link moved to bottom of Ideas page. Feedback button removed
from About page.
2026-05-13 19:29:39 +02:00
Davide Scaini 9553ca5ce7 ideas: add JSON-file-backed ideas API (list, create, vote, delete)
Ideas and votes are stored as flat JSON files in /var/bincio/_ideas/,
following the same filesystem-first philosophy as segments and efforts.
Vote toggling uses fcntl exclusive locking to prevent concurrent writes.
2026-05-13 19:27:54 +02:00
Davide Scaini cf9817e853 segments: clear stored efforts before rescan so stale entries are removed
Both trigger_detect and me_segment_rescan were appending-only, so false
efforts recorded before the geometric speed check fix remained after
rescan. Now each rescan path clears the effort file first, making the
result authoritative.
2026-05-13 16:35:44 +02:00
Davide Scaini 6e92ea4fce segments: reject false efforts via geometric speed check
Long circuit rides were matching a segment START early and finding the
segment END hours later on a second pass, producing effort times of
~17000s on a 4.7km segment. The conformance check passed because the
full-circuit track covers all interior points within 50m over 5 hours.

Add a per-sport minimum geometric speed (segment_distance / elapsed_s):
cycling ≥ 1.0 m/s, running ≥ 0.5 m/s, default ≥ 0.2 m/s. When the
check fails, advance past the current start candidate and retry, so a
legitimate later match (e.g. a second lap done at real speed) is still
detected.
2026-05-13 16:31:00 +02:00
Davide Scaini 994f4287ef Dedup segment efforts by started_at to handle same activity stored under two IDs 2026-05-13 16:20:10 +02:00
Davide Scaini ad9e428b1e Fix UnicodeEncodeError: sanitize surrogate pairs before JSON writes in merge.py 2026-05-13 16:16:58 +02:00
Davide Scaini c837464a28 Exclude indoor/virtual activities from records and power curve 2026-05-13 16:05:26 +02:00
Davide Scaini 2395a6e566 Fix segment effort duplicates; auto-scan on segment creation
- detect.py: truncate started_at to seconds so dedup key survives JSON round-trip
- store.py: dedup by (activity_id, iso-started_at) string key, not object equality
- server.py: extract _scan_segment_for_user helper; trigger background scan
  for the creating user's activities when a new segment is saved
2026-05-13 15:58:57 +02:00
Davide Scaini cb3c9b6e41 Move search bar above sport/date filters, below page title 2026-05-13 11:54:37 +02:00
Davide Scaini 861748a059 ActivityFeed: add title search bar with URL sync 2026-05-13 11:51:14 +02:00
Davide Scaini f00e5e47b2 SegmentDetail: sort efforts by time by default, sortable column headers 2026-05-13 11:17:22 +02:00
Davide Scaini 0ff5473dfd Athlete segments tab: link best time to activity; expandable effort list
- best_activity_id now included in segment_summary API response
- Best time is a direct link to the activity that produced it
- Clicking a row expands an inline effort list (lazy-loaded from
  /api/segments/{id}/efforts): date linked to activity, time, Δ vs PR
- Clicking again collapses; ▲/▼ chevron shows state
2026-05-13 08:40:39 +02:00
Davide Scaini 59cf99f0af Fix stuck segments tab; add /segments/ dev fallback
AthleteView: use segmentsFetched flag to prevent infinite fetch loop when
there are no efforts (segmentSummary.length === 0 was re-triggering the
reactive statement after every empty response). Also improve empty state
message and reset flag after rescan so the table reloads.

astro.config.mjs: extend shell fallback plugin to cover /segments/{id}/
the same way /activity/{id}/ is handled, so segment detail pages work in
the dev server without nginx.
2026-05-13 08:35:00 +02:00
Davide Scaini b8fd4e4ded Move segment rescan button from segments list to athlete/segments tab 2026-05-13 08:20:05 +02:00
Davide Scaini d7fd585e77 Add global segment rescan: POST /api/me/segment-rescan + Rescan all button 2026-05-13 08:17:18 +02:00
Davide Scaini f2075e29d2 Segments Phase 4: detail page, activity efforts, athlete tab, new APIs
New API endpoints:
- GET /api/segments/{id} — single segment metadata
- GET /api/activities/{id}/segment_efforts — efforts for an activity (auth)
- GET /api/users/{handle}/segment_summary — public best time + count per segment

New components:
- SegmentDetail.svelte — map + metadata + effort table (with PR/Δ) + rescan button
- SegmentsPage.svelte — URL router: shows detail when /segments/{id}/, list otherwise

Updated:
- segments/index.astro — now uses SegmentsPage router
- nginx-activity.conf — add /segments/ try_files rule for client-side routing
- ActivityDetail.svelte — segment efforts block below laps
- AthleteView.svelte — Segments tab with best time + effort count per segment
- format.ts — add formatElapsed() for compact m:ss display
2026-05-13 08:09:24 +02:00
Davide Scaini c7f0013e57 SegmentCreate: prompt after save instead of immediate redirect; update plan
After saving, show "Saved! Add another from this activity?" with two
buttons: "Add another" (resets name/handles, keeps map loaded) and
"Done" (navigates to /segments/).
2026-05-13 01:03:34 +02:00
Davide Scaini 6c9de35426 Enforce 500 m minimum segment length in UI and API 2026-05-13 00:56:04 +02:00
Davide Scaini e9e7b5d0e7 SegmentCreate: add elevation profile that zooms to selected portion
Shows a dim area for the visible range around the selection (4% padding)
and a blue overlay for the selected segment, with a light stroke on the
top edge. Both the x-domain and y-domain track the selection, so the
chart zooms in as the handles narrow. Elevation min/max labels overlaid
at top-left and bottom-left.
2026-05-13 00:54:39 +02:00
Davide Scaini 4d2df860ce Segments Phase 3: detection algorithm, CLI, ingest hook, and efforts API
- detect.py: ActivityTrack + detect_one/detect_all (bbox pre-filter →
  start/end proximity 25m → path conformance 50m/30% → effort extraction
  with avg speed/HR/power and Coggan NP)
- cli.py: `bincio segments detect` for retroactive detection over stored
  timeseries JSONs, with optional --activity-id / --segment-id filters
- ingest.py: non-fatal hook at end of ingest_parsed runs detect_all
- server.py: GET /api/segments/{id}/efforts and POST /api/segments/{id}/detect
2026-05-13 00:50:39 +02:00
Davide Scaini 61db0734d2 Move segment shortcut next to Edit button, shorten to '+ segment' 2026-05-13 00:39:51 +02:00
Davide Scaini dd9f7a82dc Segments phase 2: /segments/ browse page, /segments/new/ creation flow, activity detail shortcut 2026-05-13 00:36:44 +02:00
Davide Scaini 79cad29ff1 Segments phase 1: models, store, and API endpoints (GET/POST/DELETE /api/segments) 2026-05-13 00:19:15 +02:00
Davide Scaini 6b2698c0c5 Mark fallback NP computation for future removal 2026-05-12 23:52:19 +02:00
Davide Scaini c46e91d0f5 Compute NP from timeseries in frontend for activities missing np_power_w in JSON 2026-05-12 23:51:22 +02:00
Davide Scaini bd0595ee79 Add avg power and NP to activity summary; NP uses Coggan 30s rolling-average method 2026-05-12 23:47:06 +02:00
Davide Scaini f1fec6d825 ActivityCharts: smoothing toggle (Raw/10s/20s) for all line chart metrics 2026-05-12 23:37:41 +02:00
Davide Scaini a5db6142b3 ActivityCharts: 10s rolling mean on cadence and power line charts (display only) 2026-05-12 23:32:33 +02:00
Davide Scaini 1298586a74 ActivityCharts: extend reference lines to HR; use high-contrast label styling 2026-05-12 23:29:09 +02:00
Davide Scaini 3231fdb4b7 ActivityCharts: add avg/P20/P80 reference lines to speed, cadence, and power line charts 2026-05-12 23:24:33 +02:00
Davide Scaini 0b266d208c Strip pre-2000 leading points to prevent epoch-zero start time and absurd duration 2026-05-12 23:11:33 +02:00
Davide Scaini 867da767eb Add sub_sport editing to activity edit drawer 2026-05-12 23:01:12 +02:00
Davide Scaini 93f6109028 Add hamburger menu for mobile nav 2026-05-11 11:37:33 +02:00
Davide Scaini 8fbbf460a9 Add PWA icons and manifest for iOS/Android home screen 2026-05-11 11:17:13 +02:00
Davide Scaini 14313ec59c Add Disconnect button to Strava section of upload modal 2026-05-10 17:12:55 +02:00
Davide Scaini 1eaf5c4e0b Remove TS annotation from define:vars script (caused parse error) 2026-05-10 17:10:38 +02:00
Davide Scaini 5be58f4e1c Fix Strava OAuth popup detection via postMessage (cross-origin safe) 2026-05-10 17:04:30 +02:00
Davide Scaini 695dc9fdce Fix Strava re-auth when credentials change; add disconnect button
When a user saves new Strava credentials with a different client_id,
auto-delete the existing token (it belongs to a different OAuth app
and will always fail on refresh). Add POST /api/strava/disconnect
endpoint and a "Disconnect from Strava" button in settings, visible
only when connected.

Immediate: deleted diego_p's stale token so he can reconnect.
2026-05-10 16:33:52 +02:00
Davide Scaini 8f028101c7 Fix elevation gain inflation from device no-fix leading zeros
Apple Watch and similar devices record exactly 0.0 for elevation while
waiting for barometric/GPS lock, then jump to the real altitude. The
hysteresis accumulator was seeding from 0.0, counting the full jump as
ascent. Fix: detect a leading near-zero run followed by a large jump
and seed the accumulator from the first real value instead.

Applied in both _elevation() (fresh extractions) and
recalculate_elevation_hysteresis() (recompute path). Added a bulk
admin endpoint POST /api/admin/users/{handle}/recompute-elevation and
corresponding button to fix existing stored activities.
2026-05-10 16:21:24 +02:00
Davide Scaini 55d59112ad Fix: don't copy 9 GB data dir into dist/ during production builds
BINCIO_DATA_DIR is already set in the build env, so manifest.ts reads
the data root directly at build time without needing public/data.
Moving _link_data() into the serve-only branch prevents Astro from
following the symlink and copying the full data dir into dist/. Any
leftover symlink from a previous dev session is removed before build.
Dev mode is unchanged.
2026-05-08 13:56:31 +02:00