feat(oura): capture daily resilience as an ordinal RESILIENCE metric

Oura's daily_resilience collection reports a categorical level — limited,
adequate, solid, strong, exceptional. Encode it ordinally (limited=1 …
exceptional=5) into the numeric measurement value so it fits the existing
model with no new categorical column: add the RESILIENCE measurement type
(migration 0186), fetch and map daily_resilience in the Oura sync, and skip
an unknown or missing level rather than coercing it to zero.

RESILIENCE_LEVELS in the Oura client is the single source of truth for the
encoding. The day-keyed externalId keeps a re-sync idempotent so a re-scored
day overwrites in place. Extend the measurement-type label, unit, range,
category, chart-token, and PR-direction maps; add the metric label across all
six locales.
This commit is contained in:
Marc-André Bombeck committed 2026-06-21 10:44:15 +02:00
1 parent 3faafef0df
commit aff4e9abeb
21 files changed
+338 -24

No files matched your search

+14
View File
@@ -116,6 +116,12 @@ export const measurementTypeEnum = z.enum([
// buckets. Both ingest server-side as `source = OURA`.
"SLEEP_SCORE",
"BODY_TEMPERATURE_DEVIATION",
// ── v1.19.0 — Oura resilience (additive) ──
// Oura's daily resilience LEVEL (`daily_resilience.level`) — a categorical
// band (limited / adequate / solid / strong / exceptional) ORDINAL-ENCODED
// into the numeric value (limited=1 … exceptional=5), unit `level`. Ingests
// server-side as `source = OURA`. See `RESILIENCE_LEVELS` in `src/lib/oura/client`.
"RESILIENCE",
]);
/**
@@ -333,6 +339,10 @@ const unitMap: Record<string, string> = {
SLEEP_SCORE: "score",
// Signed body-temperature deviation in °C (Oura nightly baseline offset).
BODY_TEMPERATURE_DEVIATION: "celsius",
// ── v1.19.0 — Oura resilience ──
// Ordinal level scale (1=limited … 5=exceptional) — the categorical band
// encoded into the numeric value. See RESILIENCE_LEVELS in src/lib/oura/client.
RESILIENCE: "level",
};
export function getUnitForType(type: string): string {
@@ -523,6 +533,10 @@ export const VALUE_RANGES: Record<string, { min: number; max: number }> = {
// rejects an obvious sensor glitch while keeping every real illness /
// luteal-phase swing.
BODY_TEMPERATURE_DEVIATION: { min: -5, max: 5 },
// ── v1.19.0 — Oura resilience ──
// Ordinal level scale: 1 (limited) … 5 (exceptional). The closed encoding
// never falls outside this band.
RESILIENCE: { min: 1, max: 5 },
};
export function validateMeasurementRange(