From bb6eefc837636c4e86ff32e33cc0e6044bbf57aa Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 10 Apr 2026 10:42:59 +0200 Subject: [PATCH] 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. --- .../issue-21-seed-migration-example.sql | 4 +- .gitea/workflows/test.yml | 91 ++++++++++++------- .../043_csv_parser_seed_templates.sql | 4 +- ...csv_fddb_kcal_type_conversions_cleanup.sql | 19 ++++ .../src/pages/AdminCsvTemplateEditorPage.jsx | 12 ++- 5 files changed, 91 insertions(+), 39 deletions(-) create mode 100644 backend/migrations/046_csv_fddb_kcal_type_conversions_cleanup.sql diff --git a/.claude/docs/working/issue-21-seed-migration-example.sql b/.claude/docs/working/issue-21-seed-migration-example.sql index efb9362..0bd6220 100644 --- a/.claude/docs/working/issue-21-seed-migration-example.sql +++ b/.claude/docs/working/issue-21-seed-migration-example.sql @@ -36,9 +36,7 @@ INSERT INTO csv_field_mappings ( }, "kcal": { "type": "float", - "source_unit": "kJ", - "target_unit": "kcal", - "conversion_factor": 0.239, + "source_unit": "kj", "decimal_separator": "," }, "fat_g": { diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 4374563..718319f 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -8,54 +8,83 @@ on: types: [completed] jobs: - pytest-backend-csv: + pytest-backend: if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - # Debian 12 ARM64 runner hat fuer 3.12 oft kein setup-python Build. - # 3.11 ist dort breit verfuegbar und fuer unsere Tests ausreichend. - python-version: "3.11" - - name: Install dependencies + - name: Run backend pytest suite in deployed container run: | - pip install -r backend/requirements.txt -r backend/requirements-dev.txt - - name: Pytest — CSV-Import & Parser (Smoke) - run: | - cd backend - python -m pytest tests/test_csv_parser_core.py tests/test_csv_import_executor.py tests/test_mapping_suggest.py -q --tb=short + EVENT_NAME="${{ github.event_name }}" + REF_NAME="${{ github.ref_name }}" + RUN_WORKFLOW="${{ github.event.workflow_run.name }}" + APP_DIR="/home/lars/docker/bodytrack" + COMPOSE_FILE="docker-compose.yml" + + if [ "$EVENT_NAME" = "workflow_run" ]; then + if [ "$RUN_WORKFLOW" = "Deploy Development" ]; then + APP_DIR="/home/lars/docker/bodytrack-dev" + COMPOSE_FILE="docker-compose.dev-env.yml" + fi + elif [ "$REF_NAME" = "develop" ]; then + APP_DIR="/home/lars/docker/bodytrack-dev" + COMPOSE_FILE="docker-compose.dev-env.yml" + fi + + cd "$APP_DIR" + docker compose -f "$COMPOSE_FILE" exec -T backend sh -lc " + pip install -r /app/requirements-dev.txt && + cd /app && + python -m pytest \ + tests/test_csv_parser_core.py \ + tests/test_csv_import_executor.py \ + tests/test_mapping_suggest.py \ + tests/test_placeholder_metadata.py \ + tests/test_placeholder_metadata_v2.py \ + -q --tb=short + " lint-backend: if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - name: Check backend syntax + - name: Check backend syntax on deployed app run: | - python -m py_compile backend/main.py + EVENT_NAME="${{ github.event_name }}" + REF_NAME="${{ github.ref_name }}" + RUN_WORKFLOW="${{ github.event.workflow_run.name }}" + APP_DIR="/home/lars/docker/bodytrack" + + if [ "$EVENT_NAME" = "workflow_run" ]; then + if [ "$RUN_WORKFLOW" = "Deploy Development" ]; then + APP_DIR="/home/lars/docker/bodytrack-dev" + fi + elif [ "$REF_NAME" = "develop" ]; then + APP_DIR="/home/lars/docker/bodytrack-dev" + fi + + python3 -m py_compile "$APP_DIR/backend/main.py" echo "✓ Backend syntax OK" build-frontend: if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: "20" - - name: Build frontend + - name: Build frontend on deployed app run: | - cd frontend + EVENT_NAME="${{ github.event_name }}" + REF_NAME="${{ github.ref_name }}" + RUN_WORKFLOW="${{ github.event.workflow_run.name }}" + APP_DIR="/home/lars/docker/bodytrack" + + if [ "$EVENT_NAME" = "workflow_run" ]; then + if [ "$RUN_WORKFLOW" = "Deploy Development" ]; then + APP_DIR="/home/lars/docker/bodytrack-dev" + fi + elif [ "$REF_NAME" = "develop" ]; then + APP_DIR="/home/lars/docker/bodytrack-dev" + fi + + cd "$APP_DIR/frontend" npm install npm run build echo "✓ Frontend build OK" diff --git a/backend/migrations/043_csv_parser_seed_templates.sql b/backend/migrations/043_csv_parser_seed_templates.sql index 93f8016..74855dd 100644 --- a/backend/migrations/043_csv_parser_seed_templates.sql +++ b/backend/migrations/043_csv_parser_seed_templates.sql @@ -31,9 +31,7 @@ SELECT }, "kcal": { "type": "float", - "source_unit": "kJ", - "target_unit": "kcal", - "conversion_factor": 0.239, + "source_unit": "kj", "decimal_separator": "," }, "fat_g": {"type": "float", "decimal_separator": ","}, diff --git a/backend/migrations/046_csv_fddb_kcal_type_conversions_cleanup.sql b/backend/migrations/046_csv_fddb_kcal_type_conversions_cleanup.sql new file mode 100644 index 0000000..b7393e3 --- /dev/null +++ b/backend/migrations/046_csv_fddb_kcal_type_conversions_cleanup.sql @@ -0,0 +1,19 @@ +-- Migration 046: FDDB-Systemvorlage — kcal ohne legacy conversion_factor/target_unit (Issue #21, source_unit-Registry) +-- Doppelte Umrechnung (kj-Faktor * 0.239) wurde fälschlich gespeichert; target_unit ist nur für duration vorgesehen. + +UPDATE csv_field_mappings +SET type_conversions = jsonb_set( + type_conversions, + '{kcal}', + ((type_conversions->'kcal') - 'conversion_factor' - 'target_unit') + || jsonb_build_object('source_unit', 'kj') +) +WHERE is_system = true + AND profile_id IS NULL + AND module = 'nutrition' + AND mapping_name = 'FDDB Export (Standard)' + AND (type_conversions->'kcal') IS NOT NULL + AND ( + (type_conversions->'kcal') ? 'conversion_factor' + OR (type_conversions->'kcal') ? 'target_unit' + ); diff --git a/frontend/src/pages/AdminCsvTemplateEditorPage.jsx b/frontend/src/pages/AdminCsvTemplateEditorPage.jsx index f04f6aa..5d27a7b 100644 --- a/frontend/src/pages/AdminCsvTemplateEditorPage.jsx +++ b/frontend/src/pages/AdminCsvTemplateEditorPage.jsx @@ -182,7 +182,11 @@ export default function AdminCsvTemplateEditorPage() { const opts = modMeta?.fields?.[fieldKey]?.source_unit_options || [] const canonical = opts.find((o) => o.is_canonical)?.id || opts[0]?.id const su = tc[fieldKey]?.source_unit - if (su && opts.some((o) => o.id === su)) return su + if (su != null && su !== '') { + const sid = String(su).toLowerCase() + const hit = opts.find((o) => o.id === sid) + if (hit) return hit.id + } return canonical || '' } @@ -205,6 +209,8 @@ export default function AdminCsvTemplateEditorPage() { } else { base.source_unit = sourceUnitId } + delete base.target_unit + delete base.conversion_factor tc[fieldKey] = base setTypeConversionsText(JSON.stringify(tc, null, 2)) setError(null) @@ -586,7 +592,9 @@ export default function AdminCsvTemplateEditorPage() {
3b. Quelleinheit (optional)

Ziel-Einheit kommt aus dem Datenmodell (z. B. kcal, g, kg, km). Hier nur angeben, falls die CSV - abweicht (z. B. FDDB kJ → kcal, Makros in kg → g, Gewicht in lb → kg). + abweicht (z. B. FDDB kJ → kcal, Makros in kg → g, Gewicht in lb → kg). Beim Ändern hier werden + pro Feld conversion_factor und target_unit entfernt (verhindert + Doppel-Umrechnung); bei Bedarf wieder in Abschnitt 4 ergänzen.

{unitTargets.map(({ field: fkey, options }) => (