init
This commit is contained in:
73
backend/belege.py
Normal file
73
backend/belege.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""Beleg-Fenster um Anker (−300/+1200): Faktenbasis für Writer und Prüfer.
|
||||
Überlappende Fenster derselben Quelle werden gemerged (creator2-Muster).
|
||||
Außerdem: Facetten-Abdeckung — jede Soll-BELEG-Stelle braucht ein Atom in
|
||||
der Nähe (das feine 100-%-Maß, Gutachten-Lücke „Namensherkunft")."""
|
||||
from . import config, db, laden, textkit
|
||||
|
||||
|
||||
def beleg_stellen(topic: str) -> list[dict]:
|
||||
"""Alle auffindbaren Beleg-Stellen bestätigter Soll-Punkte."""
|
||||
stellen = []
|
||||
for s in db.query("SELECT id, belege FROM soll WHERE topic=? AND "
|
||||
"status='bestaetigt' AND freispruch=''", topic):
|
||||
for beleg in db.uj(s["belege"], []):
|
||||
quelle = db.one("SELECT * FROM quellen WHERE id=? AND "
|
||||
"status='geladen'", beleg["quelle"])
|
||||
if not quelle:
|
||||
continue
|
||||
try:
|
||||
text = laden.snapshot_lesen(quelle)
|
||||
except OSError:
|
||||
continue
|
||||
pos = textkit.finde_zitat(text, beleg["zitat"])
|
||||
if pos:
|
||||
stellen.append({"soll_id": s["id"], "quelle_id": quelle["id"],
|
||||
"start": pos[0], "ende": pos[1],
|
||||
"zitat": beleg["zitat"][:120]})
|
||||
return stellen
|
||||
|
||||
|
||||
def unbedeckte_belege(topic: str) -> list[dict]:
|
||||
"""Beleg-Stellen OHNE aktives Atom im ±EXTRAKT_FENSTER derselben Quelle."""
|
||||
anker = {}
|
||||
for a in db.query(
|
||||
"SELECT an.quelle_id q, an.start s FROM anker an JOIN atome a ON "
|
||||
"a.id=an.atom_id WHERE a.topic=? AND a.status='aktiv' AND "
|
||||
"an.start>=0", topic):
|
||||
anker.setdefault(a["q"], []).append(a["s"])
|
||||
return [st for st in beleg_stellen(topic)
|
||||
if not any(abs(s - st["start"]) <= config.EXTRAKT_FENSTER
|
||||
for s in anker.get(st["quelle_id"], []))]
|
||||
|
||||
|
||||
def abdeckung_prozent(topic: str) -> float | None:
|
||||
stellen = beleg_stellen(topic)
|
||||
if not stellen:
|
||||
return None
|
||||
frei = len(unbedeckte_belege(topic))
|
||||
return round((len(stellen) - frei) / len(stellen) * 100, 1)
|
||||
|
||||
|
||||
def fenster_fuer_atome(atom_ids: list[int]) -> str:
|
||||
spans: dict[int, list[list[int]]] = {}
|
||||
for aid in atom_ids:
|
||||
for a in db.query("SELECT * FROM anker WHERE atom_id=? AND start>=0", aid):
|
||||
von = max(0, a["start"] - config.BELEG_FENSTER_VOR)
|
||||
bis = a["ende"] + config.BELEG_FENSTER_NACH
|
||||
spans.setdefault(a["quelle_id"], []).append([von, bis])
|
||||
teile = []
|
||||
for qid, fenster in spans.items():
|
||||
quelle = db.one("SELECT * FROM quellen WHERE id=?", qid)
|
||||
if not quelle or quelle["status"] != "geladen":
|
||||
continue
|
||||
text = laden.snapshot_lesen(quelle)
|
||||
fenster.sort()
|
||||
gemerged = [fenster[0]]
|
||||
for von, bis in fenster[1:]:
|
||||
if von <= gemerged[-1][1]:
|
||||
gemerged[-1][1] = max(gemerged[-1][1], bis)
|
||||
else:
|
||||
gemerged.append([von, bis])
|
||||
for von, bis in gemerged:
|
||||
teile.append(f"[Quelle: {quelle['titel'][:60]}]\n{text[von:bis]}")
|
||||
return "\n\n---\n\n".join(teile)
|
||||
Reference in New Issue
Block a user