mitai-jinkendo/backend/migrations/043_csv_parser_seed_templates.sql
Lars bb6eefc837
Some checks failed
Deploy Development / deploy (push) Successful in 49s
Build Test / pytest-backend (push) Failing after 3s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 17s
fix(csv-import): Normalize source unit representation and update CI workflows
- Changed source unit representation from "kJ" to "kj" for consistency across CSV templates and migrations.
- Updated CI workflow to enhance testing conditions, ensuring tests run in the correct environment based on deployment context.
- Improved job steps for backend testing and syntax checking by utilizing deployed application directories, streamlining the CI process.
2026-04-10 10:42:59 +02:00

313 lines
11 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 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",
"source_unit": "kj",
"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,
'{
"start_time": {"type": "datetime", "format": "yyyy-mm-dd HH:MM:SS", "extract": "date_and_time"},
"end_time": {"type": "datetime", "format": "yyyy-mm-dd 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 = '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.',
ARRAY['Aktive Energie (kcal)', 'Dauer', 'Durchschnittliche Herzfrequenz (bpm)', 'Ende', 'Start', 'Strecke (km)', 'Trainingsart']::TEXT[],
',', 'utf-8', true,
'{
"Trainingsart": "activity_type",
"Start": "start_time",
"Ende": "end_time",
"Dauer": "duration_min",
"Strecke (km)": "distance_km",
"Aktive Energie (kcal)": "kcal_active",
"Durchschnittliche Herzfrequenz (bpm)": "hr_avg"
}'::JSONB,
'{
"start_time": {"type": "datetime", "format": "yyyy-mm-dd HH:MM:SS", "extract": "date_and_time"},
"end_time": {"type": "datetime", "format": "yyyy-mm-dd 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 = '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'
);