[FEATURE] Universeller CSV-Parser mit lernbarem Feldmapping #21

Open
opened 2026-03-23 15:37:22 +01:00 by Lars · 0 comments
Owner

Feature Request: Universeller CSV-Parser

Beschreibung

Ein modulübergreifendes CSV-Import-System, das Feldmappings abfragt, erlernt und für verschiedene Datenquellen wiederverwendet.

Motivation

Aktuell gibt es separate CSV-Import-Endpunkte für:

  • Ernährung (FDDB)
  • Aktivität (Apple Health)
  • Schlaf (Apple Health)
  • Vitalwerte (Omron, Apple Health)

Jeder Import hat hardcodierte Feldnamen. Bei neuen Formaten oder Apps muss der Code angepasst werden.

Ziel

Ein universeller Parser, der:

  1. CSV hochladen → Spalten erkennen
  2. Nutzer fragt: "Welches Feld ist Datum?" → Dropdown mit Spalten
  3. Mapping speichert (DB: csv_field_mappings)
  4. Beim nächsten Import: automatisch erkennen (Fingerprint: Spalten-Hash)
  5. Werte direkt in Zielmodul importieren (weight_log, nutrition_log, activity_log, etc.)

Anforderungen

1. Feldmapping-Assistent (UI)

Flow:

  • CSV hochladen
  • System erkennt Spalten: ["Datum", "Gewicht (kg)", "Notiz"]
  • Falls unbekannt: Mapping-Dialog
    Zielmodul: [Dropdown: Gewicht | Ernährung | Aktivität | Schlaf | Vitalwerte]
    
    Feldmappings:
    CSV-Spalte "Datum"       → [Dropdown: date | created_at | timestamp | ---]
    CSV-Spalte "Gewicht"     → [Dropdown: weight | value | ---]
    CSV-Spalte "Notiz"       → [Dropdown: note | comment | ---]
    
    [X] Mapping speichern für zukünftige Importe
    

2. Datenbankschema

Tabelle: csv_field_mappings

CREATE TABLE csv_field_mappings (
  id UUID PRIMARY KEY,
  profile_id UUID REFERENCES profiles(id),  -- NULL = global
  target_module VARCHAR(50),  -- "weight", "nutrition", "activity", etc.
  source_fingerprint VARCHAR(64),  -- SHA256 Hash der Spalten (sortiert)
  source_name VARCHAR(100),  -- z.B. "Withings Export", "MyFitnessPal CSV"
  field_mappings JSONB,  -- {"Datum": "date", "Gewicht (kg)": "weight"}
  created_at TIMESTAMP DEFAULT NOW()
);

Fingerprint-Berechnung:

import hashlib, json
columns = sorted(["Datum", "Gewicht (kg)", "Notiz"])
fingerprint = hashlib.sha256(json.dumps(columns).encode()).hexdigest()

3. API-Endpunkte

POST /api/import/universal

  • Body: multipart/form-data mit CSV-Datei
  • Rückgabe:
    • Falls bekannt: {"fingerprint": "abc123", "mapping_id": "uuid", "preview": [...]}
    • Falls unbekannt: {"fingerprint": "abc123", "columns": ["Datum", ...], "needs_mapping": true}

POST /api/import/universal/map

  • Body: {"fingerprint": "abc123", "target_module": "weight", "mappings": {...}, "source_name": "..."}
  • Speichert Mapping in DB
  • Führt Import aus

GET /api/import/mappings

  • Zeigt alle gespeicherten Mappings (global + user-spezifisch)

DELETE /api/import/mappings/{id}

  • Löscht Mapping

4. Modul-Integration

Jeder Zielmodul definiert erforderliche Felder:

MODULE_SCHEMAS = {
    "weight": {
        "required": ["date", "weight"],
        "optional": ["note"],
        "target_table": "weight_log"
    },
    "nutrition": {
        "required": ["date", "kcal"],
        "optional": ["protein_g", "fat_g", "carbs_g"],
        "target_table": "nutrition_log"
    }
}

5. UI-Komponente

UniversalCsvImport.jsx

  • Drag & Drop Upload
  • Falls Mapping bekannt: "Import starten"
  • Falls unbekannt: Mapping-Dialog
  • History: Liste aller Mappings mit Edit/Delete

Integration in Module:

  • WeightScreen, NutritionPage, ActivityPage, SleepPage, VitalsPage
  • Zusätzlicher Tab "Universal-Import" neben den spezifischen Imports

