#!/usr/bin/env python3 """Generate the Cucina Food static merksite from harvested live content.""" from __future__ import annotations import html as htmlmod import json import re import shutil import urllib.request from pathlib import Path ROOT = Path(__file__).resolve().parent CDN = "https://www-static.cucinafood.nl/wp-content/uploads" LOGO = f"{CDN}/2025/02/Group-1000002463-1.png" UA = {"User-Agent": "Mozilla/5.0 Cucina redesign"} LINE_META = { "to-cook": { "name": "To Cook", "kicker": "Halal maaltijdpakketten", "hero": f"{CDN}/2025/02/Pasta-Pesta-Red-chili-kip.webp", "intro": "Kook de kleurrijke wereldkeuken met alles-in-1 halal maaltijdpakketten. Verse groenten, een receptkaart, binnen ongeveer 30 minuten op tafel.", "blurb": "Alles-in-1 · ±30 min · receptkaart in de doos", "points": [ "Elke maaltijd bevat verse groenten", "Alles in één pakket, geen extra boodschap", "Duurzaam verpakt en recyclebaar", "Binnen 30 minuten op tafel", ], }, "to-heat": { "name": "To Heat", "kicker": "Ovenschotels & kant-en-klare maaltijden", "hero": f"{CDN}/2025/10/cucinaa-foods.webp", "intro": "Geen voorbereiding: opwarmen en eten. Ovenschotels en maaltijden uit de halal wereldkeuken, in ovenbestendige schalen.", "blurb": "Opwarmen · klaar om te eten", "points": [ "Smaakvolle gerechten uit de halal wereldkeuken", "De marinades en kruiden zitten erin", "Ovenbestendige ready-to-eat schalen", "Verwarm. Geniet. Klaar.", ], }, "to-go": { "name": "To Go", "kicker": "Halal maaltijdsalades", "hero": f"{CDN}/2025/02/Cutting-Borad-and-Tomato.webp", "intro": "Smaakvolle halal maaltijdsalades voor onderweg of als lunch. Verrassende combinaties, in een TO GO-verpakking.", "blurb": "Lunch · onderweg · kant-en-klaar", "points": [ "Verpakt in handige TO GO-verpakkingen", "Snel en vullend, zonder gedoe", "Wereldse smaakcombinaties in één bak", ], }, "halal-meat": { "name": "Halal Meat", "kicker": "Premium kwaliteit halal vlees", "hero": f"{CDN}/2025/10/halal_meat_chicken.webp", "intro": "Halal rund, lam, kalf en kip van Hollandse kwaliteit. Gewoon in het schap van je supermarkt, gecertificeerd volgens strikte halalnormen.", "blurb": "Halal gecertificeerd · in de supermarkt", "points": [ "Premium kwaliteit halal vlees", "Verkrijgbaar bij je lokale supermarkt", "Halal gecertificeerd volgens strikte normen", ], }, } RECIPE_FOR = { "rode pasta pesto": "rode-pasta-pesto-met-gehaktballetjes", "sticky tempeh": "sticky-tempeh-met-rijst-en-gewokte-groenten", "enchilada’s": "enchilada-met-bonen-en-mexicaanse-yoghurtdip", "enchiladas": "enchilada-met-bonen-en-mexicaanse-yoghurtdip", "gele kokoscurry": "gele-kokoscurry-met-kip", "pad thai": "pad-thai-met-limoenwortel-en-pindas", "gevulde puntpaprika": "gevulde-puntpaprika-met-bulgur-gehakt-en-echte-feta", "penne": "penne-met-kipburgertjes-en-citroen-ricotta", "köfte": "kipkofte-met-couscous-en-4-soorten-groenten", "kofte": "kipkofte-met-couscous-en-4-soorten-groenten", "kip teriyaki": "kip-teriyaki-met-gewokte-broccoli", "kruidige arabische hutspot": "kruidige-arabische-hutspot-met-gebakken-ui-merguez-worstjes", "risotto": "risotto-met-gemarineerde-kip-en-kastanjechampignons", "gevulde zoete aardappel": "gevulde-zoete-aardappel-met-geroosterde-kikkererwten-oosterse", } def get(url: str) -> str: req = urllib.request.Request(url, headers=UA) with urllib.request.urlopen(req, timeout=30) as r: return r.read().decode("utf-8", "replace") def strip_tags(s: str) -> str: return re.sub(r"<[^>]+>", "", htmlmod.unescape(s)).strip() def textify(html: str) -> str: clean = re.sub(r"", " ", html, flags=re.I) clean = re.sub(r"", " ", clean, flags=re.I) clean = re.sub(r"", "\n", clean, flags=re.I) clean = re.sub(r"", "\n", clean, flags=re.I) t = htmlmod.unescape(re.sub(r"<[^>]+>", " ", clean)) t = re.sub(r"[ \t]+", " ", t) return re.sub(r"\n{3,}", "\n\n", t) def grab(block: str, start: str, ends: list[str], require_colon: bool = True) -> str: col = r"\s*:" if require_colon else r"\s*:?" i = re.search(rf"(?m)(? list[tuple[str, str, str]]: html = get(url) cut = re.split(r"VOLG ONS OP FACEBOOK", html, flags=re.I)[0] groups: list[tuple[str, str, str]] = [] parts = re.split(r"]*>", cut, flags=re.I) skip_h2 = ("volg", "verkrijgbaar", "geinteresseerd", "cucina") for part in parts[1:]: hm = re.match(r"(.*?)", part, re.I | re.S) if not hm: continue group = strip_tags(hm.group(1)) if not group or any(s in group.lower() for s in skip_h2): continue body = part[hm.end() :] h3s = [strip_tags(x) for x in re.findall(r"]*>(.*?)", body, flags=re.I | re.S)] h3s = [x for x in h3s if x] imgs = [i.split("?")[0] for i in re.findall(r']+src="([^"]+)"', body)] skip = ("Group-1000002463", "image-6.png", "image-7.png", "image-8.png", "Group-1000002471", "Ellipse", "Vector-1", "fi_") imgs = [i for i in imgs if "uploads" in i and not any(s in i for s in skip)] uniq = list(dict.fromkeys(imgs)) n = min(len(h3s), len(uniq)) for name, img in zip(h3s[:n], uniq[:n]): groups.append((group, name, img)) if not groups: h3s = [strip_tags(x) for x in re.findall(r"]*>(.*?)", cut, flags=re.I | re.S)] h3s = [x for x in h3s if x] imgs = [i.split("?")[0] for i in re.findall(r']+src="([^"]+)"', cut)] skip = ("Group-1000002463", "image-6.png", "image-7.png", "image-8.png", "Group-1000002471", "Ellipse", "Vector-1", "fi_") imgs = [i for i in imgs if "uploads" in i and not any(s in i for s in skip)] uniq = list(dict.fromkeys(imgs)) for name, img in zip(h3s, uniq): groups.append(("", name, img)) return groups def recipe_for(name: str) -> str | None: key = name.lower().replace("’", "'").strip() key = key.replace("'", "") for k, slug in RECIPE_FOR.items(): kk = k.replace("'", "") if kk in key or key in kk: return slug return None def esc(s: str) -> str: return htmlmod.escape(s or "") def nav(active: str) -> str: items = [ ("/to-cook/", "to-cook", "To Cook"), ("/to-heat/", "to-heat", "To Heat"), ("/to-go/", "to-go", "To Go"), ("/halal-meat/", "halal-meat", "Meat"), ("/recepten/", "recepten", "Recepten"), ("/halal/", "halal", "Halal"), ("/over-ons/", "over-ons", "Over"), ] links = [] for href, key, label in items: cur = ' aria-current="page"' if active == key else "" links.append(f'{label}') return "\n ".join(links) def page(title: str, active: str, main: str, desc: str = "") -> str: d = desc or title return f""" {esc(title)}
{main}
""" def tile(name: str, img: str, sub: str, href: str | None = None, product: dict | None = None) -> str: inner = f'{esc(name)}{esc(name)}{esc(sub)}' if href: return f'{inner}' if product: payload = esc(json.dumps(product, ensure_ascii=False)) return f'' return f'
{inner}
' def drawer() -> str: return """
""" def finder_block(compact: bool = False) -> str: h = "Waar ligt Cucina?" if not compact else "Zoek een winkel" return f"""

