feat(workouts): show fitness, fatigue and form from the stored training impulse

The first page of the workout list now carries the fitness-fatigue model
over the day-total heart-rate impulse the nightly strain run already
stores: fitness as the 42-day and fatigue as the 7-day exponentially
weighted average, form as their difference. A day without a stored impulse
counts as rest. It needs 28 days and three training days, flags the first
six weeks as warming up, and reads in the person's own units only. The
workouts page shows the three figures above the list.
This commit is contained in:
Marc-André Bombeck committed 2026-10-21 22:13:28 +02:00
1 parent 5c64192afc
commit 9be720146c
19 files changed
+651 -1

No files matched your search

+92
View File
@@ -48526,6 +48526,10 @@ components:
- offset
- droppedDuplicates
additionalProperties: false
trainingLoad:
description: v1.44 — on the first page (`offset=0`) only, account-wide whatever the filter. Absent on later pages and
older servers.
$ref: "#/components/schemas/WorkoutTrainingLoad"
required:
- workouts
- meta
@@ -48602,6 +48606,94 @@ components:
- hasRoute
- hasHrSeries
additionalProperties: false
WorkoutTrainingLoad:
anyOf:
- type: object
properties:
status:
type: string
const: ok
asOf:
type: string
description: The last scored UTC day the figures describe.
ctl:
type: number
description: "Fitness: 42-day exponentially weighted TRIMP."
atl:
type: number
description: "Fatigue: 7-day exponentially weighted TRIMP."
tsb:
type: number
description: "Form: fitness minus fatigue at `asOf`."
daysCovered:
type: integer
minimum: -9007199254740991
maximum: 9007199254740991
trainingDays:
type: integer
minimum: -9007199254740991
maximum: 9007199254740991
warmingUp:
type: boolean
description: "True below 42 covered days: both averages start at zero."
series:
type: array
items:
type: object
properties:
day:
type: string
ctl:
type: number
atl:
type: number
tsb:
type: number
required:
- day
- ctl
- atl
- tsb
additionalProperties: false
description: The trailing 90 days, oldest first; `tsb` is form going into the day.
required:
- status
- asOf
- ctl
- atl
- tsb
- daysCovered
- trainingDays
- warmingUp
- series
additionalProperties: false
- type: object
properties:
status:
type: string
const: insufficient
reason:
type: string
enum:
- no_impulse_history
- too_little_history
daysCovered:
type: integer
minimum: -9007199254740991
maximum: 9007199254740991
trainingDays:
type: integer
minimum: -9007199254740991
maximum: 9007199254740991
required:
- status
- reason
- daysCovered
- trainingDays
additionalProperties: false
description: "v1.44 — the fitness-fatigue model over the stored daily heart-rate training impulse (Banister TRIMP; a day
without a stored impulse counts as rest). Needs 28 days and three training days. TRIMP units: comparable with
the person's own past only."
GetWorkoutResponse:
type: object
properties: