"""Halal vlees trends, top gerechten en food concept seeds.""" from __future__ import annotations import re from datetime import datetime, timezone from typing import Any from app.db import fetch_all HALAL_MEAT_KEYWORDS = ( "halal vlees", "halal meat", "halal kip", "halal lam", "halal rund", "halal gehakt", "halal chicken", "halal beef", "halal slacht", "halal certific", "vleesvervanger halal", ) TOP_DISH_KEYWORDS = ( "kant-en-klaar", "kant en klaar", "ready meal", "maaltijd", "gerecht", "curry", "stamppot", "biryani", "tagine", "lasagne", "schotel", "meal prep", "microwave meal", "diepvries maaltijd", ) CONCEPT_SEEDS = [ "Halal {dish} single-serve voor {chain} schappen in regio's met score >{score}", "Premium halal {meat} maaltijdlijn — inspelen op trend: {trend}", "Seizoens {dish} tray (4-portions) voor Plus/Jumbo non-listed partnership pitch", "AH {dish} variant — benchmark tegen {competitor} koers momentum ({pct}%)", "Halal-gap fill: {dish} + {meat} combo voor filialen zonder halal schap", ] def _match_keywords(text: str, keywords: tuple[str, ...]) -> bool: blob = (text or "").lower() return any(k in blob for k in keywords) def fetch_halal_meat_trends(limit: int = 15) -> list[dict[str, Any]]: rows = fetch_all( """SELECT i.title, i.link, i.summary, i.published_at, f.name AS feed_name, f.url AS feed_url FROM rss_items i JOIN rss_feeds f ON f.id = i.feed_id AND f.is_active = TRUE ORDER BY i.published_at DESC NULLS LAST LIMIT 200""" ) out = [] for r in rows: title = r.get("title") or "" summary = r.get("summary") or "" if not _match_keywords(f"{title} {summary}", HALAL_MEAT_KEYWORDS): continue out.append({ "title": title, "link": r.get("link"), "summary": (summary or "")[:280], "feed_name": r.get("feed_name"), "feed_url": r.get("feed_url"), "published_at": r.get("published_at").isoformat() if r.get("published_at") else None, "category": "halal_vlees", "source_url": r.get("link"), }) if len(out) >= limit: break return out def fetch_top_dishes(limit: int = 12) -> list[dict[str, Any]]: rows = fetch_all( """SELECT i.title, i.link, i.summary, i.published_at, f.name AS feed_name, f.url AS feed_url FROM rss_items i JOIN rss_feeds f ON f.id = i.feed_id AND f.is_active = TRUE ORDER BY i.published_at DESC NULLS LAST LIMIT 250""" ) scored: dict[str, dict[str, Any]] = {} for r in rows: title = (r.get("title") or "").lower() summary = (r.get("summary") or "").lower() blob = f"{title} {summary}" if not _match_keywords(blob, TOP_DISH_KEYWORDS): continue for kw in TOP_DISH_KEYWORDS: if kw in blob: key = kw.strip() if key not in scored: scored[key] = { "dish_keyword": key, "mentions": 0, "latest_title": r.get("title"), "latest_link": r.get("link"), "feed_name": r.get("feed_name"), "source_url": r.get("link"), } scored[key]["mentions"] += 1 break items = sorted(scored.values(), key=lambda x: x["mentions"], reverse=True)[:limit] return items def fetch_market_trend_rows(limit: int = 8) -> list[dict[str, Any]]: rows = fetch_all( """SELECT trend_name, description, opportunity_score, source, category, updated_at FROM market_trends ORDER BY updated_at DESC NULLS LAST LIMIT %s""", (limit,), ) out = [] for r in rows: out.append({ "trend_name": r.get("trend_name"), "description": r.get("description"), "opportunity_score": float(r.get("opportunity_score") or 0), "source": r.get("source") or "Foodlinkk trends", "source_url": "/retail", "category": r.get("category") or "markt", }) return out def generate_concepts( dishes: list[dict[str, Any]] | None = None, halal_items: list[dict[str, Any]] | None = None, market_best: dict[str, Any] | None = None, limit: int = 6, ) -> list[dict[str, Any]]: dishes = dishes or fetch_top_dishes(5) halal_items = halal_items or fetch_halal_meat_trends(5) best_chain = (market_best or {}).get("chains", ["Albert Heijn"])[0] pct = (market_best or {}).get("change_pct", 0) competitor = (market_best or {}).get("name", "Ahold Delhaize") concepts = [] for i, tmpl in enumerate(CONCEPT_SEEDS[:limit]): dish = dishes[i % len(dishes)]["dish_keyword"] if dishes else "kant-en-klaar maaltijd" meat = "halal kip" if halal_items else "halal vlees" trend = halal_items[i % len(halal_items)]["title"][:60] if halal_items else "groei halal convenience" text = tmpl.format( dish=dish, meat=meat, chain=best_chain, score=75, trend=trend, competitor=competitor, pct=pct, ) concepts.append({ "id": i + 1, "concept": text, "based_on": { "dish": dish, "halal_trend": trend, "market_signal": f"{competitor} {pct:+.1f}%" if market_best else "retail DB", }, "source_urls": [ u for u in [ dishes[i % len(dishes)].get("source_url") if dishes else None, halal_items[i % len(halal_items)].get("source_url") if halal_items else None, (market_best or {}).get("source_url"), ] if u ], }) return concepts def food_trends_dashboard() -> dict[str, Any]: halal = fetch_halal_meat_trends() dishes = fetch_top_dishes() trends = fetch_market_trend_rows() return { "halal_meat_trends": halal, "top_dishes": dishes, "market_trends": trends, "updated_at": datetime.now(timezone.utc).isoformat(), }