Files
Stride/docs/PERSONAL_RECORDS.md
T

3.2 KiB

Running personal records

Activity import calculates and persists estimated best efforts for 1 km, 2 km, 5 km, 10 km, half marathon (21,097.5 m), and marathon (42,195 m). These are fastest stretches anywhere within a run, rather than lap times or times projected from average pace. All existing running activities participate.

Following Strava's elapsed-time convention, stops inside an effort count. The calculation uses full-resolution FIT cumulative distance and timestamps, with linear interpolation at distance boundaries. Missing distance/time, backwards distance, and non-increasing timestamps split the data. Timestamp gaps count as elapsed time; there is no subtraction for auto-pause. No distance or time is inferred from an activity summary when samples are absent.

The dashboard lists current records. Runs earn historical PR badges if they established or strictly improved a record on their activity date; equal times do not earn another badge. Import order does not affect the result. Records and badges are recalculated from history, so older imports and removals are reflected. Comparisons currently appear only in the owner's dashboard and activity page.

These are estimates from watch data, not verified race records or an exact clone of Strava's proprietary calculation. GPS/distance spikes or sparse recording can affect results. There is currently no manual effort exclusion or correction UI.

Imports (FIT upload, Garmin sync and local cache) persist per-run efforts in the same transaction as the run. Refreshing recorded data recalculates the efforts. efforts_version identifies the calculation version; version 0 means pending. A current version with an empty list means calculated with no eligible distances. Page requests only compare stored efforts; there is no sample-based fallback. Raw samples and laps are deferred in list queries. Route availability is also persisted, so the dashboard can choose its map without loading historical samples. Chronological PR comparisons still happen on page requests, using at most six stored efforts per run, preserving correct badges after backfills or removals.

After flask --app app db upgrade, run:

flask --app app backfill-efforts --batch-size 25

The command commits each bounded batch and skips current-version rows on reruns. It includes non-running and incomplete activities, storing an empty result when appropriate. --owner-id ID limits the operation to one athlete; --force recalculates current results. It uses row locks on PostgreSQL to coordinate with concurrent refreshes, and fixes an upper activity ID at the start so new imports do not extend the backfill indefinitely. New imports calculate their own results. No FIT files need to be downloaded again. On Dokku:

dokku run stride flask --app app backfill-efforts --batch-size 25

Until backfill finishes, pending activities do not contribute to records or route selection. Keep backfills separate from schema migrations so a large history does not delay every deployment. Further additions could include second/third-best medals, annual records, manual exclusion of bad recordings, and a selected-effort highlight on the route map.