feat(#139,#129,#138,#141): v2-Frontend (ECOnGOOD-CD), Login-Modal, Auto-DL, OG-Cards
v2-Frontend (#139, ECOnGOOD CD Manual Juni 2024):
- app/static/v2/: tokens.css, fonts.css, v2.css, Nunito-Sans woff2, Phosphor-Icons (21 SVGs)
- app/templates/v2/: base.html + 11 Screens + 8 Component-Macros
- AppShell mit Sidebar (Lesen/Pruefen/Daten/Admin), v2-Detail mit allen Features
(ScoreHero, MatrixMini, QuoteCard, Redline, Fraktions-Scores)
- v2 ist jetzt Default unter / — classic unter /classic
- Login-Modal in v2-Topbar mit Tabs Anmelden/Registrieren (#129)
- Phosphor-Icons in Sidebar + Topbar mit dynamischem Theme-Toggle
- Keyboard-Shortcuts (j/k/Enter/Esc/?/path), Landtag-Suche, Antrag-Historie,
Sort-Dropdown, Matrix-Feld-Info-Modal, Bookmarks/Comments/Voting/Share/Re-Analyze
Backend-Erweiterungen:
- main.py: ~30 neue Routes (/v2/*, /antrag/{ds}, /api/auth/{login,refresh,logout},
/api/me/merkliste/*, /api/admin/*, /v2/admin/*, OG-Cards, etc.)
- og_card.py + og_template: Open-Graph-Bilder via Playwright (#141)
- wahlprogramm_fetch.py + wahlprogramm-links.yaml: SHA-Gate Auto-DL (#138)
- auswertungen.py: BL-Filter + get_wahlperioden Helper (#137)
- auth.py: Direct-Access-Grant + Refresh-Token-Cookie
Classic-Updates:
- Header-DRY via _header.html, Auswertungen redirected, Batch-Inline raus
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:55:57 +02:00
{% extends "v2/base.html" %}
{% block title %}Quellen — GWÖ-Antragsprüfer{% endblock %}
{% set v2_active_nav = "" %}
{% block head_extra %}
< style >
.quellen-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 12px;
margin-bottom: 1.5rem;
}
.prog-card {
display: flex;
gap: 12px;
padding: 14px;
background: var(--ecg-card-bg);
border: 1px solid var(--ecg-border);
border-radius: 6px;
}
.prog-card img {
width: 64px;
height: auto;
border: 1px solid var(--ecg-border);
border-radius: 3px;
flex-shrink: 0;
object-fit: cover;
}
.prog-meta { font-size: 11px; opacity: 0.6; margin-top: 2px; margin-bottom: 6px; }
.prog-badge {
display: inline-block;
padding: 1px 5px;
border-radius: 3px;
font-size: 10px;
font-weight: 700;
margin-right: 4px;
font-family: var(--font-mono);
}
.badge-spd { background: #e3000f; color: #fff; }
.badge-cdu { background: #2c2c2c; color: #fff; }
.badge-gruene,.badge-grüne { background: #46962b; color: #fff; }
.badge-fdp { background: #ffed00; color: #222; }
.badge-afd { background: #009ee0; color: #fff; }
.badge-linke { background: #be3075; color: #fff; }
.badge-type-wp { background: var(--ecg-teal); color: #fff; }
.badge-type-pp { background: var(--ecg-green); color: #fff; }
.indexed-ok { color: var(--ecg-green); font-size: 11px; font-family: var(--font-mono); }
.indexed-no { color: var(--ecg-dark); opacity: 0.4; font-size: 11px; font-family: var(--font-mono); }
.stat-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 10px;
margin: 10px 0;
}
.stat-cell {
text-align: center;
padding: 12px;
background: var(--ecg-bg-subtle);
border-radius: 4px;
}
.stat-val { font-family: var(--font-display); font-size: 28px; color: var(--ecg-teal); font-weight: 900; }
.stat-lbl { font-size: 11px; opacity: 0.6; margin-top: 2px; }
.section-h2 {
font-family: var(--font-display);
font-size: 16px;
color: var(--ecg-teal);
margin: 1.5rem 0 0.75rem;
padding-bottom: 4px;
border-bottom: 2px solid var(--ecg-teal);
}
feat(quellen): Semantische Volltextsuche über alle Programme (#235)
GET /api/quellen/search?q=&filter=current|all&top_k=&bundesland=&partei=
nutzt text-embedding-v4 für wortunscharfe Suche (Endungen + Synonyme).
Filter:
- filter=current: nur Programme mit gueltig_bis IS NULL (Default)
- filter=all: auch historische Programme
Response liefert pro Treffer name, partei, bundesland, gueltig_ab/bis,
seite, gekürztes Snippet, similarity, plus pdf_url mit Direkt-Sprung
ins highlightete PDF (über /api/wahlprogramm-cite).
UI auf /quellen oben über der BL-Auflistung:
- Suchfeld + Submit
- Radio-Toggle "nur aktuelle Programme" / "auch historische"
- Treffer-Karten mit Partei-Badge, gültig-Pille (grün/grau),
Seite + Relevanz-%, Snippet, Direktlink ins PDF
- Filter-Wechsel triggert automatischen Re-Run
Smoketest dev: "Klimaschutz" liefert 13 Treffer in aktuellen Programmen
mit korrekter Similarity-Sortierung; "Solidarität" mischt Wahl- und
Grundsatzprogramme. Zugriff erfordert keinen Login (read-only).
2026-05-08 14:11:09 +02:00
.search-box {
display: flex;
gap: 8px;
flex-wrap: wrap;
align-items: center;
margin: 8px 0 4px;
}
.search-box input[type="text"] {
flex: 1 1 240px;
min-width: 200px;
padding: 8px 10px;
font-family: var(--font-mono);
font-size: 13px;
border: 1px solid var(--ecg-border);
border-radius: 4px;
background: var(--ecg-card-bg);
color: var(--ecg-dark);
}
.search-box button {
padding: 8px 16px;
font-family: var(--font-mono);
font-size: 12px;
background: var(--ecg-teal);
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
.search-box button:disabled { opacity: 0.5; cursor: wait; }
.search-filter {
display: inline-flex;
gap: 14px;
font-size: 12px;
font-family: var(--font-mono);
}
.search-filter label { cursor: pointer; }
.search-results { margin-top: 12px; }
.search-hit {
padding: 10px 12px;
margin-bottom: 8px;
background: var(--ecg-card-bg);
border: 1px solid var(--ecg-border);
border-radius: 4px;
border-left: 3px solid var(--ecg-teal);
}
.search-hit.historic { border-left-color: var(--ecg-dark); opacity: 0.85; }
.search-hit-meta {
font-family: var(--font-mono);
font-size: 11px;
opacity: 0.7;
margin-top: 2px;
margin-bottom: 6px;
}
.search-hit-text {
font-size: 13px;
line-height: 1.5;
color: var(--ecg-dark);
}
.search-hit-actions {
margin-top: 6px;
font-size: 11px;
font-family: var(--font-mono);
}
.search-hit-actions a { color: var(--ecg-teal); margin-right: 12px; }
.search-status { font-size: 12px; opacity: 0.7; margin-top: 8px; font-family: var(--font-mono); }
.gueltig-pill {
display: inline-block;
padding: 1px 6px;
font-size: 10px;
font-family: var(--font-mono);
border-radius: 3px;
margin-left: 4px;
}
.gueltig-pill.aktuell { background: var(--ecg-green); color: #fff; }
.gueltig-pill.historisch { background: var(--ecg-dark); color: #fff; opacity: 0.7; }
feat(#139,#129,#138,#141): v2-Frontend (ECOnGOOD-CD), Login-Modal, Auto-DL, OG-Cards
v2-Frontend (#139, ECOnGOOD CD Manual Juni 2024):
- app/static/v2/: tokens.css, fonts.css, v2.css, Nunito-Sans woff2, Phosphor-Icons (21 SVGs)
- app/templates/v2/: base.html + 11 Screens + 8 Component-Macros
- AppShell mit Sidebar (Lesen/Pruefen/Daten/Admin), v2-Detail mit allen Features
(ScoreHero, MatrixMini, QuoteCard, Redline, Fraktions-Scores)
- v2 ist jetzt Default unter / — classic unter /classic
- Login-Modal in v2-Topbar mit Tabs Anmelden/Registrieren (#129)
- Phosphor-Icons in Sidebar + Topbar mit dynamischem Theme-Toggle
- Keyboard-Shortcuts (j/k/Enter/Esc/?/path), Landtag-Suche, Antrag-Historie,
Sort-Dropdown, Matrix-Feld-Info-Modal, Bookmarks/Comments/Voting/Share/Re-Analyze
Backend-Erweiterungen:
- main.py: ~30 neue Routes (/v2/*, /antrag/{ds}, /api/auth/{login,refresh,logout},
/api/me/merkliste/*, /api/admin/*, /v2/admin/*, OG-Cards, etc.)
- og_card.py + og_template: Open-Graph-Bilder via Playwright (#141)
- wahlprogramm_fetch.py + wahlprogramm-links.yaml: SHA-Gate Auto-DL (#138)
- auswertungen.py: BL-Filter + get_wahlperioden Helper (#137)
- auth.py: Direct-Access-Grant + Refresh-Token-Cookie
Classic-Updates:
- Header-DRY via _header.html, Auswertungen redirected, Batch-Inline raus
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:55:57 +02:00
< / style >
{% endblock %}
{% block main %}
< div style = "padding:0 0 2rem;" >
< h1 style = "font-family:var(--font-display);font-size:22px;color:var(--ecg-teal);margin:0 0 4px;" > Quellen & Referenzdokumente< / h1 >
< p style = "font-size:12px;font-family:var(--font-mono);color:var(--ecg-dark);opacity:0.6;" >
Wahl- und Grundsatzprogramme · semantisch indexiert
< / p >
< / div >
< div class = "v2-kasten outline-blue" style = "margin-bottom:1.5rem;" >
< p >
Der GWÖ-Antragsprüfer vergleicht parlamentarische Anträge mit den Wahl- und Grundsatzprogrammen
der Parteien. Hier finden Sie alle verwendeten Originaldokumente zum Download.
< / p >
< p style = "font-size:11px;opacity:0.7;margin-top:6px;" >
Die Programme werden semantisch indexiert, um relevante Passagen für jeden Antrag zu finden.
< / p >
< / div >
<!-- Indexierungsstatus -->
< div class = "v2-kasten outline-green" style = "margin-bottom:1.5rem;" >
< h4 > Indexierungsstatus< / h4 >
< div class = "stat-grid" >
< div class = "stat-cell" >
< div class = "stat-val" > {{ status.indexed }}< / div >
< div class = "stat-lbl" > Indexiert< / div >
< / div >
< div class = "stat-cell" >
< div class = "stat-val" > {{ status.total }}< / div >
< div class = "stat-lbl" > Gesamt< / div >
< / div >
< / div >
< div style = "margin-top:8px;" >
< button onclick = "indexAll()"
style="font-family:var(--font-mono);font-size:11px;padding:5px 12px;background:var(--ecg-teal);color:#fff;border:none;border-radius:3px;cursor:pointer;">
Alle Programme indexieren (Admin)
< / button >
< span id = "index-status" style = "margin-left:10px;font-size:11px;opacity:0.7;" > < / span >
< / div >
< / div >
feat(quellen): Semantische Volltextsuche über alle Programme (#235)
GET /api/quellen/search?q=&filter=current|all&top_k=&bundesland=&partei=
nutzt text-embedding-v4 für wortunscharfe Suche (Endungen + Synonyme).
Filter:
- filter=current: nur Programme mit gueltig_bis IS NULL (Default)
- filter=all: auch historische Programme
Response liefert pro Treffer name, partei, bundesland, gueltig_ab/bis,
seite, gekürztes Snippet, similarity, plus pdf_url mit Direkt-Sprung
ins highlightete PDF (über /api/wahlprogramm-cite).
UI auf /quellen oben über der BL-Auflistung:
- Suchfeld + Submit
- Radio-Toggle "nur aktuelle Programme" / "auch historische"
- Treffer-Karten mit Partei-Badge, gültig-Pille (grün/grau),
Seite + Relevanz-%, Snippet, Direktlink ins PDF
- Filter-Wechsel triggert automatischen Re-Run
Smoketest dev: "Klimaschutz" liefert 13 Treffer in aktuellen Programmen
mit korrekter Similarity-Sortierung; "Solidarität" mischt Wahl- und
Grundsatzprogramme. Zugriff erfordert keinen Login (read-only).
2026-05-08 14:11:09 +02:00
<!-- Volltextsuche (semantisch) -->
< div class = "v2-kasten outline-blue" style = "margin-bottom:1.5rem;" >
< h4 > Volltextsuche durch alle Programme< / h4 >
< p style = "font-size:12px;opacity:0.75;margin:4px 0 8px;" >
Semantische Suche über alle indizierten Wahl- und Grundsatzprogramme.
Wortunscharf — Endungen sind egal, verwandte Begriffe werden ebenfalls
gefunden.
< / p >
2026-05-09 01:09:45 +02:00
< div id = "quellen-search-form" class = "search-box" >
feat(quellen): Semantische Volltextsuche über alle Programme (#235)
GET /api/quellen/search?q=&filter=current|all&top_k=&bundesland=&partei=
nutzt text-embedding-v4 für wortunscharfe Suche (Endungen + Synonyme).
Filter:
- filter=current: nur Programme mit gueltig_bis IS NULL (Default)
- filter=all: auch historische Programme
Response liefert pro Treffer name, partei, bundesland, gueltig_ab/bis,
seite, gekürztes Snippet, similarity, plus pdf_url mit Direkt-Sprung
ins highlightete PDF (über /api/wahlprogramm-cite).
UI auf /quellen oben über der BL-Auflistung:
- Suchfeld + Submit
- Radio-Toggle "nur aktuelle Programme" / "auch historische"
- Treffer-Karten mit Partei-Badge, gültig-Pille (grün/grau),
Seite + Relevanz-%, Snippet, Direktlink ins PDF
- Filter-Wechsel triggert automatischen Re-Run
Smoketest dev: "Klimaschutz" liefert 13 Treffer in aktuellen Programmen
mit korrekter Similarity-Sortierung; "Solidarität" mischt Wahl- und
Grundsatzprogramme. Zugriff erfordert keinen Login (read-only).
2026-05-08 14:11:09 +02:00
< input type = "text" id = "quellen-q" name = "q" placeholder = "z.B. Klimaschutz, soziale Gerechtigkeit, Mietpreisbremse"
autocomplete="off" minlength="2" maxlength="200">
2026-05-09 01:09:45 +02:00
< button type = "button" id = "quellen-q-btn" > Suchen< / button >
< / div >
feat(quellen): Semantische Volltextsuche über alle Programme (#235)
GET /api/quellen/search?q=&filter=current|all&top_k=&bundesland=&partei=
nutzt text-embedding-v4 für wortunscharfe Suche (Endungen + Synonyme).
Filter:
- filter=current: nur Programme mit gueltig_bis IS NULL (Default)
- filter=all: auch historische Programme
Response liefert pro Treffer name, partei, bundesland, gueltig_ab/bis,
seite, gekürztes Snippet, similarity, plus pdf_url mit Direkt-Sprung
ins highlightete PDF (über /api/wahlprogramm-cite).
UI auf /quellen oben über der BL-Auflistung:
- Suchfeld + Submit
- Radio-Toggle "nur aktuelle Programme" / "auch historische"
- Treffer-Karten mit Partei-Badge, gültig-Pille (grün/grau),
Seite + Relevanz-%, Snippet, Direktlink ins PDF
- Filter-Wechsel triggert automatischen Re-Run
Smoketest dev: "Klimaschutz" liefert 13 Treffer in aktuellen Programmen
mit korrekter Similarity-Sortierung; "Solidarität" mischt Wahl- und
Grundsatzprogramme. Zugriff erfordert keinen Login (read-only).
2026-05-08 14:11:09 +02:00
< div class = "search-filter" style = "margin-top:6px;" >
< label > < input type = "radio" name = "qfilter" value = "current" checked > nur aktuelle Programme< / label >
< label > < input type = "radio" name = "qfilter" value = "all" > auch historische< / label >
< / div >
< div id = "quellen-search-status" class = "search-status" > < / div >
< div id = "quellen-search-results" class = "search-results" > < / div >
< / div >
feat(#139,#129,#138,#141): v2-Frontend (ECOnGOOD-CD), Login-Modal, Auto-DL, OG-Cards
v2-Frontend (#139, ECOnGOOD CD Manual Juni 2024):
- app/static/v2/: tokens.css, fonts.css, v2.css, Nunito-Sans woff2, Phosphor-Icons (21 SVGs)
- app/templates/v2/: base.html + 11 Screens + 8 Component-Macros
- AppShell mit Sidebar (Lesen/Pruefen/Daten/Admin), v2-Detail mit allen Features
(ScoreHero, MatrixMini, QuoteCard, Redline, Fraktions-Scores)
- v2 ist jetzt Default unter / — classic unter /classic
- Login-Modal in v2-Topbar mit Tabs Anmelden/Registrieren (#129)
- Phosphor-Icons in Sidebar + Topbar mit dynamischem Theme-Toggle
- Keyboard-Shortcuts (j/k/Enter/Esc/?/path), Landtag-Suche, Antrag-Historie,
Sort-Dropdown, Matrix-Feld-Info-Modal, Bookmarks/Comments/Voting/Share/Re-Analyze
Backend-Erweiterungen:
- main.py: ~30 neue Routes (/v2/*, /antrag/{ds}, /api/auth/{login,refresh,logout},
/api/me/merkliste/*, /api/admin/*, /v2/admin/*, OG-Cards, etc.)
- og_card.py + og_template: Open-Graph-Bilder via Playwright (#141)
- wahlprogramm_fetch.py + wahlprogramm-links.yaml: SHA-Gate Auto-DL (#138)
- auswertungen.py: BL-Filter + get_wahlperioden Helper (#137)
- auth.py: Direct-Access-Grant + Refresh-Token-Cookie
Classic-Updates:
- Header-DRY via _header.html, Auswertungen redirected, Batch-Inline raus
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:55:57 +02:00
<!-- Wahlprogramme nach BL -->
{% for bl_name, bl_progs in wahlprogramme_grouped %}
< h2 class = "section-h2" > {{ bl_name }}< / h2 >
< div class = "quellen-grid" >
{% for prog in bl_progs %}
< div class = "prog-card" >
< a href = "{{ prog.pdf_url }}" target = "_blank" style = "flex-shrink:0;" >
< img src = "/api/programme/thumbnail/{{ prog.id }}" alt = "{{ prog.name }}"
loading="lazy" onerror="this.style.display='none'">
< / a >
< div style = "min-width:0;" >
< div style = "font-family:var(--font-display);font-size:13px;margin-bottom:3px;" >
< span class = "prog-badge badge-{{ prog.partei|lower|replace('ü','ue')|replace('ä','ae')|replace('ö','oe') }}" > {{ prog.partei }}< / span >
{{ prog.name }}
< / div >
< div class = "prog-meta" >
< span class = "prog-badge badge-type-wp" > Wahlprogramm< / span >
{% if prog.bundesland %}{{ prog.bundesland }}{% endif %}
< / div >
< div style = "display:flex;align-items:center;gap:8px;flex-wrap:wrap;" >
< a href = "{{ prog.pdf_url }}" target = "_blank"
style="font-size:11px;font-family:var(--font-mono);color:var(--ecg-teal);">
PDF herunterladen
< / a >
{% for s in status.programmes if s.id == prog.id %}
{% if s.indexed %}
< span class = "indexed-ok" > ✓ {{ s.chunks }} Chunks< / span >
{% else %}
< span class = "indexed-no" > ○ Nicht indexiert< / span >
{% endif %}
{% endfor %}
< / div >
< / div >
< / div >
{% endfor %}
< / div >
{% endfor %}
<!-- Grundsatzprogramme -->
< h2 class = "section-h2" > Grundsatzprogramme (Bundesebene)< / h2 >
< div class = "quellen-grid" >
{% for prog in grundsatzprogramme %}
< div class = "prog-card" >
< a href = "{{ prog.pdf_url }}" target = "_blank" style = "flex-shrink:0;" >
< img src = "/api/programme/thumbnail/{{ prog.id }}" alt = "{{ prog.name }}"
loading="lazy" onerror="this.style.display='none'">
< / a >
< div style = "min-width:0;" >
< div style = "font-family:var(--font-display);font-size:13px;margin-bottom:3px;" >
< span class = "prog-badge badge-{{ prog.partei|lower|replace('ü','ue')|replace('ä','ae')|replace('ö','oe') }}" > {{ prog.partei }}< / span >
{{ prog.name }}
< / div >
< div class = "prog-meta" >
< span class = "prog-badge badge-type-pp" > Grundsatzprogramm< / span >
< / div >
< div style = "display:flex;align-items:center;gap:8px;flex-wrap:wrap;" >
< a href = "{{ prog.pdf_url }}" target = "_blank"
style="font-size:11px;font-family:var(--font-mono);color:var(--ecg-teal);">
PDF herunterladen
< / a >
{% for s in status.programmes if s.id == prog.id %}
{% if s.indexed %}
< span class = "indexed-ok" > ✓ {{ s.chunks }} Chunks< / span >
{% else %}
< span class = "indexed-no" > ○ Nicht indexiert< / span >
{% endif %}
{% endfor %}
< / div >
< / div >
< / div >
{% endfor %}
< / div >
< div class = "v2-kasten outline-green" style = "margin-top:1rem;" >
< h4 > Hinweise zur Methodik< / h4 >
< ul style = "margin:6px 0 0 1.2rem;font-size:12px;" >
< li > Die Programme werden in semantische Chunks aufgeteilt (~400 Wörter)< / li >
< li > Jeder Chunk wird mit einem Embedding-Modell vektorisiert< / li >
< li > Bei der Analyse wird der Antrag ebenfalls vektorisiert< / li >
< li > Die ähnlichsten Passagen werden als Kontext an das LLM übergeben< / li >
< li > Das LLM zitiert nur, wenn eine Passage wirklich zur Argumentation passt< / li >
< / ul >
< / div >
{% endblock %}
{% block body_scripts %}
< script >
feat(tour): seitenspezifische Touren für jede Public/Auth-Page
Bisher hatten nur Startseite und Antrag-Detail eigene Touren — die
Fallback-Tour (Logo+Topbar+Sidebar) war für jede andere Page identisch.
User-Wunsch: pro Menüpunkt eigene Tour, die diese Page funktional
erklärt.
Pro Page 3-5 Stationen mit konkreten Selektoren auf die Hauptbereiche:
- Quellen: Volltextsuche, Treffer-Liste, Programm-Karten
- Methodik: TOC, GWÖ-Matrix, Pipeline
- Auswertungen: Tabs (4 Sichten), Filter, Visualisierung
- Stimmverhalten: Stimm-Index, Heuchelei-Quote, Cross-BL — eigene
Variante via {% if v2_active_nav == 'stimmverhalten' %}, da
Stimmverhalten dasselbe Template wie Auswertungen nutzt
- Tags: Tag-Wolke, gefilterte Treffer
- Merkliste: Liste, Empty-State
- Neuer Antrag: BL-Wahl, Drucksachen-Eingabe, Modell, Submit
- Abos: Filter, aktive Abos
- Atom-Feed: Filter, Feed-URL
- Landtag-Suche: BL, Suchbegriff, Treffer + Bewertung anstoßen
- Aktuelle Themen: Top-News, Cluster+Zeitreihe, PM-Generator
Texte ermächtigend formuliert (was-wofür-wie), nicht oberflächlich.
Selektoren mit Fallback-Liste (komma-separiert), damit Templates ohne
exakte Klassennamen die Station trotzdem zeigen können.
2026-05-09 09:03:59 +02:00
window.GWOE_TOUR_STEPS = [
{ selector: 'h1',
title: 'Quellen — was hier liegt',
text: 'Diese Seite listet alle Wahl- und Grundsatzprogramme der Parteien, gegen die der GWÖ-Antragsprüfer Anträge bewertet. Aktuell sind das 287 Programme — historisch und aktuell, von Bundestag bis Bundesländer.' },
{ selector: '#quellen-q, .gwoe-search-form',
title: 'Volltextsuche',
text: 'Hier findest du Stellen in den Programmen — semantisch durchsuchbar. Wortunscharf: Endungen sind egal, verwandte Begriffe werden ebenfalls gefunden. Filter „nur aktuelle Programme" oder „auch historische" daneben.' },
{ selector: '.search-results, #search-results',
title: 'Treffer',
text: 'Pro Treffer siehst du das Programm, die Seitenzahl, einen Snippet-Auszug und einen Direkt-Link ins PDF mit gelb hervorgehobener Stelle.' },
{ selector: '.prog-card, .v2-kasten.outline-blue',
title: 'Programm-Karten',
text: 'Unten gruppiert nach Bundesland alle Programme als Karten — mit Direktlink zum PDF und Hinweis ob das Programm semantisch indiziert ist.' },
];
< / script >
< script >
feat(#139,#129,#138,#141): v2-Frontend (ECOnGOOD-CD), Login-Modal, Auto-DL, OG-Cards
v2-Frontend (#139, ECOnGOOD CD Manual Juni 2024):
- app/static/v2/: tokens.css, fonts.css, v2.css, Nunito-Sans woff2, Phosphor-Icons (21 SVGs)
- app/templates/v2/: base.html + 11 Screens + 8 Component-Macros
- AppShell mit Sidebar (Lesen/Pruefen/Daten/Admin), v2-Detail mit allen Features
(ScoreHero, MatrixMini, QuoteCard, Redline, Fraktions-Scores)
- v2 ist jetzt Default unter / — classic unter /classic
- Login-Modal in v2-Topbar mit Tabs Anmelden/Registrieren (#129)
- Phosphor-Icons in Sidebar + Topbar mit dynamischem Theme-Toggle
- Keyboard-Shortcuts (j/k/Enter/Esc/?/path), Landtag-Suche, Antrag-Historie,
Sort-Dropdown, Matrix-Feld-Info-Modal, Bookmarks/Comments/Voting/Share/Re-Analyze
Backend-Erweiterungen:
- main.py: ~30 neue Routes (/v2/*, /antrag/{ds}, /api/auth/{login,refresh,logout},
/api/me/merkliste/*, /api/admin/*, /v2/admin/*, OG-Cards, etc.)
- og_card.py + og_template: Open-Graph-Bilder via Playwright (#141)
- wahlprogramm_fetch.py + wahlprogramm-links.yaml: SHA-Gate Auto-DL (#138)
- auswertungen.py: BL-Filter + get_wahlperioden Helper (#137)
- auth.py: Direct-Access-Grant + Refresh-Token-Cookie
Classic-Updates:
- Header-DRY via _header.html, Auswertungen redirected, Batch-Inline raus
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:55:57 +02:00
async function indexAll() {
const statusEl = document.getElementById('index-status');
statusEl.textContent = 'Indexierung gestartet …';
try {
const fd = new FormData();
fd.append('all_programmes', 'true');
const resp = await fetch('/api/programme/index', { method: 'POST', body: fd });
const data = await resp.json();
statusEl.textContent = 'Indexierung läuft für ' + data.programmes.length + ' Programme';
setTimeout(() => location.reload(), 30000);
} catch (e) {
statusEl.textContent = 'Fehler: ' + e.message;
}
}
feat(quellen): Semantische Volltextsuche über alle Programme (#235)
GET /api/quellen/search?q=&filter=current|all&top_k=&bundesland=&partei=
nutzt text-embedding-v4 für wortunscharfe Suche (Endungen + Synonyme).
Filter:
- filter=current: nur Programme mit gueltig_bis IS NULL (Default)
- filter=all: auch historische Programme
Response liefert pro Treffer name, partei, bundesland, gueltig_ab/bis,
seite, gekürztes Snippet, similarity, plus pdf_url mit Direkt-Sprung
ins highlightete PDF (über /api/wahlprogramm-cite).
UI auf /quellen oben über der BL-Auflistung:
- Suchfeld + Submit
- Radio-Toggle "nur aktuelle Programme" / "auch historische"
- Treffer-Karten mit Partei-Badge, gültig-Pille (grün/grau),
Seite + Relevanz-%, Snippet, Direktlink ins PDF
- Filter-Wechsel triggert automatischen Re-Run
Smoketest dev: "Klimaschutz" liefert 13 Treffer in aktuellen Programmen
mit korrekter Similarity-Sortierung; "Solidarität" mischt Wahl- und
Grundsatzprogramme. Zugriff erfordert keinen Login (read-only).
2026-05-08 14:11:09 +02:00
function escHtml(s) {
return String(s == null ? '' : s)
.replace(/& /g, '& ').replace(/< /g, '< ')
.replace(/>/g, '> ').replace(/"/g, '" ').replace(/'/g, '' ');
}
2026-05-09 01:09:45 +02:00
async function runSearch() {
feat(quellen): Semantische Volltextsuche über alle Programme (#235)
GET /api/quellen/search?q=&filter=current|all&top_k=&bundesland=&partei=
nutzt text-embedding-v4 für wortunscharfe Suche (Endungen + Synonyme).
Filter:
- filter=current: nur Programme mit gueltig_bis IS NULL (Default)
- filter=all: auch historische Programme
Response liefert pro Treffer name, partei, bundesland, gueltig_ab/bis,
seite, gekürztes Snippet, similarity, plus pdf_url mit Direkt-Sprung
ins highlightete PDF (über /api/wahlprogramm-cite).
UI auf /quellen oben über der BL-Auflistung:
- Suchfeld + Submit
- Radio-Toggle "nur aktuelle Programme" / "auch historische"
- Treffer-Karten mit Partei-Badge, gültig-Pille (grün/grau),
Seite + Relevanz-%, Snippet, Direktlink ins PDF
- Filter-Wechsel triggert automatischen Re-Run
Smoketest dev: "Klimaschutz" liefert 13 Treffer in aktuellen Programmen
mit korrekter Similarity-Sortierung; "Solidarität" mischt Wahl- und
Grundsatzprogramme. Zugriff erfordert keinen Login (read-only).
2026-05-08 14:11:09 +02:00
const q = document.getElementById('quellen-q').value.trim();
const filter = (document.querySelector('input[name="qfilter"]:checked') || {}).value || 'current';
const statusEl = document.getElementById('quellen-search-status');
const resultsEl = document.getElementById('quellen-search-results');
const btn = document.getElementById('quellen-q-btn');
if (q.length < 2 ) {
statusEl.textContent = 'Bitte mindestens 2 Zeichen eingeben.';
return false;
}
btn.disabled = true;
statusEl.textContent = 'Suche läuft …';
resultsEl.innerHTML = '';
try {
const params = new URLSearchParams({ q: q, filter: filter, top_k: '20' });
const resp = await fetch('/api/quellen/search?' + params.toString());
if (!resp.ok) {
statusEl.textContent = 'Fehler: ' + resp.status + ' ' + resp.statusText;
return false;
}
const data = await resp.json();
if (!data.results || data.results.length === 0) {
statusEl.textContent = 'Keine Treffer.';
return false;
}
statusEl.textContent = data.n_results + ' Treffer'
+ (filter === 'current' ? ' in aktuellen Programmen' : ' (alle Programme)') + '.';
const html = data.results.map(r => {
const aktuell = r.gueltig_bis === null;
const cls = aktuell ? '' : ' historic';
const pill = aktuell
? '< span class = "gueltig-pill aktuell" > aktuell< / span > '
: '< span class = "gueltig-pill historisch" > ' + escHtml(r.gueltig_ab) + ' bis ' + escHtml(r.gueltig_bis) + '< / span > ';
const sim = (r.similarity * 100).toFixed(0) + '%';
const partei = escHtml(r.partei || '');
const bl = escHtml(r.bundesland || '');
const wp = r.wp ? ' · WP' + escHtml(r.wp) : '';
const seite = escHtml(r.seite);
return '< div class = "search-hit' + cls + '" > '
+ '< div style = "font-family:var(--font-display);font-size:13px;" > '
+ '< span class = "prog-badge badge-' + partei.toLowerCase().replace(/ü/g,'ue').replace(/ä/g,'ae').replace(/ö/g,'oe') + '" > ' + partei + '< / span > '
+ escHtml(r.name) + ' ' + pill
+ '< / div > '
+ '< div class = "search-hit-meta" > '
+ 'Seite ' + seite + ' · ' + bl + wp + ' · Relevanz ' + sim
+ '< / div > '
+ '< div class = "search-hit-text" > ' + escHtml(r.text) + '< / div > '
+ '< div class = "search-hit-actions" > '
+ '< a href = "' + escHtml(r.pdf_url) + '" target = "_blank" > → Stelle im PDF anzeigen< / a > '
+ '< / div > '
+ '< / div > ';
}).join('');
resultsEl.innerHTML = html;
} catch (e) {
statusEl.textContent = 'Fehler: ' + e.message;
} finally {
btn.disabled = false;
}
return false;
}
2026-05-09 01:09:45 +02:00
// Suche binden — kein < form > , sondern Click auf Button + Enter im Input.
// Frühere < form > -Variante hatte einen Browser-Quirk: trotz preventDefault()
// im submit-Handler hat der anschließende fetch() nie eine Response
// erhalten (Status blieb auf "Suche läuft …"). Mit einem Plain-Container +
// Button onclick funktioniert es zuverlässig.
2026-05-09 01:02:43 +02:00
//
2026-05-09 01:09:45 +02:00
// Direkt im body_scripts-Block — wenn das Script läuft, ist DOMContentLoaded
// häufig schon vorbei. Daher kein addEventListener('DOMContentLoaded', …)
// als Wrapper, sondern IIFE mit Idempotenz-Marker.
2026-05-09 01:02:43 +02:00
(function bindQuellenSearch() {
2026-05-09 01:09:45 +02:00
const btn = document.getElementById('quellen-q-btn');
if (btn & & !btn._quellenBound) {
btn._quellenBound = true;
btn.addEventListener('click', () => runSearch());
}
const input = document.getElementById('quellen-q');
if (input & & !input._quellenBound) {
input._quellenBound = true;
input.addEventListener('keydown', (ev) => {
if (ev.key === 'Enter') {
ev.preventDefault();
runSearch();
}
2026-05-09 00:58:30 +02:00
});
}
feat(quellen): Semantische Volltextsuche über alle Programme (#235)
GET /api/quellen/search?q=&filter=current|all&top_k=&bundesland=&partei=
nutzt text-embedding-v4 für wortunscharfe Suche (Endungen + Synonyme).
Filter:
- filter=current: nur Programme mit gueltig_bis IS NULL (Default)
- filter=all: auch historische Programme
Response liefert pro Treffer name, partei, bundesland, gueltig_ab/bis,
seite, gekürztes Snippet, similarity, plus pdf_url mit Direkt-Sprung
ins highlightete PDF (über /api/wahlprogramm-cite).
UI auf /quellen oben über der BL-Auflistung:
- Suchfeld + Submit
- Radio-Toggle "nur aktuelle Programme" / "auch historische"
- Treffer-Karten mit Partei-Badge, gültig-Pille (grün/grau),
Seite + Relevanz-%, Snippet, Direktlink ins PDF
- Filter-Wechsel triggert automatischen Re-Run
Smoketest dev: "Klimaschutz" liefert 13 Treffer in aktuellen Programmen
mit korrekter Similarity-Sortierung; "Solidarität" mischt Wahl- und
Grundsatzprogramme. Zugriff erfordert keinen Login (read-only).
2026-05-08 14:11:09 +02:00
document.querySelectorAll('input[name="qfilter"]').forEach(r => {
2026-05-09 01:02:43 +02:00
if (r._quellenBound) return;
r._quellenBound = true;
feat(quellen): Semantische Volltextsuche über alle Programme (#235)
GET /api/quellen/search?q=&filter=current|all&top_k=&bundesland=&partei=
nutzt text-embedding-v4 für wortunscharfe Suche (Endungen + Synonyme).
Filter:
- filter=current: nur Programme mit gueltig_bis IS NULL (Default)
- filter=all: auch historische Programme
Response liefert pro Treffer name, partei, bundesland, gueltig_ab/bis,
seite, gekürztes Snippet, similarity, plus pdf_url mit Direkt-Sprung
ins highlightete PDF (über /api/wahlprogramm-cite).
UI auf /quellen oben über der BL-Auflistung:
- Suchfeld + Submit
- Radio-Toggle "nur aktuelle Programme" / "auch historische"
- Treffer-Karten mit Partei-Badge, gültig-Pille (grün/grau),
Seite + Relevanz-%, Snippet, Direktlink ins PDF
- Filter-Wechsel triggert automatischen Re-Run
Smoketest dev: "Klimaschutz" liefert 13 Treffer in aktuellen Programmen
mit korrekter Similarity-Sortierung; "Solidarität" mischt Wahl- und
Grundsatzprogramme. Zugriff erfordert keinen Login (read-only).
2026-05-08 14:11:09 +02:00
r.addEventListener('change', () => {
const q = (document.getElementById('quellen-q') || {}).value || '';
if (q.trim().length >= 2) runSearch();
});
});
2026-05-09 01:02:43 +02:00
})();
feat(#139,#129,#138,#141): v2-Frontend (ECOnGOOD-CD), Login-Modal, Auto-DL, OG-Cards
v2-Frontend (#139, ECOnGOOD CD Manual Juni 2024):
- app/static/v2/: tokens.css, fonts.css, v2.css, Nunito-Sans woff2, Phosphor-Icons (21 SVGs)
- app/templates/v2/: base.html + 11 Screens + 8 Component-Macros
- AppShell mit Sidebar (Lesen/Pruefen/Daten/Admin), v2-Detail mit allen Features
(ScoreHero, MatrixMini, QuoteCard, Redline, Fraktions-Scores)
- v2 ist jetzt Default unter / — classic unter /classic
- Login-Modal in v2-Topbar mit Tabs Anmelden/Registrieren (#129)
- Phosphor-Icons in Sidebar + Topbar mit dynamischem Theme-Toggle
- Keyboard-Shortcuts (j/k/Enter/Esc/?/path), Landtag-Suche, Antrag-Historie,
Sort-Dropdown, Matrix-Feld-Info-Modal, Bookmarks/Comments/Voting/Share/Re-Analyze
Backend-Erweiterungen:
- main.py: ~30 neue Routes (/v2/*, /antrag/{ds}, /api/auth/{login,refresh,logout},
/api/me/merkliste/*, /api/admin/*, /v2/admin/*, OG-Cards, etc.)
- og_card.py + og_template: Open-Graph-Bilder via Playwright (#141)
- wahlprogramm_fetch.py + wahlprogramm-links.yaml: SHA-Gate Auto-DL (#138)
- auswertungen.py: BL-Filter + get_wahlperioden Helper (#137)
- auth.py: Direct-Access-Grant + Refresh-Token-Cookie
Classic-Updates:
- Header-DRY via _header.html, Auswertungen redirected, Batch-Inline raus
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:55:57 +02:00
< / script >
{% endblock %}