Vorteile

  • Flexibilität: Neue Apps/Formate ohne Code-Änderung
  • Lernfähig: Einmal mappen → immer verfügbar
  • Nutzerfreundlich: Visuelles Mapping statt manueller Spalten-Umbennung
  • Wiederverwendbar: Mappings teilen (global/user-spezifisch)
  • Zukunftssicher: Beliebige CSV-Quellen unterstützen

Beispiel-Workflow

  1. Nutzer lädt "withings_export.csv" hoch (noch nie gesehen)
  2. System: "Unbekanntes Format. Spalten: [Date, Weight (kg), Fat Mass (kg)]"
  3. Nutzer mappt: Date → date, Weight (kg) → weight, Fat Mass (kg) → (ignorieren)
  4. System: "Mapping gespeichert als Withings Export"
  5. Import: 120 Zeilen → weight_log
  6. Nächstes Mal: Withings CSV → automatisch erkannt → 1-Klick-Import

Technische Details

  • Parser: csv Modul (Python stdlib)
  • Encoding-Erkennung: chardet für UTF-8/ISO-8859-1
  • Validierung: Pydantic Models pro Zielmodul
  • Duplikat-Erkennung: Wie bei bestehenden Imports (date-basiert)
  • Logging: Fehlerhafte Zeilen überspringen, in Import-Report auflisten

Phasen

Phase 1: MVP

  • Tabelle csv_field_mappings
  • Fingerprint-Berechnung
  • POST /api/import/universal (Erkennung)
  • POST /api/import/universal/map (Speichern + Import)
  • Modul-Schema für weight
  • Basis-UI: Mapping-Dialog

Phase 2: Erweitert

  • Alle Module (nutrition, activity, sleep, vitals, circumference, caliper)
  • GET /api/import/mappings (History)
  • DELETE Mappings
  • Mapping bearbeiten (Update)
  • Global vs. User-spezifisch

Phase 3: Komfort

  • Auto-Suggest ("Spalte Gewicht → wahrscheinlich weight")
  • Datentyp-Validierung ("Spalte enthält keine Zahlen → kann nicht weight sein")
  • Vorschau vor Import (erste 5 Zeilen)
  • Einheiten-Konvertierung (lbs → kg, etc.)

Priorität

Medium – Nice-to-have für v9e/v9f

