2026-04-09 21:37:19 +02:00
-- Migration 043: CSV Parser – System-Templates (Issue #21)
-- Idempotent: pro Template nur einfügen, wenn noch kein System-Eintrag für module+mapping_name existiert.
INSERT INTO csv_field_mappings (
profile_id , is_system , module , mapping_name , description ,
column_signature , delimiter , encoding , has_header ,
field_mappings , type_conversions
)
SELECT
NULL ,
true ,
' nutrition ' ,
' FDDB Export (Standard) ' ,
' Standard-Format für FDDB.de CSV-Exporte (Deutsch). Delimiter Semikolon, kJ → kcal Konvertierung. ' ,
ARRAY [ ' datum_tag_monat_jahr_stunde_minute ' , ' fett_g ' , ' kh_g ' , ' kj ' , ' protein_g ' ] : : TEXT [ ] ,
' ; ' ,
' utf-8 ' ,
true ,
' {
" datum_tag_monat_jahr_stunde_minute " : " date " ,
" kj " : " kcal " ,
" fett_g " : " fat_g " ,
" kh_g " : " carbs_g " ,
" protein_g " : " protein_g "
} ' ::JSONB,
' {
" date " : {
" type " : " date " ,
" format " : " dd.mm.yyyy HH:MM " ,
" extract " : " date_only "
} ,
" kcal " : {
" type " : " float " ,
2026-04-10 10:42:59 +02:00
" source_unit " : " kj " ,
2026-04-09 21:37:19 +02:00
" decimal_separator " : " , "
} ,
" fat_g " : { " type " : " float " , " decimal_separator " : " , " } ,
" carbs_g " : { " type " : " float " , " decimal_separator " : " , " } ,
" protein_g " : { " type " : " float " , " decimal_separator " : " , " }
} ' ::JSONB
WHERE NOT EXISTS (
SELECT 1 FROM csv_field_mappings f
WHERE f . is_system AND f . profile_id IS NULL
AND f . module = ' nutrition ' AND f . mapping_name = ' FDDB Export (Standard) '
) ;
INSERT INTO csv_field_mappings (
profile_id , is_system , module , mapping_name , description ,
column_signature , delimiter , encoding , has_header ,
field_mappings , type_conversions
)
SELECT
NULL , true , ' nutrition ' , ' MyFitnessPal Export ' ,
' Standard CSV export from MyFitnessPal (English) ' ,
ARRAY [ ' Carbohydrates (g) ' , ' Calories ' , ' Date ' , ' Fat (g) ' , ' Protein (g) ' ] : : TEXT [ ] ,
' , ' , ' utf-8 ' , true ,
' {
" Date " : " date " ,
" Calories " : " kcal " ,
" Fat (g) " : " fat_g " ,
" Carbohydrates (g) " : " carbs_g " ,
" Protein (g) " : " protein_g "
} ' ::JSONB,
' {
" date " : { " type " : " date " , " format " : " yyyy-mm-dd " } ,
" kcal " : { " type " : " float " , " decimal_separator " : " . " } ,
" fat_g " : { " type " : " float " , " decimal_separator " : " . " } ,
" carbs_g " : { " type " : " float " , " decimal_separator " : " . " } ,
" protein_g " : { " type " : " float " , " decimal_separator " : " . " }
} ' ::JSONB
WHERE NOT EXISTS (
SELECT 1 FROM csv_field_mappings f
WHERE f . is_system AND f . profile_id IS NULL
AND f . module = ' nutrition ' AND f . mapping_name = ' MyFitnessPal Export '
) ;
INSERT INTO csv_field_mappings (
profile_id , is_system , module , mapping_name , description ,
column_signature , delimiter , encoding , has_header ,
field_mappings , type_conversions
)
SELECT
NULL , true , ' nutrition ' , ' Cronometer Export ' ,
' Cronometer daily nutrition export (English) ' ,
ARRAY [ ' Day ' , ' Energy (kcal) ' , ' Fat (g) ' , ' Net Carbs (g) ' , ' Protein (g) ' ] : : TEXT [ ] ,
' , ' , ' utf-8 ' , true ,
' {
" Day " : " date " ,
" Energy (kcal) " : " kcal " ,
" Fat (g) " : " fat_g " ,
" Net Carbs (g) " : " carbs_g " ,
" Protein (g) " : " protein_g "
} ' ::JSONB,
' {
" date " : { " type " : " date " , " format " : " yyyy-mm-dd " } ,
" kcal " : { " type " : " float " , " decimal_separator " : " . " } ,
" fat_g " : { " type " : " float " , " decimal_separator " : " . " } ,
" carbs_g " : { " type " : " float " , " decimal_separator " : " . " } ,
" protein_g " : { " type " : " float " , " decimal_separator " : " . " }
} ' ::JSONB
WHERE NOT EXISTS (
SELECT 1 FROM csv_field_mappings f
WHERE f . is_system AND f . profile_id IS NULL
AND f . module = ' nutrition ' AND f . mapping_name = ' Cronometer Export '
) ;
INSERT INTO csv_field_mappings (
profile_id , is_system , module , mapping_name , description ,
column_signature , delimiter , encoding , has_header ,
field_mappings , type_conversions
)
SELECT
NULL , true , ' activity ' , ' Apple Health Workout Export (English) ' ,
' Apple Health CSV-Export für Workouts (English). Automatisches Training-Type-Mapping. ' ,
ARRAY [ ' Active Energy (kcal) ' , ' Distance (km) ' , ' Duration ' , ' End ' , ' Heart Rate Average (bpm) ' , ' Start ' , ' Workout Type ' ] : : TEXT [ ] ,
' , ' , ' utf-8 ' , true ,
' {
" Workout Type " : " activity_type " ,
" Start " : " start_time " ,
" End " : " end_time " ,
" Duration " : " duration_min " ,
" Distance (km) " : " distance_km " ,
" Active Energy (kcal) " : " kcal_active " ,
" Heart Rate Average (bpm) " : " hr_avg "
} ' ::JSONB,
' {
2026-04-10 16:52:11 +02:00
" start_time " : { " type " : " datetime " , " format " : " yyyy-mm-dd HH:MM:SS " , " extract " : " date_and_time " , " flexible " : true } ,
" end_time " : { " type " : " datetime " , " format " : " yyyy-mm-dd HH:MM:SS " , " flexible " : true } ,
2026-04-09 21:37:19 +02:00
" duration_min " : { " type " : " duration " , " format " : " HH:MM:SS " , " target_unit " : " minutes " } ,
" distance_km " : { " type " : " float " , " decimal_separator " : " . " } ,
" kcal_active " : { " type " : " float " , " decimal_separator " : " . " } ,
" hr_avg " : { " type " : " int " }
} ' ::JSONB
WHERE NOT EXISTS (
SELECT 1 FROM csv_field_mappings f
WHERE f . is_system AND f . profile_id IS NULL
AND f . module = ' activity ' AND f . mapping_name = ' Apple Health Workout Export (English) '
) ;
INSERT INTO csv_field_mappings (
profile_id , is_system , module , mapping_name , description ,
column_signature , delimiter , encoding , has_header ,
field_mappings , type_conversions
)
SELECT
NULL , true , ' activity ' , ' Apple Health Workout Export (Deutsch) ' ,
' Apple Health CSV-Export für Workouts (Deutsch). Automatisches Training-Type-Mapping. ' ,
2026-04-11 06:41:23 +02:00
ARRAY [ ' Aktive Energie (kJ) ' , ' Aktive Energie (kcal) ' , ' Dauer ' , ' Durchschnittliche Herzfrequenz (bpm) ' , ' Ende ' , ' Ruheeinträge (kJ) ' , ' Start ' , ' Strecke (km) ' , ' Trainingsart ' ] : : TEXT [ ] ,
2026-04-09 21:37:19 +02:00
' , ' , ' utf-8 ' , true ,
' {
" Trainingsart " : " activity_type " ,
" Start " : " start_time " ,
" Ende " : " end_time " ,
" Dauer " : " duration_min " ,
" Strecke (km) " : " distance_km " ,
" Aktive Energie (kcal) " : " kcal_active " ,
2026-04-11 06:41:23 +02:00
" Aktive Energie (kJ) " : " kcal_active " ,
" Ruheeinträge (kJ) " : " kcal_resting " ,
2026-04-09 21:37:19 +02:00
" Durchschnittliche Herzfrequenz (bpm) " : " hr_avg "
} ' ::JSONB,
' {
2026-04-10 16:52:11 +02:00
" start_time " : { " type " : " datetime " , " format " : " yyyy-mm-dd HH:MM:SS " , " extract " : " date_and_time " , " flexible " : true } ,
" end_time " : { " type " : " datetime " , " format " : " yyyy-mm-dd HH:MM:SS " , " flexible " : true } ,
2026-04-09 21:37:19 +02:00
" duration_min " : { " type " : " duration " , " format " : " HH:MM:SS " , " target_unit " : " minutes " } ,
2026-04-11 06:41:23 +02:00
" distance_km " : { " type " : " float " , " decimal_separator " : " , " , " flexible " : true } ,
" kcal_active " : { " type " : " float " , " decimal_separator " : " . " , " flexible " : true , " source_unit " : " kj " } ,
" kcal_resting " : { " type " : " float " , " decimal_separator " : " . " , " flexible " : true , " source_unit " : " kj " } ,
" hr_avg " : { " type " : " int " , " flexible " : true }
2026-04-09 21:37:19 +02:00
} ' ::JSONB
WHERE NOT EXISTS (
SELECT 1 FROM csv_field_mappings f
WHERE f . is_system AND f . profile_id IS NULL
AND f . module = ' activity ' AND f . mapping_name = ' Apple Health Workout Export (Deutsch) '
) ;
INSERT INTO csv_field_mappings (
profile_id , is_system , module , mapping_name , description ,
column_signature , delimiter , encoding , has_header ,
field_mappings , type_conversions
)
SELECT
NULL , true , ' activity ' , ' Garmin Connect Export ' ,
' Garmin Connect activity CSV export (English) ' ,
ARRAY [ ' Activity Type ' , ' Avg HR ' , ' Calories ' , ' Date ' , ' Distance ' , ' Duration ' , ' Time ' ] : : TEXT [ ] ,
' , ' , ' utf-8 ' , true ,
' {
" Activity Type " : " activity_type " ,
" Date " : " date " ,
" Time " : " start_time " ,
" Duration " : " duration_min " ,
" Distance " : " distance_km " ,
" Calories " : " kcal_active " ,
" Avg HR " : " hr_avg "
} ' ::JSONB,
' {
" date " : { " type " : " date " , " format " : " yyyy-mm-dd " } ,
" start_time " : { " type " : " time " , " format " : " HH:MM:SS " } ,
" duration_min " : { " type " : " duration " , " format " : " HH:MM:SS " , " target_unit " : " minutes " } ,
" distance_km " : { " type " : " float " , " decimal_separator " : " . " } ,
" kcal_active " : { " type " : " float " , " decimal_separator " : " . " } ,
" hr_avg " : { " type " : " int " }
} ' ::JSONB
WHERE NOT EXISTS (
SELECT 1 FROM csv_field_mappings f
WHERE f . is_system AND f . profile_id IS NULL
AND f . module = ' activity ' AND f . mapping_name = ' Garmin Connect Export '
) ;
INSERT INTO csv_field_mappings (
profile_id , is_system , module , mapping_name , description ,
column_signature , delimiter , encoding , has_header ,
field_mappings , type_conversions
)
SELECT
NULL , true , ' blood_pressure ' , ' Omron Export (Deutsch) ' ,
' Omron Blutdruckmessgerät CSV-Export (Deutsch) ' ,
ARRAY [ ' Datum ' , ' Diastolisch (mmHg) ' , ' Puls (bpm) ' , ' Systolisch (mmHg) ' , ' Zeit ' ] : : TEXT [ ] ,
' , ' , ' utf-8 ' , true ,
' {
" Datum " : " measured_date " ,
" Zeit " : " measured_time " ,
" Systolisch (mmHg) " : " systolic " ,
" Diastolisch (mmHg) " : " diastolic " ,
" Puls (bpm) " : " pulse "
} ' ::JSONB,
' {
" measured_date " : { " type " : " date " , " format " : " dd.mm.yyyy " } ,
" measured_time " : { " type " : " time " , " format " : " HH:MM " } ,
" systolic " : { " type " : " int " } ,
" diastolic " : { " type " : " int " } ,
" pulse " : { " type " : " int " }
} ' ::JSONB
WHERE NOT EXISTS (
SELECT 1 FROM csv_field_mappings f
WHERE f . is_system AND f . profile_id IS NULL
AND f . module = ' blood_pressure ' AND f . mapping_name = ' Omron Export (Deutsch) '
) ;
INSERT INTO csv_field_mappings (
profile_id , is_system , module , mapping_name , description ,
column_signature , delimiter , encoding , has_header ,
field_mappings , type_conversions
)
SELECT
NULL , true , ' blood_pressure ' , ' Omron Export (English) ' ,
' Omron blood pressure monitor CSV export (English) ' ,
ARRAY [ ' Date ' , ' Diastolic (mmHg) ' , ' Pulse (bpm) ' , ' Systolic (mmHg) ' , ' Time ' ] : : TEXT [ ] ,
' , ' , ' utf-8 ' , true ,
' {
" Date " : " measured_date " ,
" Time " : " measured_time " ,
" Systolic (mmHg) " : " systolic " ,
" Diastolic (mmHg) " : " diastolic " ,
" Pulse (bpm) " : " pulse "
} ' ::JSONB,
' {
" measured_date " : { " type " : " date " , " format " : " mm/dd/yyyy " } ,
" measured_time " : { " type " : " time " , " format " : " HH:MM " } ,
" systolic " : { " type " : " int " } ,
" diastolic " : { " type " : " int " } ,
" pulse " : { " type " : " int " }
} ' ::JSONB
WHERE NOT EXISTS (
SELECT 1 FROM csv_field_mappings f
WHERE f . is_system AND f . profile_id IS NULL
AND f . module = ' blood_pressure ' AND f . mapping_name = ' Omron Export (English) '
) ;
INSERT INTO csv_field_mappings (
profile_id , is_system , module , mapping_name , description ,
column_signature , delimiter , encoding , has_header ,
field_mappings , type_conversions
)
SELECT
NULL , true , ' weight ' , ' Apple Health Weight Export ' ,
' Apple Health body mass CSV export ' ,
ARRAY [ ' Body Mass (kg) ' , ' Start ' ] : : TEXT [ ] ,
' , ' , ' utf-8 ' , true ,
' {
" Start " : " date " ,
" Body Mass (kg) " : " weight "
} ' ::JSONB,
' {
" date " : { " type " : " datetime " , " format " : " yyyy-mm-dd HH:MM:SS " , " extract " : " date_only " } ,
" weight " : { " type " : " float " , " decimal_separator " : " . " }
} ' ::JSONB
WHERE NOT EXISTS (
SELECT 1 FROM csv_field_mappings f
WHERE f . is_system AND f . profile_id IS NULL
AND f . module = ' weight ' AND f . mapping_name = ' Apple Health Weight Export '
) ;
INSERT INTO csv_field_mappings (
profile_id , is_system , module , mapping_name , description ,
column_signature , delimiter , encoding , has_header ,
field_mappings , type_conversions
)
SELECT
NULL , true , ' weight ' , ' Withings Export ' ,
' Withings smart scale CSV export (weight, body fat, muscle mass) ' ,
ARRAY [ ' Body Fat (%) ' , ' Date ' , ' Muscle Mass (kg) ' , ' Weight (kg) ' ] : : TEXT [ ] ,
' , ' , ' utf-8 ' , true ,
' {
" Date " : " date " ,
" Weight (kg) " : " weight "
} ' ::JSONB,
' {
" date " : { " type " : " date " , " format " : " yyyy-mm-dd " } ,
" weight " : { " type " : " float " , " decimal_separator " : " . " }
} ' ::JSONB
WHERE NOT EXISTS (
SELECT 1 FROM csv_field_mappings f
WHERE f . is_system AND f . profile_id IS NULL
AND f . module = ' weight ' AND f . mapping_name = ' Withings Export '
) ;