Pierre Laub endurance × creativity

ETL-Update: Excel- & Coros-Gesundheitsdaten nahtlos in meine BigQuery-Pipeline integrieren

Motivation: Mehr Coros-Daten, ein Format mehr

Nach FIT-Dateien (Aktivitäten) und CSV-Exports (tägliche Metriken) kam der nächste logische Schritt:
Excel-Exports (.xlsx) von Coros, insbesondere für tägliche Gesundheitsdaten wie Herzfrequenz, HRV und Schlaf.

Das Ziel war klar:

  • kein separater Import-Flow
  • keine manuelle Schema-Anpassung
  • keine Dashboard-Brüche
  • weiterhin idempotent & automatisiert

Kurz: Ein weiteres Datenformat sollte sich nahtlos in die bestehende Pipeline einfügen.


1. Modell-Erweiterung: Mehr Herzfrequenz, gleiche API

Im zentralen Datenmodell (src/config.py) wurde die bestehende metrics-Tabelle erweitert, um die Excel-Daten vollständig abzubilden.

Neu hinzugekommen sind u.a.:

Heart Rate

  • resting_heart_rate
  • min_heart_rate
  • max_heart_rate
  • avg_heart_rate

HRV

  • hrv_avg

Wichtig war mir dabei Rückwärtskompatibilität:
Das bestehende Feld pulse wird weiterhin mit dem Average Heart Rate befüllt, damit:

  • bestehende Dashboards unverändert weiterlaufen
  • keine API-Verträge brechen
  • neue Daten sofort sichtbar werden

➡️ Neue Daten, keine Migration-Schmerzen.


2. Automatisches Schema-Update in BigQuery

Mit wachsendem Datenmodell wird eines schnell lästig:
manuelle Schema-Updates in BigQuery.

Deshalb wurde der BigQueryClient (src/bigquery_client.py) erweitert:

  • Beim Start der Pipeline wird geprüft:
    • Welche Spalten existieren bereits?
    • Welche fehlen im Vergleich zum aktuellen Modell?
  • Fehlende Spalten werden automatisch per ALTER TABLE ADD COLUMN ergänzt.

Das bedeutet konkret:

  • kein Löschen von Tabellen
  • kein Re-Deploy
  • kein Risiko für bestehende Daten

➡️ Schema-Evolution als Teil der Pipeline, nicht als manueller Schritt.


3. Neuer Excel-Parser (src/excel_parser.py)

Für die .xlsx-Exports wurde ein dedizierter Parser implementiert.

Validierung

Der Parser erkennt automatisch, ob eine Excel-Datei das erwartete Format besitzt, z.B.:

  • User ID
  • Date
  • Schlaf- und HR-Spalten

Fehlerhafte oder fremde Excel-Dateien werden sauber abgelehnt.

Einheiten-Konversion

Ein klassisches Problem:
Coros liefert viele Schlafwerte in Minuten, das interne Modell arbeitet jedoch mit Stunden.

Der Parser übernimmt automatisch:

  • time_in_deep_sleep
  • time_in_light_sleep
  • time_awake
  • sleep_hours

➡️ Einheitlich, unabhängig von der Datenquelle.

Header-Mapping

Die Excel-Header (teils deutsch/englisch gemischt) werden sauber auf technische Feldnamen gemappt, sodass:

  • CSV
  • Excel
  • zukünftige Exporte

alle auf das gleiche BigQuery-Schema laufen.


4. Integration in die bestehende Pipeline

Die Pipeline selbst (src/etl_pipeline.py) musste nur minimal angepasst werden.

Dateierkennung

Der File-Scanner (find_unprocessed_files) sucht nun explizit nach:

  • .xlsx
  • .XLSX

Dynamische Verarbeitung

Je nach Dateityp wird automatisch gewählt:

  • FIT → FIT-Parser
  • CSV → CSV-Parser
  • XLSX → Excel-Parser

Idempotenz bleibt erhalten

Wie bei allen anderen Datenquellen gilt weiterhin:

  • SHA-256 Hash pro Datei / Datensatz
  • Prüfung gegen BigQuery
  • kein doppeltes Laden – selbst bei mehrfachen Uploads

➡️ Excel-Daten sind first-class citizens der Pipeline.


5. Neue Abhängigkeiten

Für die Excel-Verarbeitung wurden ergänzt:

  • pandas
  • openpyxl

Beides etabliert, stabil und gut wartbar – keine exotischen Abhängigkeiten.


Status: Eine Pipeline für alle Sportdaten

Mit diesem Update verarbeitet die Pipeline nun in einem Lauf:

  • FIT-Dateien → Aktivitäten & Zeitreihen
  • CSV-Dateien → tägliche Metriken
  • Excel-Dateien (.xlsx) → Coros-Gesundheitsdaten

Fazit

Dieses Update war weniger „Feature“, mehr Architektur-Test:

Wie erweiterbar ist meine Pipeline wirklich?

Die Antwort: sehr.

  • Neues Format → kein Rewrite
  • Neues Schema → kein manueller Eingriff
  • Neue Daten → sofort sichtbar

Genau so soll sich ein privates Data-Analytics-Projekt anfühlen: kontrolliert wachsend, nicht fragil.