Energie und Makros zuerst, restliche Naehrstoffgruppen zuklappbar, damit die Liste bedienbar bleibt. Co-authored-by: Cursor <cursoragent@cursor.com>
6.3 KiB
6.3 KiB
BLS Food Reference – technische Spec
Stand: 2026-09-14 · Migration 062–069
Tabellen
food_attributes— dynamischer Stoff-/Merkmalskatalog (attr_key,data_type,origin)food_catalog— Lebensmittel (bls_codeUNIQUE bei official_bls; manuell ohne BLS-Code)food_attribute_values— typisiertes EAVfood_name_mappings— FDDB-Name →food_id(User / global)nutrition_items— Tagebuchzeilen inkl.logged_atnutrition_daily_nutrients— Tages-Rollup numerischer Attributenutrition_day_marks—fasting|incompletefood_recipes/food_recipe_ingredients— Mitai-Listen/Kombinationen (Rohmischung, kein Kochschwund);nutrition_items.recipe_id. Kein Tabellen-Rename. Gekochte Gerichte später Tandoor +cooked_yield_g, nicht hier.
Layer 1
data_layer/food_mapping.py— Normalisierung, Lookup, Learn, Apply, Deletedata_layer/nutrition_items.py— Ingest, drei Makro-Summen, Policy,resolve_*_attributesdata_layer/food_recipes.py— Listen-Upsert, Link auf Tagebuchzeilen, Listen-Makros (Skala: gegessen_g / Summe Zutaten, sonst 1/Portionen). Diese Skala gilt nur für Roh-Kombinationen, nicht für gekochte Gerichte.bls/recipe_parser.py—produkte-Feld: Split nur vor nächstem\d+ (g|kg|ml|l)(Kommas im Namen bleiben)
Import
BLS: Admin-Upload Components + Daten-XLSX (backend/bls/parser.py). Upsert über bls_code / attr_key, nie Delete+Insert offizieller Zeilen.
FDDB: Items persistieren; nutrition_log nur bei leerem Tag oder laut Policy / Bestätigung.
Router
/api/bls/*— Suche, Attribute-Suche, eigene Foods (attributes+serving_g), eigene Mappings;POST /bls/attributeslegt Extension-Stoff an (Name + Einheit),GET /bls/attributes?keys=EPA,DHAGET /bls/foods/mine— eigenemanual_user-Einträge;GET /bls/foods/shared— zentralemanual_admin;GET /bls/foods/{id}— Editor-Payload inkl.visibilityundeditable(Owner persönlich; Admin auch zentral);PUT /bls/foods/manual/{id}— nur Owner. Zentrale Updates:PUT /admin/bls/foods/manual/{id}(Admin). Suche?scope=globalblendet persönliche Foods aus (Admin-Mappings).- Sichtbarkeit:
data_layer/food_visibility.py. Lookup-Mapping: User-Zeile nur wenn Food sichtbar; globale Mapping-Zeile nurowner_profile_id IS NULL.upsert_food_mappingerzwingt dasselbe. Vorschläge sortieren persönlich → zentral → BLS. GET /admin/bls/foods?kind=manual—manual_user/manual_admin.POST /admin/bls/foods/manual— zentrale Foods.PUT /admin/bls/foods/manual/{id}— zentral und persönlich (attributes+serving_g).GET /admin/bls/foods/{id}?editor=true— Editor-Payload. Migration 069: Owner-Check (persönlich hat Owner, BLS/Admin nicht).- Migration 065 — Extension-Keys EPA/DHA/DPA/ALA/OMEGA3/OMEGA6 falls BLS sie nicht unter diesem Key hat
/api/admin/bls/*— Import, Katalog, Attribute.GET /admin/bls/attributesliefertgroup_key/group_label(BLS-Gruppen, sortiert).PUT /admin/bls/attributes/{id}ändert Name/Einheit/Gruppe (nichtattr_key).POST /admin/bls/attributes/fill-labelsfüllt leere Namen und fehlende Gruppen aus BLS-4.0-Glossar (bls/attribute_labels.py). Lebensmittel-Import schreibt fehlende Namen aus Spaltenköpfen. Makro-Rollup:catalog_macros_for_itemliest nur ENERCC/PROT625/FAT/CHO →nutrition_log;rebuild_daily_nutrientssummiert allenum_per_100gnachnutrition_daily_nutrients./api/admin/food-mappings— Admin-CRUD/api/nutrition/*— Items, Unmapped, Bulk-Map, Marken, Konflikt-ResolveGET/POST /api/nutrition/recipes,PUT/DELETE /api/nutrition/recipes/{id},POST …/import-fddb-lists,POST …/{id}/applyPUT /api/admin/food-mappings/{id}— Ziel-Lebensmittel einer bestehenden Zuordnung wechseln- Unmapped = Tagebuchzeilen ohne
food_id/recipe_idplus Listenzutaten ohne Mapping - Frontend: Inline-Vorschläge auf Zuordnen (
food_suggest.py: Collapse-Key ohne Leerzeichen, Index 5 Min. Cache, Fett-%-Zahlen 9,5/10),FoodSearchModalnur noch Zusatzsuche (Abort + Debounce). Eigene Foods: Tab Lebensmittel (MyFoodsPanel+FoodEditDialog). Listen-UI:FoodRecipeDialog(Suche gelernt + Katalog; Katalogpick upsertet Mapping). Offene Zutaten:kind=recipe_ingredient(API-Wert unverändert, UI: Listenzutat). - Tandoor (Modul
tandoor): Migration 067profile_tandoor_settings;GET/PUT /api/tandoor/settings(Token nurtoken_set/token_hint);POST /api/tandoor/test(10/min, kein Redirect-Follow). Client:data_layer/tandoor_client.py. Import undcooked_yield_gspäter. - Migration 066 —
food_recipe_ingredients.quantity_amount+source_unit;quantity_g= Umrechnung (resolve_quantityinfood_mapping.py).GET /bls/units= Einheitenkatalog (EL/TL/Prise/Stück, default_g). GET /nutrition/unmapped?since_days=28— nur Namen mitlast_dateim Fenster;count_onlyliefert{count, total, since_days};meta=trueliefert{items, count, total, since_days}. Listenzutaten-Datum: letzterecipe_id-Nutzung oderfood_recipes.updated_at/created_at— kein Join über den Listennamen auf alle Tagebuchzeilen. Migration 068: Partial-Index unmapped-open +(profile_id, recipe_id, date).GET /nutrition/recipes?brief=true— Listen ohne Zutaten, nuringredient_count.GET /nutrition/recipes/{id}lädt eine Liste. FDDB-Listenimport antwortet nach Upsert;rebuild_daily_nutrientsim Hintergrund (rebuild_pending).- Listen-Verknüpfung (
apply/link_recipes_to_items) matcht übernames_match_list(Mengen-Prefix in gespeichertemsource_name_normalizedzählt). Unmapped-Zeilen bekommenmatching_recipe_id+matching_recipe_name. Frontend: Tab Ernährung → Listen. GET /nutrition/ingredient-names— Distinct-Zutatennamen aller Listen inkl. Mapping-Status. Listendialog mappt manuell (upsertMyFoodMapping) an der Zeile.- Mapping-Schreiben und Nährwert-Rebuild sind getrennte Transaktionen; Rebuild läuft nach der API-Antwort im Hintergrund (UI bleibt bedienbar)
food_name_mappings.grams_per_unit/source_unit(Migration 064)GET/POST /api/nutrition/food-knowledge— portable JSON (mitai-food-knowledgev1): manuelle Foods, Mappings (überbls_code/ Name, keine UUIDs), Listen. Import löst Katalog auf dem Zielsystem auf (BLS muss dort importiert sein).