Lebensmittel oder Rezept wählbar; gemappte Zutaten werden übernommen, offene erscheinen in der Liste. Dazu Fettgehalt-Suche, EPA-Stoffe, Rezept-CRUD und Wechsel bestehender Zuordnungen. Co-authored-by: Cursor <cursoragent@cursor.com>
74 lines
2.8 KiB
Python
74 lines
2.8 KiB
Python
from datetime import date
|
|
|
|
from csv_parser.executor import guess_nutrition_item_fields
|
|
from data_layer.food_mapping import (
|
|
filter_unmapped_since,
|
|
merge_unmapped_rows,
|
|
normalize_food_name,
|
|
parse_quantity,
|
|
parse_quantity_g,
|
|
primary_search_query,
|
|
)
|
|
from data_layer.nutrition_items import macros_differ
|
|
|
|
|
|
def test_primary_query_keeps_decimal_comma():
|
|
assert primary_search_query("Joghurt 9,5%") == "Joghurt 9,5%"
|
|
assert primary_search_query("Joghurt, aus Kuhmilch") == "Joghurt"
|
|
|
|
|
|
def test_normalize_strips_leading_quantity():
|
|
assert normalize_food_name("50 g Hähnchen") == "hähnchen"
|
|
assert normalize_food_name(" Vollmilch 3,5% ") == "vollmilch 3.5%"
|
|
assert normalize_food_name("1 ml Olivenöl") == normalize_food_name("2 ml Olivenöl") == "olivenöl"
|
|
|
|
|
|
def test_merge_unmapped_collapses_quantity_variants():
|
|
rows = merge_unmapped_rows([
|
|
{"source_name_raw": "1 ml Olivenöl", "source_name_normalized": "1 ml olivenöl", "count": 3, "kind": "diary"},
|
|
{"source_name_raw": "2 ml Olivenöl", "source_name_normalized": "2 ml olivenöl", "count": 1, "kind": "diary"},
|
|
])
|
|
assert len(rows) == 1
|
|
assert rows[0]["source_name_normalized"] == "olivenöl"
|
|
assert rows[0]["count"] == 4
|
|
assert rows[0]["source_name_raw"].lower() == "olivenöl"
|
|
|
|
|
|
def test_parse_quantity_g():
|
|
assert parse_quantity_g("150 g") == 150.0
|
|
assert parse_quantity_g("150") == 150.0
|
|
assert parse_quantity_g("1 kg") == 1000.0
|
|
assert parse_quantity_g("5 ml") == 5.0
|
|
assert parse_quantity_g("1 Stück") is None
|
|
assert parse_quantity_g("1 Stück", 60) == 60.0
|
|
assert parse_quantity_g("2 EL") == 30.0
|
|
assert parse_quantity("1 Scheibe")["needs_unit_map"] is True
|
|
assert parse_quantity("2 EL")["unit"] == "el"
|
|
|
|
|
|
def test_guess_fddb_bezeichnung_without_template_mapping():
|
|
name, qty = guess_nutrition_item_fields({
|
|
"bezeichnung": "50 g Hähnchen",
|
|
"menge": "50 g",
|
|
"kj": "800",
|
|
})
|
|
assert name == "50 g Hähnchen"
|
|
assert qty == "50 g"
|
|
|
|
|
|
def test_recent_window_drops_old_and_dateless_foods():
|
|
today = date(2026, 9, 12)
|
|
rows = [
|
|
{"source_name_raw": "Haferflocken", "last_date": "2026-09-10", "count": 2},
|
|
{"source_name_raw": "Weißbrot", "last_date": "2026-01-02", "count": 40},
|
|
{"source_name_raw": "Altes Rezept-Salz", "last_date": None, "count": 1},
|
|
]
|
|
recent = filter_unmapped_since(rows, 28, today=today)
|
|
assert [r["source_name_raw"] for r in recent] == ["Haferflocken"]
|
|
assert len(filter_unmapped_since(rows, 0, today=today)) == 3
|
|
|
|
|
|
def test_macros_differ_rounds():
|
|
assert not macros_differ({"kcal": 1.04, "protein_g": 0, "fat_g": 0, "carbs_g": 0}, {"kcal": 1.0, "protein_g": 0, "fat_g": 0, "carbs_g": 0})
|
|
assert macros_differ({"kcal": 10, "protein_g": 0, "fat_g": 0, "carbs_g": 0}, {"kcal": 11, "protein_g": 0, "fat_g": 0, "carbs_g": 0})
|