Gegenstands-Verwaltung (Non-Food) neben Lebensmitteln
Die Kategorie bestimmt die Verwaltungsart (food/object). Gegenstaende: Menge je Lagerort statt Chargen/MHD, Umlagern (ohne Grund) und Entfernen mit Pflicht-Grund samt Entnahme-Statistik. Beliebig viele eigene Felder je Kategorie (vererbt an Unterkategorien), "gekauft bei" ueber eine verwaltbare Shop-Liste und ein Produktlink. Barcode-Lookup zusaetzlich ueber Open Products Facts. Der Lebensmittel-Teil (Chargen/MHD/FEFO) bleibt unveraendert. Umgesetzt in Backend (FastAPI, +Tests gruen), Web (React) und iOS (SwiftUI). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -42,15 +42,16 @@ def parse_quantity(quantity: str | None) -> tuple[str, float | None]:
|
||||
return base_unit, (package_size if package_size > 0 else None)
|
||||
|
||||
|
||||
def lookup_barcode(barcode: str) -> dict | None:
|
||||
"""Fragt Open Food Facts nach einem Barcode.
|
||||
def _lookup_at(barcode: str, base_url: str, source: str) -> dict | None:
|
||||
"""Fragt eine Open-Facts-Instanz (OFF oder Open Products Facts) nach einem Barcode.
|
||||
|
||||
Gibt ein vorbefülltes Produkt-Dict zurück oder None, wenn nicht gefunden.
|
||||
Beide Dienste teilen sich dieselbe API-Struktur; es unterscheidet sich nur die
|
||||
Basis-URL. Gibt ein vorbefülltes Produkt-Dict zurück oder None.
|
||||
"""
|
||||
# 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"
|
||||
url = f"{base_url}/api/v0/product/{barcode}.json"
|
||||
try:
|
||||
resp = httpx.get(
|
||||
url,
|
||||
@@ -96,6 +97,26 @@ def lookup_barcode(barcode: str) -> dict | None:
|
||||
"quantity_text": product.get("quantity"),
|
||||
"category_suggestion": categories.split(",")[0].strip() if categories else None,
|
||||
"category_tags": category_tags,
|
||||
"source": "off",
|
||||
"source": source,
|
||||
"off_raw": json.dumps(product)[:20000],
|
||||
}
|
||||
|
||||
|
||||
def lookup_barcode(barcode: str, prefer: str | None = None) -> dict | None:
|
||||
"""Sucht einen Barcode in den offenen Datenbanken.
|
||||
|
||||
Beim Scannen steht die Kategorie noch nicht fest, deshalb werden beide Quellen
|
||||
der Reihe nach befragt: Lebensmittel (Open Food Facts) und allgemeine Produkte
|
||||
(Open Products Facts). ``prefer="object"`` stellt die allgemeine Produkt-DB nach
|
||||
vorn (z.B. beim erneuten Abgleich eines Gegenstands), sonst gewinnt Essen.
|
||||
"""
|
||||
off = (settings.off_base_url, "off")
|
||||
opf = (settings.opf_base_url, "opf")
|
||||
reihenfolge = [opf, off] if prefer == "object" else [off, opf]
|
||||
for base_url, source in reihenfolge:
|
||||
if not base_url:
|
||||
continue
|
||||
treffer = _lookup_at(barcode, base_url, source)
|
||||
if treffer is not None:
|
||||
return treffer
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user