Backend: Kassenzettel-Abgleich (POST /products/match) + Schwellwert-Einstellung

Neuer Endpunkt ordnet OCR-Zeilen den aehnlichsten LEBENSMITTELN zu (Score 0-100,
difflib + Token-/Praefix-Abgleich fuer abgekuerzte Kassennamen). Gegenstaende
inkl. Verbrauchsgegenstaende bleiben aussen vor. Je Zeile die besten Treffer ueber
dem Schwellwert (Request oder Einstellung receipt_match_threshold, Default 45).
3 Tests, gesamt 182 gruen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-30 18:24:19 +02:00
parent d05727779b
commit 91adee5333
4 changed files with 173 additions and 0 deletions

View File

@@ -10,6 +10,10 @@ from ..schemas import SettingOut
router = APIRouter(prefix="/settings", tags=["settings"])
EXPIRY_WARNING_KEY = "expiry_warning_days"
# Ab wie viel Prozent Übereinstimmung ein Artikel beim Kassenzettel-Scan als
# Treffer vorgeschlagen wird.
RECEIPT_THRESHOLD_KEY = "receipt_match_threshold"
RECEIPT_THRESHOLD_DEFAULT = 45
def get_expiry_warning_days(db: Session) -> int:
@@ -22,6 +26,17 @@ def get_expiry_warning_days(db: Session) -> int:
return get_settings().expiry_warning_days_default
def get_receipt_match_threshold(db: Session) -> int:
"""Schwellwert (0100) für Kassenzettel-Treffer; auf sinnvollen Bereich geklemmt."""
row = db.get(Setting, RECEIPT_THRESHOLD_KEY)
if row is None:
return RECEIPT_THRESHOLD_DEFAULT
try:
return max(0, min(100, int(row.value)))
except ValueError:
return RECEIPT_THRESHOLD_DEFAULT
@router.get("", response_model=list[SettingOut])
def list_settings(
db: Session = Depends(get_db), _: User = Depends(get_current_user)
@@ -29,6 +44,7 @@ def list_settings(
rows = db.query(Setting).all()
known = {r.key: r.value for r in rows}
known.setdefault(EXPIRY_WARNING_KEY, str(get_expiry_warning_days(db)))
known.setdefault(RECEIPT_THRESHOLD_KEY, str(get_receipt_match_threshold(db)))
return [SettingOut(key=k, value=v) for k, v in known.items()]