Skip to content

Apple Health Extraction

  owner: jimmy-neutron + zoro
  first_touched: 2026-05-26
  last_touched: 2026-05-26

Phase 1 Shipped — Export.xml Python ETL to Per-Category CSVs

source: documentation/activities/apple-health-phase1.md

Task #840054. Phase 1 (export.xml to Python ETL to per-category CSVs) is live. Phase 2 (Shortcut daily delta sync) and Phase 3 (Parquet + jimmy-vps webhook) deferred.

Files landed (Sandpit commit 877cc21)

  • ~/Sandpit/hinata/scripts/parse-apple-health.py — stdlib-only ElementTree.iterparse over export.xml. Streaming = memory-bounded; safe on 1-10GB exports. Per-HKType cursor in state.json = idempotent.

  • ~/Library/LaunchAgents/com.hinata.health-normaliser.plist — 03:15 daily. Not loaded yet.

Storage shape

~/Sandpit/hinata/data/health/
├── inbox/         drop export.zip or export.xml here
├── processed/     timestamped archive of consumed inputs
├── csv/
│   ├── activity/  steps, distance, active_energy, exercise_min, stand_hr, flights
│   ├── vitals/    heart_rate, resting_hr, hrv, spo2, respiratory_rate, vo2max, bp_*, body_temp
│   ├── sleep/     sleep_stages
│   ├── body/      weight, bmi, body_fat, lean_mass, height, waist
│   ├── mobility/  walking_asymmetry, double_support, step_length, walking_speed, stair_*
│   ├── nutrition/ calories, protein, carbs, fat, water, caffeine, sugar, fiber, sodium
│   ├── mindfulness/ meditation
│   ├── cardiac/   afib_events, high_hr_events, low_hr_events
│   ├── environmental/ headphone_exposure, noise_exposure
│   ├── workouts/  workouts (with totalEnergyBurned + totalDistance)
│   └── other/     any unmapped HKType (safety net)
├── state.json     cursor: {HKType: lastSeenStartDate}
└── parse-apple-health.log

Why stdlib-only

pandas + lxml would be cleaner code but add dependency installs to every Mac that runs the LaunchAgent. stdlib xml.etree.ElementTree.iterparse is sufficient and runs everywhere.

Verification path (Michael)

  1. iPhone Health profile picture → Export All Health Data → AirDrop export.zip to Mac

  2. Save to ~/Sandpit/hinata/data/health/inbox/

  3. python3 ~/Sandpit/hinata/scripts/parse-apple-health.py

  4. ls ~/Sandpit/hinata/data/health/csv/ → confirm categories

  5. launchctl load ~/Library/LaunchAgents/com.hinata.health-normaliser.plist

What this closes

* #840054 closure trigger met when verification step 5 runs without error

* #200009 (Zoro body-weight query) auto-resolves once csv/body/weight.csv exists

◆ hinata · apple-health-extraction · folded from documentation/activities/ phase-19