update
This commit is contained in:
62
backend/belege.py
Normal file
62
backend/belege.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""Faktenbasis eines Atoms = Quelltext-FENSTER um seine Anker, nicht nur das Zitat.
|
||||
Rollen-Trennung: Der Anker identifiziert die Stelle (kurz, verbatim, fuzzy-matchbar);
|
||||
die Substanz eines Verfahrens (Pseudo-Code, Ablauf) steht in der Quelle DANEBEN.
|
||||
Nur gegen das Zitat geprüft war jede inhaltliche Karte formal „unbelegt" — die
|
||||
Top-Verworfen-Atome (GA 73×, LPT 33×) hatten 42–150 Zeichen Beleg."""
|
||||
|
||||
from functools import lru_cache
|
||||
|
||||
import db
|
||||
from config import BELEG_FENSTER_NACH, BELEG_FENSTER_VOR
|
||||
|
||||
|
||||
@lru_cache(maxsize=64)
|
||||
def _snapshot(pfad: str) -> str:
|
||||
# Pfade sind hash-benannt (q-<hash>.md) — neuer Korpus = neuer Pfad, Cache safe
|
||||
try:
|
||||
return open(pfad, encoding="utf-8").read()
|
||||
except OSError:
|
||||
return ""
|
||||
|
||||
|
||||
def _wortgrenzen(text: str, a: int, b: int) -> str:
|
||||
aus = text[a:b]
|
||||
if a > 0 and " " in aus[:80]:
|
||||
aus = aus.split(" ", 1)[1]
|
||||
if b < len(text) and " " in aus[-80:]:
|
||||
aus = aus.rsplit(" ", 1)[0]
|
||||
return aus.strip()
|
||||
|
||||
|
||||
def fenster_liste(atom_id: int, max_fenster: int = 2) -> list[str]:
|
||||
"""Beleg-Blöcke des Atoms: Quellumgebung je Anker (überlappende gemerged),
|
||||
gedeckelt auf max_fenster. Anker ohne Span oder ohne lesbaren Snapshot
|
||||
fallen auf ihr Zitat zurück — nie schlechter als vorher."""
|
||||
anker = db.query(
|
||||
"SELECT k.start, k.ende, k.zitat, q.snapshot FROM anker k"
|
||||
" JOIN quellen q ON q.id=k.quelle_id WHERE k.atom_id=?", (atom_id,))
|
||||
spans: dict[str, list[tuple[int, int]]] = {}
|
||||
fallback: list[str] = []
|
||||
for k in anker:
|
||||
text = _snapshot(k["snapshot"]) if k["start"] >= 0 else ""
|
||||
if not text:
|
||||
fallback.append(k["zitat"])
|
||||
continue
|
||||
a = max(0, k["start"] - BELEG_FENSTER_VOR)
|
||||
b = min(len(text), k["ende"] + BELEG_FENSTER_NACH)
|
||||
spans.setdefault(k["snapshot"], []).append((a, b))
|
||||
bloecke: list[str] = []
|
||||
for snap, sp in spans.items():
|
||||
text = _snapshot(snap)
|
||||
sp.sort()
|
||||
merged: list[list[int]] = []
|
||||
for a, b in sp:
|
||||
if merged and a <= merged[-1][1]:
|
||||
merged[-1][1] = max(merged[-1][1], b)
|
||||
else:
|
||||
merged.append([a, b])
|
||||
bloecke += [_wortgrenzen(text, a, b) for a, b in merged]
|
||||
out = bloecke[:max_fenster]
|
||||
if not out: # kein Span verwertbar → wenigstens die Zitate
|
||||
out = fallback
|
||||
return [b for b in out if b]
|
||||
Reference in New Issue
Block a user