Add running fitness history and configurable heart rate zones
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
# Running fitness
|
||||
|
||||
Open **Fitness** in the navigation. Choose automatic or manual five-zone settings in
|
||||
Settings. Existing recordings are scored immediately;
|
||||
no Strava connection or download is needed. Run detail pages show the same score.
|
||||
|
||||
## Effort v1
|
||||
|
||||
Recorded moving minutes in Z1–Z5 have weights 1, 2, 3, 4 and 5 respectively.
|
||||
An exact boundary belongs to the higher zone. Scores use their own scale and do not
|
||||
reproduce Strava numbers. The account's supplied boundaries are 122, 152, 167 and 181
|
||||
bpm (181 belongs to Z5 to avoid leaving a gap after Z4's 180).
|
||||
|
||||
Three setting modes are supported:
|
||||
|
||||
- Automatic: maximum HR is 220 minus age, updated on birthdays, or 190 without a birthday.
|
||||
- Manual maximum: the supplied maximum HR is fixed until edited.
|
||||
- Custom: four manually supplied boundaries are fixed until edited.
|
||||
|
||||
The first two modes generate boundaries at 65%, 80%, 87% and 95% of maximum HR,
|
||||
rounded to the nearest bpm. These are explicit Stride defaults and may differ from
|
||||
Strava's unpublished zone boundary calculation. No peak-HR detection from recordings
|
||||
is performed. Existing three-band Stats settings remain independent.
|
||||
|
||||
The calculation uses persisted `analysis_summary.intervals[].hr_seconds`, which
|
||||
exclude recording breaks, stops and suspect HR. Usable seconds must cover at least
|
||||
80% of the run's timer duration. The remainder is not extrapolated. Without adequate
|
||||
coverage the run remains unscored. Perceived effort is not used
|
||||
as a fallback because it has a different scale.
|
||||
|
||||
## Daily model
|
||||
|
||||
All recorded runs for the owner are grouped by their local calendar day. Daily load
|
||||
is the sum of scored effort. Fitness and fatigue start at zero at the earliest
|
||||
imported run and update every day, including rest days:
|
||||
|
||||
```
|
||||
fitness += (load - fitness) * (1 - exp(-1 / 42))
|
||||
fatigue += (load - fatigue) * (1 - exp(-1 / 7))
|
||||
form = fitness - fatigue
|
||||
```
|
||||
|
||||
The first 126 days (three fitness time constants) are marked provisional. An unscored
|
||||
run marks that day and all subsequent estimates provisional: the model continues
|
||||
using known effort only, while the day's complete load remains unavailable. Missing
|
||||
imports cannot be detected. Other sports are excluded. Values are calculated with
|
||||
full precision, rounded for display. Range controls trim only the displayed series.
|
||||
|
||||
## Historical backfill
|
||||
|
||||
The graph replays the complete stored history on each visit. Imports, deleted runs,
|
||||
timezone changes and zone edits therefore recalculate the history automatically.
|
||||
The open page checks for changes on focus and every five minutes while visible.
|
||||
There is no additional score cache. The `8da41b79e632` database migration adds the
|
||||
private `user.hr_zones` JSON configuration. Run `flask --app run db upgrade` before
|
||||
starting the updated app. Current zones apply to all dates; historical changes in
|
||||
zones are not reconstructed. This retrospective recalculation is a deliberate
|
||||
difference from Strava's documented future-only handling of subsequent zone edits.
|
||||
|
||||
For older imports without current recording summaries, use the existing resumable
|
||||
command (take the normal database backup before operational maintenance):
|
||||
|
||||
```
|
||||
python -m flask --app run backfill-analysis --owner-id OWNER_ID
|
||||
```
|
||||
|
||||
If a current-version summary was damaged or lacks intervals despite usable samples,
|
||||
add `--force` to rebuild it. This regenerates the app's existing analysis summaries,
|
||||
charts and routes as well. Runs without usable original samples remain unscored.
|
||||
|
||||
Local data audit during implementation: 18 running activities, all with current
|
||||
recording intervals; 17 meet the 80% HR coverage requirement (one has 78.5%).
|
||||
The supplied five zones were saved in custom mode and history replayed over 109
|
||||
calendar days. A SQLite backup was taken before applying the configuration migration.
|
||||
Reference in New Issue
Block a user