74 lines
3.1 KiB
Python
74 lines
3.1 KiB
Python
"""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)
|