mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
feat(workouts): accept an optional HR series on the batch contract
Extend POST /api/workouts/batch with an optional per-workout `samples`
array — a route-independent heart-rate series so indoor workouts still
yield a strain-relevant signal. Each entry is `{ t, hr?, speedMs?,
power?, cadence? }`; the array is capped at 30 000 points
(MAX_WORKOUT_HR_SAMPLES), covering a long endurance session sampled at
1 Hz while bounding the largest new workout-side write stream the same
way the 20 000-point route cap bounds the geometry.
Persistence rides the existing route-attachment path: the freshly
written workout id is resolved once (by composite key, or the manual
tuple), then both the route geometry and the HR series attach by FK
with skipDuplicates so a re-submitted batch stays idempotent. A workout
may carry both a GPS route and a canonical HR series, or just one.
GET /api/workouts/{id} returns the series alongside the route. OpenAPI
regenerated.
`samples` keeps first-write-wins/overwrite parity with the workout row
it hangs off: the 1:1 FK plus skipDuplicates means a re-ingest of an
existing workout never duplicates or mutates its series.
This commit is contained in:
1 parent
d247f6966a
commit
30be0c4e0d
8 files changed
+463
-13
No files matched your search
@@ -834,6 +834,35 @@ paths:
|
||||
- t
|
||||
required:
|
||||
- geometry
|
||||
samples:
|
||||
minItems: 1
|
||||
maxItems: 30000
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
t:
|
||||
type: string
|
||||
format: date-time
|
||||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||||
hr:
|
||||
type: integer
|
||||
minimum: 20
|
||||
maximum: 300
|
||||
speedMs:
|
||||
type: number
|
||||
minimum: 0
|
||||
maximum: 50
|
||||
power:
|
||||
type: number
|
||||
minimum: 0
|
||||
maximum: 3000
|
||||
cadence:
|
||||
type: number
|
||||
minimum: 0
|
||||
maximum: 400
|
||||
required:
|
||||
- t
|
||||
required:
|
||||
- sportType
|
||||
- startedAt
|
||||
@@ -4322,6 +4351,10 @@ components:
|
||||
anyOf:
|
||||
- $ref: "#/components/schemas/WorkoutRouteGeometry"
|
||||
- type: "null"
|
||||
samples:
|
||||
anyOf:
|
||||
- $ref: "#/components/schemas/WorkoutHrSeries"
|
||||
- type: "null"
|
||||
canonicalId:
|
||||
type: string
|
||||
required:
|
||||
@@ -4342,6 +4375,7 @@ components:
|
||||
- externalId
|
||||
- metadata
|
||||
- route
|
||||
- samples
|
||||
- canonicalId
|
||||
additionalProperties: false
|
||||
WorkoutRouteGeometry:
|
||||
@@ -4358,6 +4392,18 @@ components:
|
||||
- geometry
|
||||
- sampleTimestamps
|
||||
additionalProperties: false
|
||||
WorkoutHrSeries:
|
||||
type: object
|
||||
properties:
|
||||
sampleCount:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 9007199254740991
|
||||
samples: {}
|
||||
required:
|
||||
- sampleCount
|
||||
- samples
|
||||
additionalProperties: false
|
||||
BatchWorkoutsResponse:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
Reference in new issue
Block a user