mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 15:02:04 +00:00
feat(sleep): backfill existing WHOOP + Withings sleep rows after the stamp fix
The WHOOP timeline reconstruction and the Withings END-stamp change the stored measuredAt (and, for WHOOP, the externalId), so existing rows keep the old broken shape until re-synced. An in-place update collides with the (userId, type, measuredAt, source, sleepStage) unique index, so the backfill deletes the affected SLEEP_DURATION rows for the source and re-syncs from scratch; the re-sync re-folds the rollup tier in its tail. Add a boot-time pg-boss one-shot per connection, modelled on the WHOOP / Fitbit backfills: discovery enqueues one job per (user, provider) where sleep_timeline_backfill_at is null, the per-user pass deletes + re-syncs and stamps the marker, and the pass is idempotent across reboots. The queue is registered in allQueues, wired to a handler, and enqueued at boot.
This commit is contained in:
1 parent
7ad0b9c4f4
commit
ddfed17fa3
6 files changed
+453
No files matched your search
@@ -0,0 +1,20 @@
|
||||
-- v1.17.1 — one-shot sleep-timeline backfill markers.
|
||||
--
|
||||
-- WHOOP's v2 API returns only per-stage sleep DURATION totals (no onset
|
||||
-- timestamps), and the pre-fix mapper stamped all five stage totals on the one
|
||||
-- sleep-END instant. The hypnogram then reconstructed every stage as a span
|
||||
-- touching the night's right edge — the stacked-bar artefact with no clock
|
||||
-- times. The fix reconstructs an ordered, contiguous per-segment timeline.
|
||||
--
|
||||
-- Withings stamped each sleep segment with its START while every reader treats
|
||||
-- `measured_at` as the segment END, shifting each night one segment-length
|
||||
-- earlier. The fix stamps the END.
|
||||
--
|
||||
-- Both fixes change the stored row shape / instant, so existing rows must be
|
||||
-- re-synced. The boot-time backfill (per connection) deletes the affected
|
||||
-- SLEEP_DURATION rows, re-syncs with the corrected mapper, re-folds the rollup
|
||||
-- tier, and stamps `sleep_timeline_backfill_at` so the discovery query drops
|
||||
-- the connection. Null = backfill not yet run; the marker makes the one-shot
|
||||
-- idempotent across reboots.
|
||||
ALTER TABLE "whoop_connections" ADD COLUMN "sleep_timeline_backfill_at" TIMESTAMP(3);
|
||||
ALTER TABLE "withings_connections" ADD COLUMN "sleep_timeline_backfill_at" TIMESTAMP(3);
|
||||
@@ -2150,6 +2150,13 @@ model WithingsConnection {
|
||||
/// has never re-authed since v1.4.25 — the UI surfaces a banner
|
||||
/// inviting them to reconnect.
|
||||
scope String? @map("scope")
|
||||
/// v1.17.1 — one-shot marker for the sleep-stamp backfill. Pre-fix Withings
|
||||
/// sleep rows were stamped with the segment START; the reader treats
|
||||
/// `measuredAt` as the END, shifting every night one segment-length earlier.
|
||||
/// The boot-time backfill deletes the affected WITHINGS / SLEEP_DURATION rows
|
||||
/// and re-syncs with the corrected END stamp, then sets this so the discovery
|
||||
/// query drops the connection. Null = backfill not yet run.
|
||||
sleepTimelineBackfillAt DateTime? @map("sleep_timeline_backfill_at")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@ -2226,6 +2233,13 @@ model WhoopConnection {
|
||||
/// Set once the self-converging history backfill walks every collection to
|
||||
/// completion. Null = backfill still in flight (or never started).
|
||||
backfillCompletedAt DateTime? @map("backfill_completed_at")
|
||||
/// v1.17.1 — one-shot marker for the sleep-timeline backfill. Pre-fix WHOOP
|
||||
/// sleep rows stamped all five stage totals on the one sleep-END instant, so
|
||||
/// the hypnogram stacked them on the night's right edge. The boot-time
|
||||
/// backfill deletes the legacy summary rows and re-syncs the reconstructed
|
||||
/// per-segment timeline, then sets this so the discovery query drops the
|
||||
/// connection. Null = backfill not yet run.
|
||||
sleepTimelineBackfillAt DateTime? @map("sleep_timeline_backfill_at")
|
||||
/// Max heart rate from the WHOOP body-measurement profile — a profile
|
||||
/// constant, not a time series, so it lives here rather than as a
|
||||
/// Measurement row.
|
||||
|
||||
Reference in new issue
Block a user