"""Open Food Facts Lookup-Client.""" from __future__ import annotations import json import httpx from .config import get_settings settings = get_settings() def _guess_base_unit(quantity: str | None) -> str: """Rät die Basiseinheit aus dem OFF-Feld 'quantity' (z.B. '500 g', '1 l').""" if not quantity: return "piece" q = quantity.lower() if "ml" in q or "cl" in q or "l" in q or "liter" in q: return "milliliter" if "kg" in q or " g" in q or "gramm" in q or q.strip().endswith("g"): return "gram" return "piece" def lookup_barcode(barcode: str) -> dict | None: """Fragt Open Food Facts nach einem Barcode. Gibt ein vorbefülltes Produkt-Dict zurück oder None, wenn nicht gefunden. """ # v0-API: liefert bei Treffer status=1 + product, bei Nicht-Treffer status=0 # (HTTP 200). Die v2-API hat kein status-Feld und antwortet mit HTTP 404, # weshalb wir bewusst v0 nutzen. url = f"{settings.off_base_url}/api/v0/product/{barcode}.json" try: resp = httpx.get( url, timeout=settings.off_timeout_seconds, headers={"User-Agent": "Project-Good-Selfhosted/1.0"}, follow_redirects=True, ) except httpx.HTTPError: return None if resp.status_code != 200: return None try: data = resp.json() except ValueError: return None if data.get("status") != 1: return None product = data.get("product", {}) name = ( product.get("product_name_de") or product.get("product_name") or product.get("generic_name") or "" ).strip() if not name: return None categories = product.get("categories") or "" category_tags = product.get("categories_tags") or [] return { "barcode": barcode, "name": name, "brand": (product.get("brands") or "").strip() or None, "image_url": product.get("image_front_url") or product.get("image_url") or None, "base_unit": _guess_base_unit(product.get("quantity")), "quantity_text": product.get("quantity"), "category_suggestion": categories.split(",")[0].strip() if categories else None, "category_tags": category_tags, "source": "off", "off_raw": json.dumps(product)[:20000], }