mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 06:32:03 +00:00
fix(sleep): reconstruct an ordered WHOOP hypnogram timeline
WHOOP's v2 API returns only per-stage sleep duration totals — no onset timestamps, no hypnogram endpoint. The mapper stamped all five stage totals on the one sleep-END instant, so the hypnogram reconstructed every stage as a span touching the night's right edge: bars stacked right-aligned with no clock times whenever WHOOP was the sole source. Lay the asleep stages back-to-back from sleep onset in a fixed physiological order (AWAKE lead, then CORE → DEEP → REM), emitting one timed row per segment with measuredAt at that segment's end and a distinct indexed externalId. IN_BED stays a single envelope row over the whole window. The existing reader and renderer are untouched — they now see distinct per-segment instants and place each block at its own time. The order is synthesised, not measured, so every reconstructed row is flagged and the night DTO carries a per-session `reconstructed` boolean (GET /api/sleep/night, regenerated OpenAPI). The client renders the hypnogram but labels it an approximate layout and never recomputes; real-series sources (Apple Health, Withings, Fitbit) stay false.
This commit is contained in:
1 parent
94fd837d48
commit
bc12633569
8 files changed
+246
-30
No files matched your search
@@ -12,6 +12,11 @@
|
||||
* priority ladder — so two sources' timelines never overlay. A view over the
|
||||
* existing per-stage `SLEEP_DURATION` rows; no schema, no new type.
|
||||
*
|
||||
* `reconstructed` (per session) is true when the winning source has no
|
||||
* per-stage onset timestamps and the server synthesised a contiguous timeline
|
||||
* (WHOOP) — the client renders the hypnogram but labels it an approximate
|
||||
* layout. A real-series source (Apple Health / Withings / Fitbit) is false.
|
||||
*
|
||||
* `date` omitted → the most recent night in the trailing-year window.
|
||||
*
|
||||
* Auth: cookie session OR Bearer token (`requireAuth`). Soft-delete-filtered.
|
||||
@@ -64,6 +69,10 @@ function serializeSession(s: SleepSession) {
|
||||
inBedMinutes: s.inBedMinutes === null ? null : Math.round(s.inBedMinutes),
|
||||
awakeMinutes: s.awakeMinutes === null ? null : Math.round(s.awakeMinutes),
|
||||
awakenings: s.awakenings,
|
||||
// iOS #18 — `reconstructed` is true when the source has no per-stage onset
|
||||
// timestamps and the server synthesised a contiguous timeline (WHOOP). iOS
|
||||
// labels the hypnogram as an approximate layout; it never recomputes.
|
||||
reconstructed: s.reconstructed,
|
||||
// iOS #18 — round the per-stage map too; the underlying totals sum
|
||||
// second-precision segments and otherwise serialise as e.g. 88.4999.
|
||||
stages: Object.fromEntries(
|
||||
|
||||
Reference in new issue
Block a user