From be9385964504883f367a8f5cbd327bd79366c956 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Thu, 11 Jun 2026 12:25:27 +0200 Subject: [PATCH] fix(impressum): Pflichtangaben-Beleg = exakter Treffer statt Textpassage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _match_value gibt genau den gematchten Bereich zurück (nur die E-Mail unter Email, nur die USt-IdNr, nur die Telefonnummer) — nicht mehr ein Fenster/den umgebenden Satz. Behebt die Wiederholung desselben Anfangssatzes bei Texten ohne Zeilenumbrüche (BMW = ein Block). Co-Authored-By: Claude Opus 4.7 --- .../specialist_agents/impressum/agent.py | 22 +++++-------- .../tests/test_impressum_line_of.py | 31 +++++++------------ 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/backend-compliance/compliance/services/specialist_agents/impressum/agent.py b/backend-compliance/compliance/services/specialist_agents/impressum/agent.py index 5d4bfd76..5b692014 100644 --- a/backend-compliance/compliance/services/specialist_agents/impressum/agent.py +++ b/backend-compliance/compliance/services/specialist_agents/impressum/agent.py @@ -78,20 +78,12 @@ def _build_measure(label: str, norm: str) -> str: return msg -def _line_of(text: str, start_pos: int, end_pos: int) -> str: - """Ein enger Ausschnitt um einen Regex-Treffer — der 'gefundene Wert' für die - Pflichtangaben-Tabelle. Bevorzugt die Zeile; bei Texten ohne (genug) - Zeilenumbrüche (z.B. BMW-Impressum als ein Block) ein Fenster um den Treffer, - damit jede MC IHREN Beleg zeigt statt immer denselben Anfangssatz.""" - start = text.rfind("\n", 0, start_pos) + 1 - end = text.find("\n", end_pos) - if end == -1: - end = len(text) - # Zeile zu lang (kein/seltener Umbruch) → enges Fenster zentriert am Treffer. - if end - start > 160: - start = max(start, start_pos - 70) - end = min(end, end_pos + 70) - return " ".join(text[start:end].split())[:160] +def _match_value(text: str, start_pos: int, end_pos: int) -> str: + """Der reine Treffer-Wert (E-Mail, USt-IdNr, Telefonnummer …) — exakt der + gematchte Bereich, NICHT die umgebende Textpassage. Whitespace normalisiert + + gekappt, damit die Pflichtangaben-Tabelle genau das zeigt, was erwartet + wird (nur die Mail unter Email, nur die Anschrift unter Anschrift).""" + return " ".join(text[start_pos:end_pos].split())[:120] def _coverage(mc, status: str, reason: str, found: str = "") -> McCoverage: @@ -163,7 +155,7 @@ class ImpressumAgent(BaseSpecialistAgent): if matched is not None: coverage.append(_coverage( mc, "ok", "Pattern-Treffer", - found=_line_of(text, matched.start(), matched.end()), + found=_match_value(text, matched.start(), matched.end()), )) continue if mc.optional: diff --git a/backend-compliance/compliance/tests/test_impressum_line_of.py b/backend-compliance/compliance/tests/test_impressum_line_of.py index 1109d830..4246ab04 100644 --- a/backend-compliance/compliance/tests/test_impressum_line_of.py +++ b/backend-compliance/compliance/tests/test_impressum_line_of.py @@ -1,29 +1,22 @@ -"""_line_of — Beleg-Ausschnitt pro Pflichtangabe. +"""_match_value — exakter Beleg-Wert pro Pflichtangabe. +Die Pflichtangaben-Tabelle soll GENAU den Treffer zeigen (nur die E-Mail unter +Email, nur die Anschrift unter Anschrift) — nicht die umgebende Textpassage. Regression: bei Impressum-Texten ohne Zeilenumbrüche (z.B. BMW als ein Block) -zeigte die Pflichtangaben-Tabelle für JEDE MC denselben Anfangssatz. Jetzt wird -ein enges Fenster um den Treffer ausgeschnitten → jede MC zeigt ihren Beleg. +wurde vorher für jede MC derselbe Anfangssatz / ein breites Fenster gezeigt. """ from __future__ import annotations -from compliance.services.specialist_agents.impressum.agent import _line_of +from compliance.services.specialist_agents.impressum.agent import _match_value -def test_window_per_match_in_long_block(): - text = "A" * 200 + " EMAIL kontakt@bmw.de " + "B" * 200 + " HRB 12345 " + "C" * 200 - e_pos = text.index("kontakt@bmw.de") - h_pos = text.index("12345") - email = _line_of(text, e_pos, e_pos + len("kontakt@bmw.de")) - hrb = _line_of(text, h_pos, h_pos + 5) - assert "kontakt@bmw.de" in email - assert "12345" in hrb - assert email != hrb # nicht mehr derselbe Anfangssatz - assert len(email) <= 160 +def test_match_value_is_exact_not_context(): + text = "X" * 200 + "kontakt@bmw.de" + "Y" * 200 + s = text.index("kontakt@bmw.de") + assert _match_value(text, s, s + len("kontakt@bmw.de")) == "kontakt@bmw.de" -def test_short_line_unchanged(): - text = "Zeile eins\nkontakt@bmw.de\nZeile drei" - pos = text.index("kontakt@bmw.de") - out = _line_of(text, pos, pos + len("kontakt@bmw.de")) - assert out == "kontakt@bmw.de" +def test_match_value_normalizes_whitespace_and_caps(): + assert _match_value("A B\tC", 0, 6) == "A B C" + assert len(_match_value("z" * 300, 0, 300)) == 120