# Feature Request: Universeller CSV-Parser ## Beschreibung Ein modulübergreifendes CSV-Import-System, das Feldmappings abfragt, erlernt und für verschiedene Datenquellen wiederverwendet. ## Motivation Aktuell gibt es separate CSV-Import-Endpunkte für: - Ernährung (FDDB) - Aktivität (Apple Health) - Schlaf (Apple Health) - Vitalwerte (Omron, Apple Health) Jeder Import hat hardcodierte Feldnamen. Bei neuen Formaten oder Apps muss der Code angepasst werden. ## Ziel Ein **universeller Parser**, der: 1. CSV hochladen → Spalten erkennen 2. Nutzer fragt: "Welches Feld ist Datum?" → Dropdown mit Spalten 3. Mapping speichert (DB: `csv_field_mappings`) 4. Beim nächsten Import: automatisch erkennen (Fingerprint: Spalten-Hash) 5. Werte direkt in Zielmodul importieren (weight_log, nutrition_log, activity_log, etc.) ## Anforderungen ### 1. Feldmapping-Assistent (UI) **Flow:** - CSV hochladen - System erkennt Spalten: ["Datum", "Gewicht (kg)", "Notiz"] - Falls unbekannt: Mapping-Dialog ``` Zielmodul: [Dropdown: Gewicht | Ernährung | Aktivität | Schlaf | Vitalwerte] Feldmappings: CSV-Spalte "Datum" → [Dropdown: date | created_at | timestamp | ---] CSV-Spalte "Gewicht" → [Dropdown: weight | value | ---] CSV-Spalte "Notiz" → [Dropdown: note | comment | ---] [X] Mapping speichern für zukünftige Importe ``` ### 2. Datenbankschema **Tabelle: `csv_field_mappings`** ```sql CREATE TABLE csv_field_mappings ( id UUID PRIMARY KEY, profile_id UUID REFERENCES profiles(id), -- NULL = global target_module VARCHAR(50), -- "weight", "nutrition", "activity", etc. source_fingerprint VARCHAR(64), -- SHA256 Hash der Spalten (sortiert) source_name VARCHAR(100), -- z.B. "Withings Export", "MyFitnessPal CSV" field_mappings JSONB, -- {"Datum": "date", "Gewicht (kg)": "weight"} created_at TIMESTAMP DEFAULT NOW() ); ``` **Fingerprint-Berechnung:** ```python import hashlib, json columns = sorted(["Datum", "Gewicht (kg)", "Notiz"]) fingerprint = hashlib.sha256(json.dumps(columns).encode()).hexdigest() ``` ### 3. API-Endpunkte **POST /api/import/universal** - Body: `multipart/form-data` mit CSV-Datei - Rückgabe: - Falls bekannt: `{"fingerprint": "abc123", "mapping_id": "uuid", "preview": [...]}` - Falls unbekannt: `{"fingerprint": "abc123", "columns": ["Datum", ...], "needs_mapping": true}` **POST /api/import/universal/map** - Body: `{"fingerprint": "abc123", "target_module": "weight", "mappings": {...}, "source_name": "..."}` - Speichert Mapping in DB - Führt Import aus **GET /api/import/mappings** - Zeigt alle gespeicherten Mappings (global + user-spezifisch) **DELETE /api/import/mappings/{id}** - Löscht Mapping ### 4. Modul-Integration Jeder Zielmodul definiert **erforderliche Felder**: ```python MODULE_SCHEMAS = { "weight": { "required": ["date", "weight"], "optional": ["note"], "target_table": "weight_log" }, "nutrition": { "required": ["date", "kcal"], "optional": ["protein_g", "fat_g", "carbs_g"], "target_table": "nutrition_log" } } ``` ### 5. UI-Komponente **UniversalCsvImport.jsx** - Drag & Drop Upload - Falls Mapping bekannt: "Import starten" - Falls unbekannt: Mapping-Dialog - History: Liste aller Mappings mit Edit/Delete **Integration in Module:** - WeightScreen, NutritionPage, ActivityPage, SleepPage, VitalsPage - Zusätzlicher Tab "Universal-Import" neben den spezifischen Imports ## Vorteile - **Flexibilität:** Neue Apps/Formate ohne Code-Änderung - **Lernfähig:** Einmal mappen → immer verfügbar - **Nutzerfreundlich:** Visuelles Mapping statt manueller Spalten-Umbennung - **Wiederverwendbar:** Mappings teilen (global/user-spezifisch) - **Zukunftssicher:** Beliebige CSV-Quellen unterstützen ## Beispiel-Workflow 1. Nutzer lädt "withings_export.csv" hoch (noch nie gesehen) 2. System: "Unbekanntes Format. Spalten: [Date, Weight (kg), Fat Mass (kg)]" 3. Nutzer mappt: Date → date, Weight (kg) → weight, Fat Mass (kg) → (ignorieren) 4. System: "Mapping gespeichert als Withings Export" 5. Import: 120 Zeilen → weight_log 6. Nächstes Mal: Withings CSV → automatisch erkannt → 1-Klick-Import ## Technische Details - **Parser:** `csv` Modul (Python stdlib) - **Encoding-Erkennung:** `chardet` für UTF-8/ISO-8859-1 - **Validierung:** Pydantic Models pro Zielmodul - **Duplikat-Erkennung:** Wie bei bestehenden Imports (date-basiert) - **Logging:** Fehlerhafte Zeilen überspringen, in Import-Report auflisten ## Phasen ### Phase 1: MVP - [ ] Tabelle `csv_field_mappings` - [ ] Fingerprint-Berechnung - [ ] POST /api/import/universal (Erkennung) - [ ] POST /api/import/universal/map (Speichern + Import) - [ ] Modul-Schema für `weight` - [ ] Basis-UI: Mapping-Dialog ### Phase 2: Erweitert - [ ] Alle Module (nutrition, activity, sleep, vitals, circumference, caliper) - [ ] GET /api/import/mappings (History) - [ ] DELETE Mappings - [ ] Mapping bearbeiten (Update) - [ ] Global vs. User-spezifisch ### Phase 3: Komfort - [ ] Auto-Suggest ("Spalte Gewicht → wahrscheinlich weight") - [ ] Datentyp-Validierung ("Spalte enthält keine Zahlen → kann nicht weight sein") - [ ] Vorschau vor Import (erste 5 Zeilen) - [ ] Einheiten-Konvertierung (lbs → kg, etc.) ## Priorität Medium – Nice-to-have für v9e/v9f
Lars added the
feature
develop
labels 2026-03-23 15:37:22 +01:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: Lars/mitai-jinkendo#21
No description provided.