fix(csv-import): Normalize source unit representation and update CI workflows
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

- 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.
This commit is contained in:
Lars 2026-04-10 10:42:59 +02:00
parent 0d0ab62674
commit bb6eefc837
5 changed files with 91 additions and 39 deletions

View File

@ -36,9 +36,7 @@ INSERT INTO csv_field_mappings (
}, },
"kcal": { "kcal": {
"type": "float", "type": "float",
"source_unit": "kJ", "source_unit": "kj",
"target_unit": "kcal",
"conversion_factor": 0.239,
"decimal_separator": "," "decimal_separator": ","
}, },
"fat_g": { "fat_g": {

View File

@ -8,54 +8,83 @@ on:
types: [completed] types: [completed]
jobs: jobs:
pytest-backend-csv: pytest-backend:
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }} if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Run backend pytest suite in deployed container
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
run: | run: |
pip install -r backend/requirements.txt -r backend/requirements-dev.txt EVENT_NAME="${{ github.event_name }}"
- name: Pytest — CSV-Import & Parser (Smoke) REF_NAME="${{ github.ref_name }}"
run: | RUN_WORKFLOW="${{ github.event.workflow_run.name }}"
cd backend APP_DIR="/home/lars/docker/bodytrack"
python -m pytest tests/test_csv_parser_core.py tests/test_csv_import_executor.py tests/test_mapping_suggest.py -q --tb=short 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: lint-backend:
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }} if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Check backend syntax on deployed app
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Check backend syntax
run: | 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" echo "✓ Backend syntax OK"
build-frontend: build-frontend:
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }} if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Build frontend on deployed app
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Build frontend
run: | 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 install
npm run build npm run build
echo "✓ Frontend build OK" echo "✓ Frontend build OK"

View File

@ -31,9 +31,7 @@ SELECT
}, },
"kcal": { "kcal": {
"type": "float", "type": "float",
"source_unit": "kJ", "source_unit": "kj",
"target_unit": "kcal",
"conversion_factor": 0.239,
"decimal_separator": "," "decimal_separator": ","
}, },
"fat_g": {"type": "float", "decimal_separator": ","}, "fat_g": {"type": "float", "decimal_separator": ","},

View File

@ -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'
);

View File

@ -182,7 +182,11 @@ export default function AdminCsvTemplateEditorPage() {
const opts = modMeta?.fields?.[fieldKey]?.source_unit_options || [] const opts = modMeta?.fields?.[fieldKey]?.source_unit_options || []
const canonical = opts.find((o) => o.is_canonical)?.id || opts[0]?.id const canonical = opts.find((o) => o.is_canonical)?.id || opts[0]?.id
const su = tc[fieldKey]?.source_unit 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 || '' return canonical || ''
} }
@ -205,6 +209,8 @@ export default function AdminCsvTemplateEditorPage() {
} else { } else {
base.source_unit = sourceUnitId base.source_unit = sourceUnitId
} }
delete base.target_unit
delete base.conversion_factor
tc[fieldKey] = base tc[fieldKey] = base
setTypeConversionsText(JSON.stringify(tc, null, 2)) setTypeConversionsText(JSON.stringify(tc, null, 2))
setError(null) setError(null)
@ -586,7 +592,9 @@ export default function AdminCsvTemplateEditorPage() {
<div className="form-label">3b. Quelleinheit (optional)</div> <div className="form-label">3b. Quelleinheit (optional)</div>
<p style={{ fontSize: 13, color: 'var(--text2)', marginTop: 8, lineHeight: 1.55 }}> <p style={{ fontSize: 13, color: 'var(--text2)', marginTop: 8, lineHeight: 1.55 }}>
Ziel-Einheit kommt aus dem Datenmodell (z.B. kcal, g, kg, km). Hier nur angeben, falls die CSV 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 <code>conversion_factor</code> und <code>target_unit</code> entfernt (verhindert
Doppel-Umrechnung); bei Bedarf wieder in Abschnitt 4 ergänzen.
</p> </p>
<div style={{ marginTop: 14, display: 'flex', flexDirection: 'column', gap: 12 }}> <div style={{ marginTop: 14, display: 'flex', flexDirection: 'column', gap: 12 }}>
{unitTargets.map(({ field: fkey, options }) => ( {unitTargets.map(({ field: fkey, options }) => (