Filter meal suggestions by simple flavor profiles

This commit is contained in:
2026-04-13 18:47:39 +02:00
parent f85ec81851
commit 5a1c1d5c41
5 changed files with 193 additions and 35 deletions
+73
View File
@@ -39,6 +39,39 @@ def normalize_name_for_profile(name: str | None) -> str:
return (name or "").strip().lower()
def infer_food_flavor_profile(
name: str | None,
category: str | None,
base_type: str | None = None,
suggestion_role: str | None = None,
) -> str:
normalized_name = normalize_name_for_profile(name)
normalized_category = (category or "").strip().lower()
normalized_base_type = (base_type or "").strip().lower()
normalized_role = (suggestion_role or "").strip().lower()
if any(token in normalized_name for token in ("proteinpulver", "eiweißpulver", "whey", "clear whey")):
return "neutral"
if any(token in normalized_name for token in ("schoko", "choco", "müsli", "granola", "cornflakes", "fruchtjoghurt", "vanillejoghurt", "pudding")):
return "sweet"
if any(token in normalized_name for token in ("banane", "apfel", "birne", "beeren", "himbeer", "erdbeer", "heidelbeer", "mango", "kiwi", "trauben")):
return "sweet"
if any(token in normalized_name for token in ("räucher", "tofu", "tempeh", "hack", "salami", "wurst", "thunfisch", "lachs", "fisch", "huhn", "hähn", "rind", "schwein", "aufstrich", "pesto", "humus", "hummus", "reisgericht", "chili", "curry")):
return "savory"
if any(token in normalized_name for token in ("naturjoghurt", "joghurt natur", "quark", "skyr", "haferflocken", "gurke", "karotte", "karotten", "kartoffel", "kartoffeln", "reis", "nudeln", "brot", "brötchen")):
return "neutral"
if "obst" in normalized_category or normalized_base_type == "fruit":
return "sweet"
if any(token in normalized_category for token in ("eiweiß", "protein")) or normalized_base_type == "protein":
return "savory"
if any(token in normalized_category for token in ("gemüse",)) or normalized_base_type in {"veg", "carb", "dairy", "nuts", "seeds"}:
return "neutral"
if normalized_role in {"topping", "cooking"}:
return "neutral"
return "neutral"
def infer_food_profile(name: str | None, category: str | None, energy_density: str | None) -> dict[str, object]:
normalized_name = normalize_name_for_profile(name)
normalized_category = (category or "").strip().lower()
@@ -275,6 +308,42 @@ def migrate_item_profiles(database: sqlite3.Connection) -> None:
)
def migrate_food_flavor_profiles(database: sqlite3.Connection) -> None:
if get_meta(database, "food_flavor_profiles_migrated") == "1":
return
rows = database.execute(
"""
SELECT id, name, category, base_type, suggestion_role, flavor_profile
FROM items
WHERE kind = 'food'
ORDER BY id
"""
).fetchall()
for row in rows:
current_flavor = (row["flavor_profile"] or "").strip().lower()
if current_flavor in {"sweet", "savory"}:
continue
database.execute(
"""
UPDATE items
SET flavor_profile = ?
WHERE id = ?
""",
(
infer_food_flavor_profile(
row["name"],
row["category"],
row["base_type"],
row["suggestion_role"],
),
int(row["id"]),
),
)
set_meta(database, "food_flavor_profiles_migrated", "1")
def get_db() -> sqlite3.Connection:
if "db" not in g:
g.db = sqlite3.connect(
@@ -392,6 +461,7 @@ def bootstrap_legacy_schema(database: sqlite3.Connection) -> None:
add_column_if_missing(database, "items", "target_user_id INTEGER")
add_column_if_missing(database, "items", "visibility TEXT NOT NULL DEFAULT 'shared'")
add_column_if_missing(database, "items", "base_type TEXT NOT NULL DEFAULT 'neutral'")
add_column_if_missing(database, "items", "flavor_profile TEXT NOT NULL DEFAULT 'neutral'")
add_column_if_missing(database, "items", "suggestion_role TEXT NOT NULL DEFAULT 'base'")
add_column_if_missing(database, "items", "suggestion_priority TEXT NOT NULL DEFAULT 'normal'")
add_column_if_missing(database, "items", "can_be_meal_core INTEGER NOT NULL DEFAULT 0")
@@ -645,6 +715,7 @@ def ensure_schema_upgrades(database: sqlite3.Connection) -> None:
add_column_if_missing(database, table_name, "visibility TEXT NOT NULL DEFAULT 'shared'")
add_column_if_missing(database, "items", "target_user_id INTEGER")
add_column_if_missing(database, "items", "base_type TEXT NOT NULL DEFAULT 'neutral'")
add_column_if_missing(database, "items", "flavor_profile TEXT NOT NULL DEFAULT 'neutral'")
add_column_if_missing(database, "items", "suggestion_role TEXT NOT NULL DEFAULT 'base'")
add_column_if_missing(database, "items", "suggestion_priority TEXT NOT NULL DEFAULT 'normal'")
add_column_if_missing(database, "items", "can_be_meal_core INTEGER NOT NULL DEFAULT 0")
@@ -699,6 +770,7 @@ def ensure_schema_upgrades(database: sqlite3.Connection) -> None:
sync_default_categories(database)
migrate_item_profiles(database)
migrate_food_flavor_profiles(database)
database.execute(
"""
INSERT OR IGNORE INTO user_settings (user_id)
@@ -707,6 +779,7 @@ def ensure_schema_upgrades(database: sqlite3.Connection) -> None:
)
database.execute("UPDATE items SET energy_density = 'neutral' WHERE energy_density IS NULL OR energy_density = ''")
database.execute("UPDATE items SET base_type = 'neutral' WHERE base_type IS NULL OR base_type = ''")
database.execute("UPDATE items SET flavor_profile = 'neutral' WHERE flavor_profile IS NULL OR flavor_profile = ''")
database.execute("UPDATE items SET suggestion_role = 'base' WHERE suggestion_role IS NULL OR suggestion_role = ''")
database.execute("UPDATE items SET suggestion_priority = 'normal' WHERE suggestion_priority IS NULL OR suggestion_priority = ''")
database.execute("UPDATE items SET can_be_meal_core = 0 WHERE can_be_meal_core IS NULL")