## Audit-Ergebnis – priorisierte Findings ### P0 – kritisch 1. **Credential fest im Quellcode hinterlegt** - `apple_health_drive_sync.py:20-23` - Ein Schlüsselring-Passwort wird als Klartext-Default gesetzt. Das ist ein unmittelbares Secret-Leak-Risiko, insbesondere bei Backups oder Git-Synchronisation. - **Maßnahme:** Secret sofort rotieren, aus Datei/Git-Historie entfernen und ausschließlich über Environment Variable bzw. geschützten Credential Store beziehen. Bei fehlender Variable hart abbrechen. 2. **Fehlerhafte Imports können partiell committed und danach dauerhaft blockiert werden** - `apple_health_import.py:222-265` - Records werden einzeln eingefügt. Bei einem späteren unerwarteten Fehler erfolgt kein `rollback`; der Exception-Pfad schreibt anschließend den Fehlerstatus und führt `commit()` aus (`257-262`). Dadurch können Teilimporte bestehen bleiben. - Gleichzeitig behandelt die Vorprüfung jeden vorhandenen Datei-Hash unabhängig vom Status als erfolgreich verarbeitet (`227-229`). Eine Datei mit `status='error'` wird bei Wiederholung daher nicht erneut versucht. - **Maßnahme:** Explizite Transaktion pro Datei, bei Fehler `rollback()`, anschließend Fehlerstatus in separater Transaktion speichern. Nur `status='imported'` als abgeschlossen behandeln; Retry-Zähler und Fehlerhistorie ergänzen. ### P1 – hohe Priorität 3. **Tagessummen können Quellen und überlappende Aggregate doppelt zählen** - `apple_health_analytics.py:59-77`, `80-100` - Dedupliziert wird nur nach `(metric, source_name, start_date, end_date, unit)`. Anschließend summiert `mode='sum'` alle verbleibenden Quellen desselben Tages. - Mehrere Geräte/Apps können dieselbe Aktivität erfassen. Es gibt keine Source-Priorität oder Apple-Health-Konsolidierungsregel. Schritte, Distanz und Energie können deshalb überschätzt werden. - **Maßnahme:** Pro Metrik eine dokumentierte Source-Priority einführen oder bevorzugt bereits von HealthKit konsolidierte Tagesaggregate exportieren. Überschneidende Intervalle dürfen nicht blind addiert werden. 4. **Deduplizierung kann sowohl echte Messungen verlieren als auch Dubletten behalten** - Import-Fingerprint: `apple_health_import.py:125-150` - Kanonische Deduplizierung: `apple_health_analytics.py:70-77` - Der Record-Hash enthält das vollständige Raw-JSON. Bereits kleine Export-Metadatenänderungen erzeugen neue Raw-Records. - Umgekehrt ignoriert der kanonische Schlüssel `value` und `device`: zwei legitime Messungen mit identischem Zeitintervall und gleicher Quelle können auf eine Zeile reduziert werden. - **Maßnahme:** Stabilen fachlichen Observation-Key definieren, z. B. HealthKit UUID, falls verfügbar; andernfalls Typ, Zeitpunkte, Wert, normalisierte Einheit, Quelle und Gerätekennung. Export-/Import-Provenienz getrennt von der Observation modellieren. 5. **Wochenwerte werden methodisch irreführend behandelt** - `apple_health_analytics.py:49-56`, `90-97` - Nur ein exakt benannter historischer Export wird als wöchentlich erkannt. Andere Jahres-/Wochenexporte oder umbenannte Dateien werden nicht erkannt. - Division durch sieben erzeugt zwar einen Tagesdurchschnitt, schreibt diesen aber nur auf den Starttag der Woche. Diagramme erscheinen dadurch lückenhaft und Tages-Min/Max bzw. „letzter Wert“ werden fehlinterpretiert. - Teilwochen werden ebenfalls pauschal durch sieben geteilt. - **Maßnahme:** Aggregationsintervall und Granularität beim Import speichern. Wochenwerte entweder separat anzeigen oder auf alle tatsächlich abgedeckten Kalendertage verteilen; keine Vermischung mit echten Tageswerten. 6. **Timezone- und Tagesgrenzen sind nicht korrekt modelliert** - Normalisierung: `apple_health_import.py:115-150` - Tageszuordnung: `apple_health_analytics.py:80-97` - Das Schema speichert Zeitpunkte als unvalidierten Text. Die Analyse verwendet lediglich die ersten zehn Zeichen von `start_date`; UTC-Offset, lokale Zeitzone und DST werden nicht ausgewertet. - Schlaf wird dadurch grundsätzlich dem Startdatum zugeordnet, obwohl klinische Tagesdarstellungen meist die Nacht nach Aufwachdatum benennen. - **Maßnahme:** Original-Zeitzone/Offset speichern, Zeitpunkte robust parsen und explizit in die gewünschte lokale Zone konvertieren. Für Schlaf eine dokumentierte „sleep day“-Regel auf Basis des End-/Aufwachzeitpunkts verwenden. 7. **Aktueller Tag wird nur heuristisch und systemzeitabhängig ausgeschlossen** - `apple_health_analytics.py:80-96` - `datetime.now()` nutzt die Server-Zeitzone. Es gibt keinen Export-Watermark bzw. „complete through“-Zeitpunkt. - Ein bereits abgeschlossener Tag kann je nach 12-Stunden-Synchronisierung ebenfalls unvollständig sein; verspätete Daten oder Zeitzonenabweichungen werden nicht erkannt. - **Maßnahme:** Importdateien um Exportzeit, Zeitzone und Abdeckungsende erweitern. Nur Tage vor einem verifizierten Vollständigkeits-Watermark als stabil kennzeichnen; provisional/stable im Dashboard sichtbar machen. 8. **Download-/Retry-Logik kann auf einer lokalen Datei hängen bleiben** - `apple_health_drive_sync.py:38-46` - Existiert eine nichtleere Zieldatei, wird sie ohne Vergleich von Drive-ID, Modified-Time oder Hash übersprungen. Das betrifft insbesondere fehlgeschlagene Imports und auf Drive überschriebene Dateien. - Ein bereits importierter Hash wird vom Importer nicht verschoben (`apple_health_import.py:227-229`), sodass eine solche Datei dauerhaft im Inbox-Verzeichnis verbleiben kann. - **Maßnahme:** Drive-ID, Version/Modified-Time und MD5 speichern; atomar in temporäre Datei laden und nach erfolgreichem Import archivieren. Bereits bekannte Dateien kontrolliert entfernen oder als verarbeitet markieren. ### P2 – mittlere Priorität 9. **Einheiten werden nur punktuell und per Stringvergleich normalisiert** - `apple_health_import.py:106-112`, `115-150` - `apple_health_analytics.py:87-91`, `103-114` - Nur `kJ → kcal` für zwei Metriken ist implementiert, und nur bei exakt `"kJ"`. Keine kanonische Unit-Tabelle oder Dimensionsprüfung. - Prozentwerte können je nach Export als Bruch oder Prozentzahl auftreten; Distanz-, Masse-, Temperatur- und Blutdruckvarianten werden nicht konvertiert bzw. validiert. - `unit_for()` meldet lediglich die häufigste Einheit, obwohl die Tagesreihe potenziell mehrere Einheiten mischt. - **Maßnahme:** Metrik-Registry mit kanonischer Einheit, erlaubten Einheiten, Konvertierungsfunktion und plausiblen Bereichen. Inkompatible Einheiten als QA-Fehler behandeln statt zusammenzufassen. 10. **Durchschnittsbildung ist statistisch nicht belastbar** - `apple_health_analytics.py:80-100` - Tagesdurchschnitte sind ungewichtete Mittelwerte aller Records. Messungen mit unterschiedlicher Dauer oder bereits aggregierte Mittelwerte erhalten dasselbe Gewicht. - Herzfrequenz, Atemrate, HRV und Physical Effort benötigen je nach Datentyp unterschiedliche Aggregationen; HRV ist häufig eher median-/robustheitsorientiert. - **Maßnahme:** Metrikspezifische Aggregatoren verwenden: zeitgewichtetes Mittel, Median, Min/Max, Summe oder letzte valide Messung. Record-Dauer und Sample-Anzahl speichern. 11. **Parser deckt komplexe HealthKit-Strukturen nur unvollständig ab** - `apple_health_import.py:115-124`, `154-208` - `blood_pressure` kann als verschachtelte Korrelation vorliegen; `parse_float()` kann solche Objekte nicht normalisieren. Die Liste `systolic/diastolic` liefert außerdem nur einen einzelnen Wert. - Wenn `data.metrics` vorhanden ist, beendet der Parser danach die Verarbeitung (`163-179`); parallel vorhandene Workouts oder andere Top-Level-Bereiche werden ignoriert. - Workout-Routen, Workout-Typ, Dauer, Energie, Distanz und Schlafstadien besitzen kein eigenes normalisiertes Modell. - **Maßnahme:** Fixture-basierte Parser pro Exportstruktur; Blutdruck in gekoppelte systolische/diastolische Felder, Workouts und Schlafsegmente in eigene Tabellen normalisieren. 12. **Dashboard deckt nur einen Teil der importierten Metriken ab** - `health_dashboard_v3.py:32-44` - Nicht dargestellt werden unter anderem Blutdruck, Workouts, Basalenergie, Stand-/Trainingszeit, Gehparameter und weitere verfügbare Mobilitätsmetriken. - Das initial dokumentierte Ziel „Blood Pressure / Workouts / Basal Energy / Temperature“ ist damit nur teilweise umgesetzt. - **Maßnahme:** Coverage-Matrix „importiert → validiert → aggregiert → angezeigt“ führen. Klinisch relevante Metriken priorisieren; unbekannte oder nicht validierte Metriken nicht stillschweigend ignorieren. 13. **Fehlende Daten sind von echten Nullwerten nicht unterscheidbar** - `apple_health_analytics.py:80-100` - Nur vorhandene Tage werden zurückgegeben. Es gibt keinen vollständigen Kalenderindex, keine Coverage-Kennzahl und keine Kennzeichnung von Geräte-Tragepausen. - Im Dashboard können dadurch Linien über lange Datenlücken laufen und Kontinuität vortäuschen. - **Maßnahme:** Erwartete Tagesachse erzeugen, fehlende Tage als `null` belassen, Coverage/Tragezeit anzeigen und Chart.js `spanGaps=false` explizit setzen. 14. **Keine Outlier- oder Plausibilitätsprüfung** - `apple_health_import.py:106-112` - `apple_health_analytics.py:80-100` - Jeder numerisch parsebare Wert fließt ein. Negative Summen, unplausible physiologische Werte, unmögliche Dauer oder inkonsistente Start-/Endzeiten werden nicht markiert. - **Maßnahme:** Soft-/Hard-Limits pro Metrik, `end >= start`, Einheitenprüfung, Änderungsraten und Quarantäne-/QA-Status. Raw-Record behalten, aber fragliche Werte standardmäßig aus Trends ausschließen. 15. **Trends bestehen nur aus globalem Mittel, Minimum und Maximum** - `health_dashboard_v3.py:143-152`, `270-281` - Keine rollierenden Baselines, robuste Streuung, Wochenvergleich, Trendsteigung, Konfidenz, Mindeststichprobe oder Veränderung gegenüber persönlicher Baseline. - Gesamtdurchschnitte über lange Zeiträume sind anfällig für Gerätewechsel, Therapiephasen, Saison und unterschiedliche Datenabdeckung. - **Maßnahme:** 7-/28-Tage-Rolling-Median, IQR/MAD, Coverage und Vergleich mit persönlicher Baseline ergänzen; Mindestdatenmenge und Unsicherheit anzeigen. 16. **Konfundierung und Quellenwechsel werden nicht berücksichtigt** - `apple_health_analytics.py:70-100` - `health_dashboard_v3.py:143-152` - Gerät, Quelle, Tragezeit, Krankheit, Medikamente und Therapiephasen werden nicht in der Apple-Health-Analyse berücksichtigt. Ein Gerätewechsel kann als Gesundheitsänderung erscheinen. - **Maßnahme:** Source-/Device-Timeline, Change-Point-Markierungen und stratifizierte Analysen ergänzen. Beobachtete Assoziationen ausdrücklich nicht kausal interpretieren. 17. **Schema ist für belastbare Provenienz und Validierung zu schwach** - `apple_health_import.py:41-83` - Keine Foreign Keys zwischen Records und Importdatei, obwohl die Analytics-Dokumentation einen `import_file_id` erwähnt (`apple_health_analytics.py:14`). - Keine HealthKit UUID, Exportzeit, Zeitzone, Granularität, Aggregationsart, QA-Status, Normalisierungsversion oder `CHECK`-Constraints. - `file_name`/`file_hash` werden pro Record redundant gespeichert. - **Maßnahme:** Versionierte Migration mit `import_file_id`, Observation-ID, Source/Device-Dimension, parsed timestamps, timezone, aggregation period/type, canonical value/unit und QA-Feldern. ### P3 – Qualität und Wartbarkeit 18. **Keine automatisierten Tests für Apple-Health-Pipeline gefunden** - Betroffene Dateien: alle vier geprüften Module. - Der Syntaxcheck der vier Skripte war erfolgreich, ersetzt aber keine funktionalen Tests. - Benötigt werden mindestens: - Parser-Fixtures für JSON/CSV, Schlaf, Blutdruck und Workouts - Idempotenz und Error-Retry - Quellpriorität und überlappende Intervalle - Zeitzonen/DST und Nacht-/Tagesgrenzen - Teilwochen und aktuelle/provisorische Tage - Unit-Konvertierung und Ausreißer - Missing-Day-Darstellung - Golden Tests für Dashboard-Serien ohne Patientendaten ## Positiv festgestellt - Raw-Records bleiben für Auditierbarkeit erhalten. - Datei- und Record-Hashes sowie Unique Constraints bieten eine erste Idempotenzbasis. - Der aktuelle Tag wird zumindest bewusst als provisorisch behandelt. - Das Dashboard verwendet die zentrale Analytics-Schicht statt direkt Raw-Werte zu summieren. - Alle vier geprüften Python-Dateien lassen sich syntaktisch kompilieren. - Die vorhandenen Datumsfelder waren strukturell parsebar; im Bericht wurden keine Rohwerte oder Patientengeheimnisse ausgegeben. ## Durchführung / Änderungen - Geprüft: `apple_health_drive_sync.py`, `apple_health_import.py`, `apple_health_analytics.py`, Apple-Health-Abschnitte in `health_dashboard_v3.py` sowie die beiden Apple-Health-Tabellen und Indizes der lokalen SQLite-DB. - Zusätzlich nur aggregierte Metadaten zu Metriken, Einheiten, Quellenabdeckung und Importstatus geprüft. - **Keine Dateien erstellt oder verändert.** - **Probleme:** Keine Testsuite vorhanden; Bewertung basiert daher auf Code-, Schema- und Metadaten-Audit plus erfolgreichem Syntaxcheck.