mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
feat(cycle): import Apple Health reproductive data into cycle, add cycle export
Promote the reproductive HealthKit category types out of the Apple-Health-import privacy-hold list and route them into cycle day-logs instead of measurements, and surface cycle data on the three export surfaces. Import promotion: - A new HealthKit -> cycle mapping module is the single source of truth for how MenstrualFlow, IntermenstrualBleeding, CervicalMucusQuality, OvulationTestResult, SexualActivity, PregnancyTestResult, ProgesteroneTestResult, Contraceptive and the symptom category types fold into CycleDayLog fields / seeded symptom keys. The XML importer accumulates same-day reproductive samples and writes one canonical day-log per day with source APPLE_HEALTH and a synthetic per-day externalId for idempotent re-import. The fold is gated on cycle tracking being enabled, so a non-cycle export never provisions cycle rows. A contraceptive method nudges the profile goal toward avoiding pregnancy only when the goal is still on its default. Basal body temperature stays a measurement and mirrors onto the day-log. Pregnancy/Lactation status and the pregnancy-bleeding types stay deferred; the cycle awareness types remain server-derived. Export: - FHIR emits LMP (LOINC 8665-2), average cycle length (64700-8), period length (64698-4) and the current phase as opt-in reproductive-health observations grouped into their own document section. - The doctor PDF gains an opt-in cycle section (recent cycles, average length and variability, current phase), privacy-default off like mood. - The full-backup export, the weekly backup worker and the off-host DR snapshot include the cycle tables, and the admin restore re-creates them; the day-log note round-trips as ciphertext and is never decrypted onto a report surface. Mapping table documented in docs/api/cycle-healthkit-mapping.md. Adds unit + integration coverage for the mapping, the import routing, the FHIR observations and the backup round-trip. OpenAPI regenerated. Six-locale strings added. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
7333d8e40f
commit
e984ada5f1
39 files changed
+2406
-41
No files matched your search
@@ -0,0 +1,71 @@
|
||||
# Apple Health → cycle mapping
|
||||
|
||||
How HealthLog folds Apple Health reproductive samples into the cycle model.
|
||||
Both the `export.xml` importer and the iOS batch contract share one mapping
|
||||
table: `src/lib/cycle/healthkit-mapping.ts`. This page is the human-readable
|
||||
companion — keep the two in sync.
|
||||
|
||||
Reproductive samples route into **cycle day-logs** (`CycleDayLog`), never into
|
||||
`Measurement`. Each Apple Health day collapses into one canonical day-log per
|
||||
`(user, date)`; the importer writes `source = APPLE_HEALTH` with a synthetic
|
||||
per-day `externalId` (`hkcycle:<YYYY-MM-DD>`) so a re-import upserts the same
|
||||
row. The fold only runs when cycle tracking is enabled for the account.
|
||||
|
||||
## Day-log fields
|
||||
|
||||
| HealthKit category type | CycleDayLog field | Values |
|
||||
|---|---|---|
|
||||
| `MenstrualFlow` | `flow` | unspecified→LIGHT, light→LIGHT, medium→MEDIUM, heavy→HEAVY, none→NONE |
|
||||
| `IntermenstrualBleeding` | `intermenstrualBleeding` | presence → `true` |
|
||||
| `CervicalMucusQuality` | `cervicalMucus` | dry→DRY, sticky→STICKY, creamy→CREAMY, watery→WATERY, eggWhite→EGG_WHITE |
|
||||
| `OvulationTestResult` | `ovulationTest` | negative→NEGATIVE, lhSurge→POSITIVE_LH_SURGE, indeterminate→INDETERMINATE, estrogenSurge→ESTROGEN_SURGE |
|
||||
| `SexualActivity` | `sexualActivity` + `protectedSex` | presence → `true`; `HKMetadataKeySexualActivityProtectionUsed` → `protectedSex` |
|
||||
| `PregnancyTestResult` | `pregnancyTest` | negative→NEGATIVE, positive→POSITIVE, indeterminate→INDETERMINATE |
|
||||
| `ProgesteroneTestResult` | `progesteroneTest` | negative→NEGATIVE, positive→POSITIVE, indeterminate→INDETERMINATE |
|
||||
| `Contraceptive` | `contraceptive` (+ profile nudge) | unspecified→UNSPECIFIED, implant→IMPLANT, injection→INJECTION, iud→IUD, ring→INTRAVAGINAL_RING, oral→ORAL, patch→PATCH, emergency→EMERGENCY |
|
||||
|
||||
HealthLog's `SPOTTING` flow level has no HealthKit counterpart; it is only ever
|
||||
produced by a manual entry. HealthKit `unspecified` flow maps to `LIGHT` (a
|
||||
logged-but-unspecified bleeding day is at least light flow).
|
||||
|
||||
## Symptom category types → seeded symptom keys
|
||||
|
||||
Symptom category samples link to the seeded `CycleSymptom` catalogue by key. A
|
||||
sample whose severity is *not present* (codepoint 1) does NOT create a link.
|
||||
|
||||
| HealthKit category type | CycleSymptom key |
|
||||
|---|---|
|
||||
| `AbdominalCramps` | `cramps` |
|
||||
| `Headache` | `headache` |
|
||||
| `Bloating` | `bloating` |
|
||||
| `Acne` | `acne` |
|
||||
| `BreastPain` | `breast_tenderness` |
|
||||
| `Fatigue` | `fatigue` |
|
||||
| `LowerBackPain` | `back_pain` |
|
||||
| `SleepChanges` | `insomnia` |
|
||||
| `MoodChanges` | `mood_swings` |
|
||||
| `AppetiteChanges` | `food_cravings` |
|
||||
| `Nausea` | `nausea` |
|
||||
|
||||
## Profile-level signals
|
||||
|
||||
A `Contraceptive` sample asserting an active method also nudges the
|
||||
`CycleProfile.goal` to `AVOID_PREGNANCY` — but only when the goal is still on
|
||||
its `GENERAL_HEALTH` default, so an explicit user choice is never overwritten.
|
||||
|
||||
## Basal body temperature
|
||||
|
||||
`basalBodyTemperature` is **not** routed here. It stays a
|
||||
`Measurement(BODY_TEMPERATURE)` (the system of record for the rollup tier and
|
||||
the cross-source source-priority ladder) and is mirrored onto
|
||||
`CycleDayLog.basalBodyTempC` for the cycle chart. Apple Watch wrist/skin
|
||||
temperature already flows to `WRIST_TEMPERATURE` / `SKIN_TEMPERATURE` and feeds
|
||||
the temperature-trend ovulation method directly from `Measurement`.
|
||||
|
||||
## Deferred
|
||||
|
||||
| HealthKit category type | Status |
|
||||
|---|---|
|
||||
| `Pregnancy`, `Lactation` | Deferred — no `CycleProfile` column yet (pregnancy-mode is a later release). |
|
||||
| `BleedingDuringPregnancy`, `BleedingAfterPregnancy` | Deferred — pregnancy-mode. |
|
||||
| `IrregularMenstrualCycles`, `InfrequentMenstrualCycles`, `ProlongedMenstrualPeriods`, `PersistentIntermenstrualBleeding` | Server-derived from the cycle engine, never ingested. |
|
||||
@@ -829,6 +829,8 @@ paths:
|
||||
type: boolean
|
||||
bmi:
|
||||
type: boolean
|
||||
cycle:
|
||||
type: boolean
|
||||
locale:
|
||||
type: string
|
||||
enum:
|
||||
@@ -955,6 +957,8 @@ paths:
|
||||
type: boolean
|
||||
bmi:
|
||||
type: boolean
|
||||
cycle:
|
||||
type: boolean
|
||||
resourceTypes:
|
||||
maxItems: 8
|
||||
type: array
|
||||
|
||||
Reference in new issue
Block a user