fix: Beschriftungen unabhaengig von Umlaut und Zitat erkennen
Ohne den Beguenstigten ist ein Eintrag im Eingang wertlos – man sieht einen Betrag und weiss nicht, wofuer. Genau das passierte bei weitergeleiteten Mails: Gmail zitiert den Text mit "> " davor, die Beschriftung stand also nicht mehr am Zeilenanfang, und je nach Kodierung kam "Beguenstigter" statt "Beguenstigter" mit Umlaut an. Beschriftungen werden jetzt normalisiert verglichen (Umlaute verschriftlicht, alles ausser Buchstaben und Ziffern verworfen), und Zitatzeichen fallen vor der Feldsuche weg. Die Suchbegriffe laufen durch dieselbe Normalisierung, damit sie lesbar bleiben statt doppelt gepflegt zu werden. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,10 +30,9 @@ from decimal import Decimal, InvalidOperation
|
||||
|
||||
# Beschriftungen je Feld, längere zuerst (spezifischer schlägt allgemeiner).
|
||||
LABELS_BETRAG = ("betrag", "gesendet", "empfangen", "erhalten", "amount", "summe")
|
||||
LABELS_EMPFAENGER = ("begünstigter", "beguenstigter", "empfänger", "empfaenger",
|
||||
"zahlungsempfänger", "an", "händler", "haendler", "to")
|
||||
LABELS_DATUM = ("ausführungsdatum", "ausfuehrungsdatum", "valutadatum", "buchungsdatum",
|
||||
"datum", "am", "date")
|
||||
LABELS_EMPFAENGER = ("beguenstigter", "zahlungsempfaenger", "empfaenger", "haendler",
|
||||
"beguenstigte", "an", "to")
|
||||
LABELS_DATUM = ("ausfuehrungsdatum", "valutadatum", "buchungsdatum", "datum", "am", "date")
|
||||
LABELS_KONTO = ("konto", "account", "iban")
|
||||
LABELS_STATUS = ("neuer status", "status")
|
||||
|
||||
@@ -100,6 +99,20 @@ def html_zu_text(roh: str) -> str:
|
||||
return "\n".join(z for z in zeilen if z)
|
||||
|
||||
|
||||
def _schluessel(text: str) -> str:
|
||||
"""Beschriftung auf eine vergleichbare Form bringen.
|
||||
|
||||
Umlaute verschriftlichen und alles ausser Buchstaben und Ziffern
|
||||
wegwerfen. „Begünstigter", „Beguenstigter" und „BEGÜNSTIGTER :" ergeben
|
||||
damit denselben Schlüssel – sonst haengt die Erkennung daran, wie das
|
||||
Mailprogramm den Umlaut kodiert hat.
|
||||
"""
|
||||
t = text.strip().lower()
|
||||
for von, nach in (("ä", "ae"), ("ö", "oe"), ("ü", "ue"), ("ß", "ss")):
|
||||
t = t.replace(von, nach)
|
||||
return re.sub(r"[^a-z0-9]", "", t)
|
||||
|
||||
|
||||
def _zahl_zu_cent(roh: str) -> int | None:
|
||||
s = roh.replace("'", "").replace("’", "").replace(" ", "")
|
||||
if "," in s and "." in s:
|
||||
@@ -134,7 +147,9 @@ def _felder(text: str) -> dict[str, list[tuple[str, int, str]]]:
|
||||
``Gesendet`` / ``324 €`` den Inhalt der Folgezeile.
|
||||
"""
|
||||
gefunden: dict[str, list[tuple[str, int, str]]] = {}
|
||||
zeilen = text.splitlines()
|
||||
# Weitergeleitete Mails kommen oft mit „> " zitiert an; die Beschriftung
|
||||
# steht dann nicht mehr am Zeilenanfang.
|
||||
zeilen = [re.sub(r"^[\s>]+", "", z) for z in text.splitlines()]
|
||||
pos = 0
|
||||
for i, zeile in enumerate(zeilen):
|
||||
start = pos
|
||||
@@ -142,12 +157,12 @@ def _felder(text: str) -> dict[str, list[tuple[str, int, str]]]:
|
||||
if ":" in zeile:
|
||||
links, rechts = zeile.split(":", 1)
|
||||
beschriftung = links.strip()
|
||||
schl, wert = beschriftung.lower(), rechts.strip()
|
||||
schl, wert = _schluessel(beschriftung), rechts.strip()
|
||||
if wert:
|
||||
gefunden.setdefault(schl, []).append((wert, start, beschriftung))
|
||||
continue
|
||||
beschriftung = zeile.strip().rstrip(":")
|
||||
schl = beschriftung.lower()
|
||||
schl = _schluessel(beschriftung)
|
||||
if schl and len(schl) <= 30 and i + 1 < len(zeilen):
|
||||
folge = zeilen[i + 1].strip()
|
||||
if folge and ":" not in folge:
|
||||
@@ -156,8 +171,14 @@ def _felder(text: str) -> dict[str, list[tuple[str, int, str]]]:
|
||||
|
||||
|
||||
def _erster_treffer(felder: dict, labels: tuple[str, ...]) -> tuple[str, int, str] | None:
|
||||
"""Wert, Stelle und die Beschriftung in Originalschreibweise."""
|
||||
for label in labels:
|
||||
"""Wert, Stelle und die Beschriftung in Originalschreibweise.
|
||||
|
||||
Die Suchbegriffe laufen durch dieselbe Normalisierung wie die gefundenen
|
||||
Beschriftungen – sonst muesste man sie doppelt pflegen, einmal lesbar und
|
||||
einmal fuer den Vergleich.
|
||||
"""
|
||||
for roh in labels:
|
||||
label = _schluessel(roh)
|
||||
if label in felder:
|
||||
wert, stelle, beschriftung = felder[label][0]
|
||||
return wert, stelle, beschriftung
|
||||
|
||||
Reference in New Issue
Block a user