#47 Fix: Highlighting retroaktiv für alle bestehenden Assessments
Problem: Alle Assessments in der Prod-DB haben Pre-#47-URLs
(/static/referenzen/X.pdf#page=N). Die _chunk_pdf_url-Änderung wirkt
nur auf NEUE Analysen, die noch nicht stattgefunden haben.
Fix (zwei Seiten):
1. Endpoint /api/wahlprogramm-cite akzeptiert jetzt auch pdf=<filename>
als Alternative zu pid=<programm_id>. Reverse-Lookup über PROGRAMME-
Registry: pdf-Filename → programm_id. Damit können die statischen
URLs aus Pre-#47-Assessments trotzdem an den Cite-Endpoint geleitet
werden.
2. Frontend: neue JS-Funktion makeCiteUrl(z) die JEDE Zitat-URL on-the-
fly umschreibt:
- /static/referenzen/X.pdf#page=N + z.text
→ /api/wahlprogramm-cite?pdf=X.pdf&seite=N&q=<urlencoded text>
- /api/wahlprogramm-cite?... → durchreichen (schon Cite-URL)
- Fallback: URL unverändert
Funktioniert retroaktiv für ALLE ~31 Assessments in der DB, ohne
Re-Analyse. Sobald ein User auf ein Zitat klickt, wird die Seite
des Wahlprogramms mit gelber Markierung gerendert.
Tests: 194/194 grün.
Refs: #47
This commit is contained in:
parent
2b2a363127
commit
47897e13cd
13
app/main.py
13
app/main.py
@ -596,7 +596,7 @@ async def quellen_page(request: Request):
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/api/wahlprogramm-cite")
|
@app.get("/api/wahlprogramm-cite")
|
||||||
async def wahlprogramm_cite(pid: str, seite: int, q: str = ""):
|
async def wahlprogramm_cite(pid: str = "", pdf: str = "", seite: int = 1, q: str = ""):
|
||||||
"""Render eine Wahlprogramm-Seite mit gelb hervorgehobener Zitat-Stelle.
|
"""Render eine Wahlprogramm-Seite mit gelb hervorgehobener Zitat-Stelle.
|
||||||
|
|
||||||
Issue #47: Klick auf eine Zitat-Quelle im Report soll direkt zur
|
Issue #47: Klick auf eine Zitat-Quelle im Report soll direkt zur
|
||||||
@ -606,11 +606,22 @@ async def wahlprogramm_cite(pid: str, seite: int, q: str = ""):
|
|||||||
1-Seiten-PDF mit ``add_highlight_annot``-Annotation auf den per
|
1-Seiten-PDF mit ``add_highlight_annot``-Annotation auf den per
|
||||||
``page.search_for`` gefundenen Bounding-Boxes.
|
``page.search_for`` gefundenen Bounding-Boxes.
|
||||||
|
|
||||||
|
Akzeptiert ``pid`` (PROGRAMME-Key) ODER ``pdf`` (Dateiname wie
|
||||||
|
``spd-grundsatzprogramm.pdf``). Letzterer ermöglicht die retroaktive
|
||||||
|
Nutzung von Pre-#47-URLs im Frontend, wo nur der statische Pfad
|
||||||
|
``/static/referenzen/<pdf>#page=<N>`` gespeichert ist.
|
||||||
|
|
||||||
Security: ``pid`` muss ein registrierter PROGRAMME-Key sein —
|
Security: ``pid`` muss ein registrierter PROGRAMME-Key sein —
|
||||||
verhindert Path-Traversal und arbiträren File-Read aus dem
|
verhindert Path-Traversal und arbiträren File-Read aus dem
|
||||||
referenzen-Verzeichnis. ``seite`` wird per Pydantic-Coercion
|
referenzen-Verzeichnis. ``seite`` wird per Pydantic-Coercion
|
||||||
auf int gezwungen. ``q`` ist auf 200 Zeichen begrenzt im Renderer.
|
auf int gezwungen. ``q`` ist auf 200 Zeichen begrenzt im Renderer.
|
||||||
"""
|
"""
|
||||||
|
# Reverse-Lookup: pdf-Filename → programm_id, falls nur pdf angegeben.
|
||||||
|
if not pid and pdf:
|
||||||
|
for p, info in PROGRAMME.items():
|
||||||
|
if info.get("pdf") == pdf:
|
||||||
|
pid = p
|
||||||
|
break
|
||||||
if pid not in PROGRAMME:
|
if pid not in PROGRAMME:
|
||||||
raise HTTPException(status_code=404, detail="Unbekanntes Wahlprogramm")
|
raise HTTPException(status_code=404, detail="Unbekanntes Wahlprogramm")
|
||||||
if seite < 1 or seite > 2000:
|
if seite < 1 or seite > 2000:
|
||||||
|
|||||||
@ -1498,12 +1498,31 @@
|
|||||||
</div>
|
</div>
|
||||||
`}).join('');
|
`}).join('');
|
||||||
|
|
||||||
|
// Issue #47: Zitat-URLs zu Cite-Endpoint umschreiben für gelbes
|
||||||
|
// Highlighting. Funktioniert retroaktiv für Pre-#47-Assessments
|
||||||
|
// (statische /static/referenzen/X.pdf#page=N) und nativ für
|
||||||
|
// Post-#47 (die schon /api/wahlprogramm-cite enthalten).
|
||||||
|
function makeCiteUrl(z) {
|
||||||
|
if (!z || !z.url) return '#';
|
||||||
|
// Schon eine Cite-URL? Durchreichen.
|
||||||
|
if (z.url.includes('/api/wahlprogramm-cite')) return z.url;
|
||||||
|
// Statische URL umschreiben: /static/referenzen/X.pdf#page=N
|
||||||
|
const m = z.url.match(/\/static\/referenzen\/(.+\.pdf)#page=(\d+)/);
|
||||||
|
if (m && z.text) {
|
||||||
|
const pdf = m[1];
|
||||||
|
const page = m[2];
|
||||||
|
const q = encodeURIComponent((z.text || '').substring(0, 200));
|
||||||
|
return `/api/wahlprogramm-cite?pdf=${encodeURIComponent(pdf)}&seite=${page}&q=${q}`;
|
||||||
|
}
|
||||||
|
return z.url;
|
||||||
|
}
|
||||||
|
|
||||||
const wahlprogrammHtml = (item.wahlprogrammScores || []).map(wp => {
|
const wahlprogrammHtml = (item.wahlprogrammScores || []).map(wp => {
|
||||||
// Zitate formatieren mit klickbaren Links
|
// Zitate formatieren mit klickbaren Links + Highlighting
|
||||||
const zitateHtml = (wp.wahlprogramm?.zitate || []).map(z => `
|
const zitateHtml = (wp.wahlprogramm?.zitate || []).map(z => `
|
||||||
<div style="margin: 0.5rem 0; padding: 0.5rem; background: #f8f9fa; border-left: 3px solid #889e33; font-size: 0.85rem;">
|
<div style="margin: 0.5rem 0; padding: 0.5rem; background: #f8f9fa; border-left: 3px solid #889e33; font-size: 0.85rem;">
|
||||||
<em>"${z.text}"</em><br>
|
<em>"${z.text}"</em><br>
|
||||||
<a href="${z.url || '#'}" target="_blank" style="color: #009da5; font-size: 0.8rem;">
|
<a href="${makeCiteUrl(z)}" target="_blank" style="color: #009da5; font-size: 0.8rem;">
|
||||||
📄 ${z.quelle}
|
📄 ${z.quelle}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user