fix(bericht): keine Doppelverweise und keine Platzhalter im Quellenverzeichnis

Zwei Befunde aus dem Bericht zu Lage 60 vom 02.08.2026.

1. Doppelte Verweise, ein Folgefehler des Dubletten-Zusammenfuehrens. Zeigten
   zwei Nummern auf dieselbe Adresse, wurden beide Verweise auf dieselbe neue
   Nummer umgeschrieben und standen dann nebeneinander: "[24][24]". Im Bericht
   kam das dreimal vor, in der gespeicherten Fassung nicht. Aufeinanderfolgende
   Verweise auf dieselbe Nummer werden jetzt zusammengezogen.

2. Platzhalter ohne Adresse. Das Modell hatte einen Eintrag {"nr": 12, "name":
   "Quelle", "url": ""} erzeugt. Der Bericht druckte ihn als "12 Quelle" ohne
   Adresse und die Statistik fuehrte ein Medium namens "Quelle". Ein Eintrag
   ohne Adresse ist kein pruefbarer Beleg und kommt nicht mehr ins
   Verzeichnis; der Verweis darauf wird wie ein verwaister behandelt und
   entfernt.

Gegenprobe an Lage 60: aus 30 Eintraegen werden 26 (drei doppelte Adressen
zusammengefuehrt, ein Platzhalter entfernt), die Zaehlung bleibt lueckenlos,
kein doppelter Verweis mehr im Text.

Neu sind 4 Pruefungen, insgesamt laufen 220 ohne Netzzugriff und ohne Kosten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
claude-dev
2026-08-02 01:41:28 +02:00
Ursprung e2638ce9d5
Commit 695983245d
2 geänderte Dateien mit 34 neuen und 3 gelöschten Zeilen

Datei anzeigen

@@ -138,11 +138,14 @@ def _dubletten_zusammenfuehren(sources: list) -> list:
""" """
behalten: dict[str, dict] = {} behalten: dict[str, dict] = {}
reihenfolge: list[str] = [] reihenfolge: list[str] = []
ohne_adresse = 0
for s in sources: for s in sources:
schluessel = _url_schluessel(s.get("url") or "") schluessel = _url_schluessel(s.get("url") or "")
if not schluessel: if not schluessel:
reihenfolge.append(id(s)) # Eintraege ohne Adresse sind keine pruefbaren Belege. Im Bericht
behalten[id(s)] = s # vom 02.08.2026 stand so ein Platzhalter als "12 Quelle" ohne
# Adresse im Verzeichnis und als "Quelle 1" in der Statistik.
ohne_adresse += 1
continue continue
if schluessel in behalten: if schluessel in behalten:
behalten[schluessel].setdefault("alias_nrs", []).append(s.get("nr")) behalten[schluessel].setdefault("alias_nrs", []).append(s.get("nr"))
@@ -151,9 +154,11 @@ def _dubletten_zusammenfuehren(sources: list) -> list:
reihenfolge.append(schluessel) reihenfolge.append(schluessel)
ergebnis = [behalten[k] for k in reihenfolge] ergebnis = [behalten[k] for k in reihenfolge]
entfernt = len(sources) - len(ergebnis) entfernt = len(sources) - len(ergebnis) - ohne_adresse
if entfernt: if entfernt:
logger.info("Quellenverzeichnis: %d doppelte Adresse(n) zusammengefuehrt", entfernt) logger.info("Quellenverzeichnis: %d doppelte Adresse(n) zusammengefuehrt", entfernt)
if ohne_adresse:
logger.info("Quellenverzeichnis: %d Eintrag/Eintraege ohne Adresse entfernt", ohne_adresse)
return ergebnis return ergebnis
@@ -207,6 +212,10 @@ def _apply_citation_map(text: str, abbildung: dict) -> str:
return "" return ""
ergebnis = re.sub(r"\[(\d{1,5}[a-z]?)\]", ersetze, text) ergebnis = re.sub(r"\[(\d{1,5}[a-z]?)\]", ersetze, text)
# Zeigten zwei Nummern auf dieselbe Adresse, landen ihre Verweise nach der
# Umschreibung auf derselben Zahl und stehen dann doppelt nebeneinander:
# "[24][24]". Im Bericht vom 02.08.2026 kam das dreimal vor.
ergebnis = re.sub(r"(\[(\d{1,5})\])(?:\s*\[\2\])+", r"\1", ergebnis)
if verwaist: if verwaist:
logger.warning( logger.warning(
"Bericht: %d Quellenverweise ohne Verzeichniseintrag entfernt (%s)", "Bericht: %d Quellenverweise ohne Verzeichniseintrag entfernt (%s)",

Datei anzeigen

@@ -172,6 +172,28 @@ pruefe("C13 Ausgabe zaehlt wieder lueckenlos", [s["nr"] for s in neu_d] == [1, 2
[s["nr"] for s in neu_d]) [s["nr"] for s in neu_d])
pruefe("C14 Verweis auf die entfernte Nummer zeigt auf den behaltenen Eintrag", pruefe("C14 Verweis auf die entfernte Nummer zeigt auf den behaltenen Eintrag",
abb_d.get(23) == abb_d.get(22) == 1 and abb_d.get(25) == abb_d.get(24) == 2, abb_d) abb_d.get(23) == abb_d.get(22) == 1 and abb_d.get(25) == abb_d.get(24) == 2, abb_d)
# Zeigen zwei Verweise nach dem Zusammenfuehren auf dieselbe Zahl, darf im
# Text nicht "[24][24]" stehen bleiben. Im Bericht vom 02.08.2026 dreimal.
pruefe("C15a doppelter Verweis auf dieselbe Nummer wird zusammengezogen",
rg._apply_citation_map("Beleg [22][23] und Ende.", abb_d) == "Beleg [1] und Ende.",
rg._apply_citation_map("Beleg [22][23] und Ende.", abb_d))
pruefe("C15b verschiedene Nummern bleiben nebeneinander stehen",
rg._apply_citation_map("Beleg [22][24].", abb_d) == "Beleg [1][2].",
rg._apply_citation_map("Beleg [22][24].", abb_d))
# Eintraege ohne Adresse sind keine pruefbaren Belege.
ohne_adresse = rg._prepare_sources({"sources_json": json.dumps([
{"nr": 11, "name": "tagesschau", "url": "https://www.tagesschau.de/a.html"},
{"nr": 12, "name": "Quelle", "url": ""},
{"nr": 13, "name": "ZDF heute", "url": "https://www.zdfheute.de/b.html"},
], ensure_ascii=False)})
pruefe("C15c Eintrag ohne Adresse kommt nicht ins Verzeichnis",
[s["nr"] for s in ohne_adresse] == [11, 13], [s["nr"] for s in ohne_adresse])
_, abb_o = rg._renumber_sources(ohne_adresse)
pruefe("C15d Verweis auf den entfernten Eintrag wird getilgt",
rg._apply_citation_map("Beleg [11][12].", abb_o) == "Beleg [1].",
rg._apply_citation_map("Beleg [11][12].", abb_o))
pruefe("C15 Text mit beiden Nummern bleibt aufloesbar", pruefe("C15 Text mit beiden Nummern bleibt aufloesbar",
rg._apply_citation_map("Beleg [22] und [23].", abb_d) == "Beleg [1] und [1].", rg._apply_citation_map("Beleg [22] und [23].", abb_d) == "Beleg [1] und [1].",
rg._apply_citation_map("Beleg [22] und [23].", abb_d)) rg._apply_citation_map("Beleg [22] und [23].", abb_d))