2026-04-10 11:56:43 +02:00
|
|
|
"""Tests für CSV-Zeilenaggregation (import_row_processing)."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import datetime as dt
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from csv_parser.import_row_processing import (
|
|
|
|
|
aggregate_mapped_rows,
|
|
|
|
|
resolve_import_row_processing,
|
|
|
|
|
validate_import_row_processing,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_validate_rejects_unknown_aggregate():
|
|
|
|
|
with pytest.raises(ValueError, match="ungültige Operation"):
|
|
|
|
|
validate_import_row_processing(
|
|
|
|
|
"nutrition",
|
|
|
|
|
{"group_by": ["date"], "aggregates": {"kcal": "bogus"}},
|
|
|
|
|
{"Kal": "date", "E": "kcal"},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_validate_group_by_must_be_mapped():
|
|
|
|
|
with pytest.raises(ValueError, match="keiner CSV-Spalte zugeordnet"):
|
|
|
|
|
validate_import_row_processing(
|
|
|
|
|
"nutrition",
|
|
|
|
|
{"group_by": ["date"], "aggregates": {"kcal": "sum"}},
|
|
|
|
|
{"Kal": "kcal"}, # date nicht gemappt
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_aggregate_mapped_rows_sums_same_group():
|
|
|
|
|
d = dt.date(2024, 1, 15)
|
|
|
|
|
rows = [
|
|
|
|
|
{"date": d, "kcal": 500.0, "protein_g": 20},
|
|
|
|
|
{"date": d, "kcal": 300.0, "protein_g": 15},
|
|
|
|
|
]
|
|
|
|
|
spec = {"group_by": ["date"], "aggregates": {"kcal": "sum", "protein_g": "sum"}}
|
|
|
|
|
out = aggregate_mapped_rows(rows, spec)
|
|
|
|
|
assert len(out) == 1
|
|
|
|
|
assert out[0]["kcal"] == 800.0
|
|
|
|
|
assert out[0]["protein_g"] == 35
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_resolve_explicit_overrides_default():
|
|
|
|
|
m = {
|
|
|
|
|
"import_row_processing": {"group_by": ["date"], "aggregates": {"kcal": "mean"}},
|
|
|
|
|
}
|
|
|
|
|
spec = resolve_import_row_processing("nutrition", m)
|
|
|
|
|
assert spec is not None
|
|
|
|
|
assert spec["aggregates"]["kcal"] == "mean"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_resolve_empty_dict_falls_back_to_module_default():
|
|
|
|
|
m: dict = {"import_row_processing": {}}
|
|
|
|
|
spec = resolve_import_row_processing("nutrition", m)
|
|
|
|
|
assert spec is not None
|
|
|
|
|
assert spec["group_by"] == ["date"]
|
|
|
|
|
assert spec["aggregates"]["kcal"] == "sum"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_resolve_none_uses_nutrition_default():
|
|
|
|
|
spec = resolve_import_row_processing("nutrition", {})
|
|
|
|
|
assert spec is not None
|
|
|
|
|
assert "date" in (spec.get("group_by") or [])
|
2026-04-10 15:09:34 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_resolve_weight_default_uses_last():
|
|
|
|
|
spec = resolve_import_row_processing("weight", {})
|
|
|
|
|
assert spec is not None
|
|
|
|
|
assert spec.get("aggregates", {}).get("weight") == "last"
|
|
|
|
|
assert spec.get("aggregates", {}).get("note") == "last"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_validate_weight_import_row_processing_ok():
|
|
|
|
|
validate_import_row_processing(
|
|
|
|
|
"weight",
|
|
|
|
|
{"group_by": ["date"], "aggregates": {"weight": "mean"}},
|
|
|
|
|
{"D": "date", "W": "weight"},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_resolve_vitals_baseline_default_uses_mean():
|
|
|
|
|
spec = resolve_import_row_processing("vitals_baseline", {})
|
|
|
|
|
assert spec is not None
|
|
|
|
|
assert spec.get("aggregates", {}).get("resting_hr") == "mean"
|