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:
60
backend/tests/test_receipt_match.py
Normal file
60
backend/tests/test_receipt_match.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""Kassenzettel-Abgleich: OCR-Zeilen den ähnlichsten Lebensmitteln zuordnen."""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.models import BaseUnit, Product, Role, User
|
||||
from app.routers.products import match_receipt
|
||||
from app.schemas import MatchRequest
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def user(db):
|
||||
person = User(username="tester", password_hash="x", role=Role.admin)
|
||||
db.add(person)
|
||||
db.commit()
|
||||
db.refresh(person)
|
||||
return person
|
||||
|
||||
|
||||
def test_abgekuerzte_kassenzeile_trifft_lebensmittel(db, user):
|
||||
p = Product(
|
||||
name="Vegane Mühlen-Schnitzel auf Basis von Soja",
|
||||
base_unit=BaseUnit.gram, package_size=180,
|
||||
)
|
||||
db.add(p)
|
||||
db.commit()
|
||||
db.refresh(p)
|
||||
|
||||
res = match_receipt(
|
||||
MatchRequest(lines=["MÜHLEN SCHNITZ", "voelliger unsinn xyz"]),
|
||||
db=db, _=user,
|
||||
)
|
||||
# Abgekürzte Kassenzeile findet den vollen Namen mit hohem Score.
|
||||
assert res[0].text == "MÜHLEN SCHNITZ"
|
||||
assert res[0].candidates
|
||||
assert res[0].candidates[0].product_id == p.id
|
||||
assert res[0].candidates[0].score >= 70
|
||||
# Unsinnszeile bleibt ohne Treffer über dem Schwellwert.
|
||||
assert res[1].candidates == []
|
||||
|
||||
|
||||
def test_nur_lebensmittel_keine_gegenstaende(db, user):
|
||||
# Einzelstück-Flag -> tracking "object" -> darf nicht vorgeschlagen werden.
|
||||
obj = Product(name="Powerbank Anker", base_unit=BaseUnit.piece, individual=True)
|
||||
db.add(obj)
|
||||
db.commit()
|
||||
|
||||
res = match_receipt(MatchRequest(lines=["POWERBANK ANKER"], threshold=10), db=db, _=user)
|
||||
assert res[0].candidates == []
|
||||
|
||||
|
||||
def test_schwellwert_filtert(db, user):
|
||||
p = Product(name="Basmati Reis", base_unit=BaseUnit.gram, package_size=1)
|
||||
db.add(p)
|
||||
db.commit()
|
||||
|
||||
# Nur teilweise passende Zeile: bei sehr hohem Schwellwert kein Treffer.
|
||||
hart = match_receipt(MatchRequest(lines=["reis lose"], threshold=99), db=db, _=user)
|
||||
weich = match_receipt(MatchRequest(lines=["reis lose"], threshold=20), db=db, _=user)
|
||||
assert hart[0].candidates == []
|
||||
assert weich[0].candidates and weich[0].candidates[0].product_id == p.id
|
||||
Reference in New Issue
Block a user