{h}

Zoek op plaats of postcode. Geen muur van 500 adressen.

Typ een plaats of postcode.

    """ def write(path: Path, html: str) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(html, encoding="utf-8") def ingredients_list(text: str) -> list[str]: items = [] for ln in text.splitlines(): low = ln.lower() if low.startswith("ingrediënten") or "aantal personen" in low: if items: break continue if low.startswith("zelf toevoegen"): continue items.append(ln) return items def build() -> None: data = ROOT / "data" data.mkdir(exist_ok=True) stores = json.loads(Path("/tmp/cucina-stores.json").read_text()) (data / "stores.json").write_text(json.dumps(stores, ensure_ascii=False), encoding="utf-8") recipes_raw = json.loads(Path("/tmp/cucina-recipes.json").read_text()) # Re-parse sections with word-boundary fix if cook looks like prep recipes = [] for r in recipes_raw: # We already have fields; fix cook if it equals prep html = get("https://www.cucinafood.nl/" + r["slug"] + "/") t = textify(html) r["prep"] = grab(t, "Voorbereiding", [r"(?= 3 else recipes # pick pasta pesto, kokoscurry, teriyaki if present want = [ "rode-pasta-pesto-met-gehaktballetjes", "gele-kokoscurry-met-kip", "kip-teriyaki-met-gewokte-broccoli", ] featured = [rec_by[s] for s in want if s in rec_by] or featured home_tiles = "".join( tile(LINE_META[s]["name"], LINE_META[s]["hero"], LINE_META[s]["kicker"], f"/{s}/") for s in ("to-cook", "to-heat", "to-go", "halal-meat") ) rec_tiles = "".join( tile(r["title"], r["image"], f'{r["time"]} · {r["serves"]}', f'/recepten/{r["slug"]}/') for r in featured ) home = f"""
    Halal gerechten van Cucina
    Halal · supermarkt · vandaag op tafel

    De wereldkeuken, gewoon in het schap.

    Heerlijke halal producten om mee te koken, op te warmen of to go. Cucina is de keuken van ons allemaal.

    Vier manieren om te eten

    Koken, opwarmen, meenemen of puur vlees. Altijd halal, altijd in de supermarkt.

    {home_tiles}

    Vanavond op tafel

    {rec_tiles}

    Alle recepten

    {finder_block()}

    Halal, van bron tot bord

    100% halalOpgericht door moslimondernemers met Marokkaanse roots. Halal is geen label, het is hoe we werken. In lijn met SMIIC-normen.
    BRC & IFSGeproduceerd onder internationale voedselveiligheidsstandaarden, met controle op elke stap.
    Gewoon in de supermarktHalal food hoort in elk schap te liggen — zichtbaar, vertrouwd, zonder concessies.

    Verkrijgbaar bij

    Retailpartner Retailpartner Retailpartner Retailpartner

    Voor retailers

    Wil je Cucina in het schap? Vraag samples aan. Shop je voor vanavond, gebruik de winkelvinder.

    Vraag samples
    """ write(ROOT / "index.html", page( "Cucina Food — halal wereldkeuken in de supermarkt", "home", home, "Halal maaltijdpakketten, ovenschotels, salades en vlees. Vind Cucina in een supermarkt bij jou in de buurt.", )) for slug, pack in products.items(): meta = pack["meta"] blocks = [] for gname, items in pack["groups"]: cards = "".join( tile(p["name"], p["image"], p["blurb"], product=p) for p in items ) title = f"

    {esc(gname)}

    " if gname and gname.lower() not in meta["name"].lower() else "" blocks.append(f'
    {title}
    {cards}
    ') points = "".join(f"
  • {esc(p)}
  • " for p in meta["points"]) main = f"""
    {esc(meta['name'])}
    {esc(meta['kicker'])}

    {esc(meta['name'])}

    {esc(meta['intro'])}

      {points}
    {''.join(blocks)}
    {drawer()} """ write(ROOT / slug / "index.html", page( f"{meta['name']} — Cucina Food", slug, main, meta["intro"], )) rec_cards = "".join( tile(r["title"], r["image"], f'{r["time"]} · {r["serves"]}', f'/recepten/{r["slug"]}/') for r in recipes ) write(ROOT / "recepten" / "index.html", page( "Recepten — Cucina Food", "recepten", f"""

    Home / Recepten

    Recepten

    Duik in de keuken met Cucina. Snelle maaltijden en wereldse smaken, gemaakt om te delen met #CucinaFood.

    {rec_cards}
    """, "Halal recepten bij de Cucina maaltijdpakketten.", )) for r in recipes: ings = "".join(f"
  • {esc(x)}
  • " for x in ingredients_list(r.get("ingredients") or "")) def paras(text: str) -> str: return "".join(f"

    {esc(p)}

    " for p in (text or "").split("\n") if p.strip()) main = f"""
    To Cook

    {esc(r['title'])}

    {esc(r['time'])}{esc(r['serves'])}

    Ingrediënten

      {ings}

    Voorbereiding

    {paras(r.get('prep') or '')}

    Bereiding

    {paras(r.get('cook') or '')}

    Serveren

    {paras(r.get('serve') or '')}

    Naar To Cook

    """ write(ROOT / "recepten" / r["slug"] / "index.html", page(f"{r['title']} — Cucina Food", "recepten", main, r["title"])) write(ROOT / "waar-te-koop" / "index.html", page( "Waar te koop — Cucina Food", "waar-te-koop", f"""

    Home / Waar te koop

    Waar ligt Cucina?

    Cucina ligt in supermarkten in Nederland en België. Zoek op plaats of postcode.

    {finder_block()}

    Verkrijgbaar bij

    Retailpartner Retailpartner Retailpartner Retailpartner
    """, "Vind Cucina Food in een supermarkt bij jou in de buurt.", )) write(ROOT / "over-ons" / "index.html", page( "Over ons — Cucina Food", "over-ons", f"""
    Cucina Food
    Ons verhaal

    Over Cucina

    Opgericht door moslimondernemers, voor de halalconsument — en voor iedereen die van de wereldkeuken houdt.

    Cucina betekent ‘keuken’, en dat is precies wat we willen zijn: de keuken van ons allemaal. Een plek waar smaken uit de wereld samenkomen en waar traditie en vernieuwing elkaar ontmoeten.

    Cucina Food is een halal voedingsbedrijf. Ons team zet zich in om de belangen van de halalconsument te behartigen en hen te voorzien van zowel vertrouwde als innovatieve halal gemaksproducten. We nodigen iedereen uit om mee te genieten van de kleurrijke wereldkeuken.

    Visie

    Halalproducten net zo vanzelfsprekend als elke andere keuze in het schap. Authenticiteit van de wereldkeuken, gecombineerd met gemak, smaak en kwaliteit — passend bij een moderne levensstijl.

    Missie

    We maken halal gemaksproducten toegankelijk en zichtbaar in de supermarkt. Tegelijkertijd bewaken we de halalwaardigheid van bron tot bord, zodat je met een gerust hart kiest bij je vertrouwde winkel.

    We produceren onder strikte voedselveiligheidsvoorschriften, zoals BRC en IFS.

    Lees het halalbeleid

    """, "Cucina Food: halal gemak, opgericht door moslimondernemers.", )) write(ROOT / "halal" / "index.html", page( "Halal beleid — Cucina Food", "halal", """

    Home / Halal beleid

    100% halal

    Halalwaardigheid

    Cucina Food is opgericht en wordt geleid door islamitische ondernemers met Marokkaanse roots. Voor ons is halal geen trend of label, maar een manier van leven die in elke stap van het bedrijf zichtbaar is.

    Ons beleid is helder en streng: elk product is volledig halal. Voor kip, rund, lam en kalf werken we uitsluitend met methoden die voldoen aan erkende halalnormen, in lijn met de standaarden van het internationale instituut SMIIC.

    Certificering

    Onze kwaliteitsafdeling controleert elke stap in het productieproces en werkt samen met erkende halalcertificeringsinstanties. We produceren in een schone, gecontroleerde omgeving volgens BRC en IFS.

    Dankzij dat toezicht kun je met een gerust hart genieten van elk Cucina-product.

    Vind in de buurt

    """, "Cucina is 100% halal, in lijn met SMIIC, BRC en IFS.", )) write(ROOT / "contact" / "index.html", page( "Contact — Cucina Food", "contact", """

    Home / Contact

    Neem contact op

    Vragen, schapinformatie of samples voor retailers. We reageren via e-mail.

    Cucina Food
    Bos en Lommerplein 280
    1055 RW Amsterdam

    info@cucinafood.nl

    Shop je als consument? Vind Cucina in de buurt.

    Samples voor retailers

    Geïnteresseerd in Cucina in het schap? Stuur je gegevens. We nemen contact op.

    """, "Contact en sample-aanvragen voor Cucina Food.", )) write(ROOT / "privacy" / "index.html", page( "Privacybeleid — Cucina Food", "", """

    Privacybeleid

    Deze statische website slaat zelf geen accounts of bestellingen op. Als je het contact- of sampleformulier gebruikt, opent je e-mailprogramma een bericht naar info@cucinafood.nl met de gegevens die je invult (naam, e-mail, telefoon, bericht, eventueel bedrijfsnaam).

    We gebruiken die gegevens alleen om je vraag te beantwoorden of samples te bespreken. We verkopen ze niet.

    De productfoto’s en het logo komen van de bestaande Cucina-omgeving. Winkellocaties zijn overgenomen van cucinafood.nl zodat je een winkel kunt zoeken; die adressen zijn openbare winkeladressen.

    Vragen: info@cucinafood.nl, Bos en Lommerplein 280, 1055 RW Amsterdam.

    """, )) write(ROOT / "voorwaarden" / "index.html", page( "Voorwaarden — Cucina Food", "", """

    Voorwaarden

    Deze website is een merksite. Je koopt hier niet. Producten zijn verkrijgbaar in de supermarkt; beschikbaarheid verschilt per winkel.

    Teksten en beeld van het merk Cucina Food. Neem contact op voor samples of schapvragen via contact.

    """, )) print("pages written") if __name__ == "__main__": build()