75 lines
3.7 KiB
Markdown
75 lines
3.7 KiB
Markdown
# 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.
|