gwoe-antragspruefer/app/templates/v2/screens/aktuelle-themen.html

796 lines
36 KiB
HTML
Raw Normal View History

feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
{% extends "v2/base.html" %}
{% block title %}Aktuelle Themen — GWÖ-Antragsprüfer{% endblock %}
{% set v2_active_nav = "aktuelle-themen" %}
{% block head_extra %}
<script src="/static/chart.umd.min.js"></script>
<style>
.at-controls {
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 1rem;
font-family: var(--font-mono);
font-size: 11px;
}
.at-controls select, .at-controls input[type="number"] {
font-family: var(--font-mono);
font-size: 11px;
padding: 5px 8px;
border: 1px solid var(--ecg-border);
border-radius: 3px;
background: var(--ecg-card-bg);
color: var(--ecg-dark);
}
.at-controls button {
font-family: var(--font-mono);
font-size: 11px;
padding: 5px 12px;
border: 1px solid var(--ecg-border);
border-radius: 3px;
cursor: pointer;
background: var(--ecg-teal);
color: #fff;
}
.at-news-card {
background: var(--ecg-card-bg);
border: 1px solid var(--ecg-border);
border-radius: 6px;
padding: 14px 16px;
margin-bottom: 14px;
}
.at-news-head {
font-family: var(--font-mono);
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.05em;
opacity: 0.6;
margin-bottom: 4px;
}
.at-news-title {
font-family: var(--font-display);
font-size: 15px;
color: var(--ecg-teal);
margin: 0 0 6px;
line-height: 1.3;
}
.at-news-title a { color: inherit; text-decoration: none; }
.at-news-title a:hover { text-decoration: underline; }
.at-news-summary {
font-size: 12px;
line-height: 1.5;
margin: 0 0 10px;
opacity: 0.85;
}
.at-news-tags {
font-family: var(--font-mono);
font-size: 10px;
opacity: 0.55;
margin-bottom: 8px;
}
.at-tag {
display: inline-block;
padding: 1px 6px;
background: var(--ecg-bg-subtle);
border-radius: 3px;
margin-right: 4px;
}
.at-matches {
border-top: 1px solid var(--ecg-border);
margin-top: 10px;
padding-top: 10px;
}
.at-matches-label {
font-family: var(--font-mono);
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.05em;
opacity: 0.6;
margin-bottom: 6px;
}
.at-match {
display: flex;
align-items: center;
gap: 10px;
padding: 5px 0;
font-size: 12px;
border-bottom: 1px dotted var(--ecg-border);
}
.at-match:last-child { border-bottom: none; }
.at-tab { white-space: nowrap; }
@media (max-width: 600px) {
.at-tab { padding: 6px 10px !important; font-size: 10px !important; }
}
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
.at-score-pill {
display: inline-block;
padding: 1px 7px;
border-radius: 10px;
font-family: var(--font-mono);
font-size: 10px;
font-weight: 700;
background: var(--ecg-bg-subtle);
min-width: 28px;
text-align: center;
}
.at-score-pill.s-high { background: rgba(136,158,51,0.25); color: #44570a; }
.at-score-pill.s-mid { background: rgba(247,148,29,0.18); color: #875e10; }
.at-score-pill.s-low { background: rgba(200,0,0,0.15); color: #931515; }
.at-sim {
font-family: var(--font-mono);
font-size: 10px;
opacity: 0.5;
}
.at-presse-btn {
background: var(--ecg-card-bg);
color: var(--ecg-teal);
border: 1px solid var(--ecg-teal);
border-radius: 3px;
font-family: var(--font-mono);
font-size: 10px;
padding: 3px 8px;
cursor: pointer;
margin-left: auto;
}
.at-presse-btn:hover { background: var(--ecg-teal); color: #fff; }
</style>
{% endblock %}
{% block main %}
<div style="padding:0 0 1.5rem;">
<h1 style="font-family:var(--font-display);font-size:22px;color:var(--ecg-teal);margin:0 0 4px;">Aktuelle Themen</h1>
<p style="font-size:12px;font-family:var(--font-mono);color:var(--ecg-dark);opacity:0.6;">
Tagesschau + Bundestag-RSS · gematcht mit deinen Anträgen ·
Pressemitteilungs-Vorschläge
</p>
</div>
<div class="v2-kasten outline-blue" style="margin-bottom:1rem;">
<p style="font-size:12px;line-height:1.5;margin:0 0 0.5rem;">
Die täglich aktuellen politischen Top-Themen aus
<strong>öffentlich-rechtlichen + parlamentarischen Quellen</strong>
(Tagesschau-API + Bundestag-RSS) werden semantisch mit den von dir
bewerteten Anträgen verschnitten. Pro News-Artikel siehst du die
GWÖ-Bewertung der dazu passendsten Anträge — und kannst per Klick
eine Pressemitteilung generieren lassen.
</p>
<p style="font-size:11px;line-height:1.5;opacity:0.75;margin:0;">
Bewusst <strong>nicht</strong> verwendet: Quellen mit AI-Bann in
robots.txt (z.B. RND.de). Die UI zeigt nur Titel + URL + erste Sätze
— Volltexte werden nicht persistiert.
</p>
</div>
<div class="at-controls">
<label for="at-days">Zeitfenster:</label>
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
<select id="at-days" onchange="loadActiveTab()">
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
<option value="3">3 Tage</option>
<option value="7" selected>7 Tage</option>
<option value="14">14 Tage</option>
<option value="30">30 Tage</option>
</select>
<label for="at-topk">Top-N News:</label>
<input type="number" id="at-topk" value="50" min="3" max="200" style="width:60px;" onchange="loadActiveTab()" />
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
<label for="at-minsim">Min. Similarity:</label>
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
<select id="at-minsim" onchange="loadActiveTab()">
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
<option value="0.30">0.30 (locker)</option>
<option value="0.40" selected>0.40 (default)</option>
<option value="0.50">0.50 (streng)</option>
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
</select>
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
<label style="display:inline-flex;align-items:center;gap:5px;cursor:pointer;">
<input type="checkbox" id="at-only-relevant" checked onchange="loadActiveTab()" />
Nur GWÖ-relevant
</label>
<button onclick="loadActiveTab()">Aktualisieren</button>
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
</div>
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
<!-- Tabs -->
<div class="auswert-tabs" role="tablist" style="display:flex;gap:4px;margin:1.5rem 0 1rem;border-bottom:2px solid var(--ecg-border);padding-bottom:0;overflow-x:auto;-webkit-overflow-scrolling:touch;">
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
<button class="at-tab active" role="tab" data-tab="news" onclick="switchAtTab('news', this)" style="font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:0.06em;padding:6px 14px;border:none;background:none;cursor:pointer;color:var(--ecg-teal);opacity:1;border-bottom:2px solid var(--ecg-teal);margin-bottom:-2px;">News × Anträge</button>
<button class="at-tab" role="tab" data-tab="cluster" onclick="switchAtTab('cluster', this)" style="font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:0.06em;padding:6px 14px;border:none;background:none;cursor:pointer;color:var(--ecg-dark);opacity:0.55;border-bottom:2px solid transparent;margin-bottom:-2px;">Themen-Cluster</button>
<button class="at-tab" role="tab" data-tab="antraege" onclick="switchAtTab('antraege', this)" style="font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:0.06em;padding:6px 14px;border:none;background:none;cursor:pointer;color:var(--ecg-dark);opacity:0.55;border-bottom:2px solid transparent;margin-bottom:-2px;">GWÖ-Top-Anträge</button>
<button class="at-tab" role="tab" data-tab="zeitreihe" onclick="switchAtTab('zeitreihe', this)" style="font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:0.06em;padding:6px 14px;border:none;background:none;cursor:pointer;color:var(--ecg-dark);opacity:0.55;border-bottom:2px solid transparent;margin-bottom:-2px;">News-Volumen</button>
<button class="at-tab" role="tab" data-tab="drafts" onclick="switchAtTab('drafts', this)" style="font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:0.06em;padding:6px 14px;border:none;background:none;cursor:pointer;color:var(--ecg-dark);opacity:0.55;border-bottom:2px solid transparent;margin-bottom:-2px;">PM-Entwürfe</button>
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
</div>
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
<!-- Tab Panels -->
<div id="at-tab-news" class="at-panel">
<div id="at-news-banner" style="font-family:var(--font-mono);font-size:11px;opacity:0.65;margin-bottom:1rem;"></div>
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
<div id="at-themen-list">
<div style="font-family:var(--font-mono);font-size:12px;opacity:0.5;">Lade …</div>
</div>
</div>
<div id="at-tab-cluster" class="at-panel" style="display:none;">
<p style="font-size:11px;font-family:var(--font-mono);opacity:0.7;margin:0 0 0.5rem;">
News mit ähnlicher Thematik werden gebündelt — z.B. 4 Tagesschau- + 2 Bundestag-Artikel
zur gleichen Debatte ergeben einen Cluster mit gemeinsamem Antrags-Match.
</p>
<div id="at-cluster-list">
<div style="font-family:var(--font-mono);font-size:12px;opacity:0.5;">Lade …</div>
</div>
</div>
<div id="at-tab-antraege" class="at-panel" style="display:none;">
<p style="font-size:11px;font-family:var(--font-mono);opacity:0.7;margin:0 0 0.5rem;">
Reverse-Sicht: <strong>GWÖ-bewertete Anträge mit Score ≥ 8</strong>, sortiert nach
aktueller Pressewirkung. Anträge ohne News-Match werden gezeigt — als Hinweis
„Top-Antrag, aktuell ohne mediale Resonanz".
</p>
<div id="at-antraege-list">
<div style="font-family:var(--font-mono);font-size:12px;opacity:0.5;">Lade …</div>
</div>
</div>
<div id="at-tab-zeitreihe" class="at-panel" style="display:none;">
<h3 style="font-family:var(--font-display);font-size:14px;color:var(--ecg-teal);margin:0 0 0.5rem;">
News-Volumen pro Quelle (letzte 30 Tage)
</h3>
<div class="matrix-wrap" style="background:var(--ecg-card-bg);border:1px solid var(--ecg-border);border-radius:4px;padding:14px;">
<canvas id="at-zeitreihe-chart" style="max-height:320px;"></canvas>
</div>
<div id="at-zeitreihe-meta" class="meta-line" style="font-family:var(--font-mono);font-size:11px;opacity:0.6;margin:8px 0 1.5rem;"></div>
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
</div>
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
<div id="at-tab-drafts" class="at-panel" style="display:none;">
<p style="font-size:11px;font-family:var(--font-mono);opacity:0.7;margin:0 0 0.5rem;">
Bisher generierte Pressemitteilungs-Entwürfe (zuletzt generiert oben).
</p>
<div id="at-drafts-list">
<div style="font-family:var(--font-mono);font-size:12px;opacity:0.5;">Lade …</div>
</div>
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
</div>
<!-- Modal für Draft-Anzeige -->
<div class="v2-modal-backdrop" id="at-modal-backdrop" onclick="atCloseModal(event)" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:500;align-items:center;justify-content:center;">
<div class="v2-modal" onclick="event.stopPropagation()" style="background:var(--ecg-card-bg);border-radius:6px;padding:20px 24px;max-width:680px;width:90%;max-height:80vh;overflow-y:auto;position:relative;">
<button class="v2-modal-close" onclick="atCloseModal()" style="position:absolute;top:12px;right:14px;background:none;border:none;font-size:18px;cursor:pointer;opacity:0.5;">&times;</button>
<h2 id="at-modal-title" style="font-family:var(--font-display);font-size:16px;color:var(--ecg-teal);margin:0 0 12px;">Pressemitteilung</h2>
<div id="at-modal-body" style="font-size:13px;line-height:1.5;">Generiere …</div>
</div>
</div>
{% endblock %}
{% block body_scripts %}
<script>
let _atZeitreiheChart = null;
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
let _atActiveTab = 'news';
let _atSingleDate = null; // YYYY-MM-DD wenn aus Chart geklickt
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
function atScoreClass(score) {
if (score == null) return '';
if (score >= 7) return 's-high';
if (score >= 4) return 's-mid';
return 's-low';
}
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
function atRelevancePill(rel) {
if (!rel) return '';
const map = {
'high': {bg: 'rgba(136,158,51,0.30)', fg: '#3d4f0a', label: 'GWÖ-relevant'},
'mid': {bg: 'rgba(247,148,29,0.25)', fg: '#7a4a00', label: 'GWÖ-mittel'},
'low': {bg: 'rgba(150,150,150,0.25)', fg: '#555', label: 'schwach'},
'none': {bg: 'rgba(150,150,150,0.15)', fg: '#888', label: 'kein Match'},
};
const s = map[rel.level] || map.none;
return `<span style="display:inline-block;padding:2px 9px;border-radius:11px;font-family:var(--font-mono);font-size:10px;font-weight:700;background:${s.bg};color:${s.fg};margin-right:6px;">${s.label} · ${rel.score}</span>`;
}
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
function atFmtDatum(s) {
if (!s || s.length < 10) return '';
return s.slice(0, 10);
}
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
function switchAtTab(name, btn) {
_atActiveTab = name;
document.querySelectorAll('.at-tab').forEach(b => {
b.style.color = 'var(--ecg-dark)';
b.style.opacity = '0.55';
b.style.borderBottomColor = 'transparent';
});
btn.style.color = 'var(--ecg-teal)';
btn.style.opacity = '1';
btn.style.borderBottomColor = 'var(--ecg-teal)';
document.querySelectorAll('.at-panel').forEach(p => p.style.display = 'none');
document.getElementById('at-tab-' + name).style.display = 'block';
loadActiveTab();
}
function loadActiveTab() {
switch (_atActiveTab) {
case 'news': loadThemen(); break;
case 'cluster': loadCluster(); break;
case 'antraege': loadAntraege(); break;
case 'zeitreihe': loadZeitreihe(); break;
case 'drafts': loadDrafts(); break;
}
}
function clearDateFilter() {
_atSingleDate = null;
loadActiveTab();
}
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
async function loadThemen() {
const days = document.getElementById('at-days').value;
const topk = document.getElementById('at-topk').value;
const minsim = document.getElementById('at-minsim').value;
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
const onlyRel = document.getElementById('at-only-relevant').checked ? '1' : '0';
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
const list = document.getElementById('at-themen-list');
const banner = document.getElementById('at-news-banner');
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
list.innerHTML = '<div style="font-family:var(--font-mono);font-size:12px;opacity:0.5;">Lade …</div>';
try {
let url = `/api/aktuelle-themen/top?days=${days}&top_k=${topk}&min_similarity=${minsim}&matches_per_news=3&only_relevant=${onlyRel}`;
if (_atSingleDate) url += `&date=${encodeURIComponent(_atSingleDate)}`;
const r = await fetch(url);
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
const data = await r.json();
// Banner mit Anzeige-Info
let bannerHtml = '';
if (_atSingleDate) {
bannerHtml += `<span style="display:inline-block;padding:3px 10px;background:rgba(0,157,165,0.18);color:var(--ecg-teal);border-radius:11px;font-weight:700;margin-right:8px;">Tag: ${_atSingleDate}</span>`;
bannerHtml += `<button onclick="clearDateFilter()" style="font-family:var(--font-mono);font-size:11px;padding:2px 8px;border:1px solid var(--ecg-border);border-radius:3px;background:var(--ecg-card-bg);cursor:pointer;">× Tag-Filter entfernen</button>`;
bannerHtml += '<br>';
}
bannerHtml += `<strong>${data.n_shown || 0}</strong> News angezeigt · ${data.n_in_window || 0} News im Zeitraum (mit Embedding) · ${data.n_total_news || 0} insgesamt embedded`;
if (onlyRel === '1' && data.n_in_window > data.n_shown) {
bannerHtml += ` · <em>Filter "nur GWÖ-relevant" aktiv</em>`;
}
if (data.n_in_window > parseInt(topk)) {
bannerHtml += ` · top_k=${topk} ist limitierend, mehr News verfügbar`;
}
banner.innerHTML = bannerHtml;
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
if (!data.buckets || !data.buckets.length) {
list.innerHTML = '<div style="font-family:var(--font-mono);font-size:12px;opacity:0.5;">' + (
_atSingleDate ? `Keine News am ${_atSingleDate}` :
(onlyRel === '1' ? 'Keine News mit GWÖ-Relevanz im Zeitfenster — versuch Filter aus' : 'Keine News im Zeitfenster oder noch nicht embedded')
) + '.</div>';
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
return;
}
let html = '';
for (const b of data.buckets) {
const n = b.news;
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
const rel = b.relevance;
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
const tags = (n.tags || []).map(t => `<span class="at-tag">${t}</span>`).join('');
html += '<div class="at-news-card">';
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
html += `<div class="at-news-head">${atRelevancePill(rel)}${atFmtDatum(n.datum)} · ${n.source}${n.ressort ? ' / ' + n.ressort : ''}</div>`;
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
html += `<h4 class="at-news-title"><a href="${n.url}" target="_blank" rel="noopener">${n.titel}</a></h4>`;
if (n.summary) html += `<div class="at-news-summary">${n.summary}</div>`;
if (tags) html += `<div class="at-news-tags">${tags}</div>`;
if (b.matches && b.matches.length) {
html += '<div class="at-matches">';
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
html += `<div class="at-matches-label">Warum: ${rel.reason}</div>`;
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
for (const m of b.matches) {
const sc = m.gwoe_score != null ? m.gwoe_score.toFixed(1) : '—';
const fr = (m.fraktionen || []).join(', ');
html += '<div class="at-match">';
html += `<span class="at-score-pill ${atScoreClass(m.gwoe_score)}">${sc}</span>`;
html += `<a href="/antrag/${encodeURIComponent(m.drucksache)}" style="color:var(--ecg-teal);text-decoration:none;font-weight:500;">${m.drucksache}</a>`;
html += `<span style="opacity:0.85;">${m.title || ''}</span>`;
if (fr) html += `<span style="opacity:0.6;font-size:11px;">— ${fr}</span>`;
html += `<span class="at-sim">sim ${m.similarity}</span>`;
html += `<button class="at-presse-btn" onclick="generatePresse('${m.drucksache.replace(/'/g, "\\'")}', '${encodeURIComponent(n.url)}', this)">PM-Vorschlag</button>`;
html += '</div>';
}
html += '</div>';
} else {
html += '<div class="at-matches"><div class="at-matches-label">Keine GWÖ-bewerteten Anträge passen — wäre ein Kandidat für eine neue Bewertung.</div></div>';
}
html += '</div>';
}
list.innerHTML = html;
} catch (e) {
list.innerHTML = `<div style="color:#c00;font-family:var(--font-mono);font-size:12px;">Fehler: ${e}</div>`;
}
}
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
async function loadCluster() {
const days = document.getElementById('at-days').value;
const list = document.getElementById('at-cluster-list');
list.innerHTML = '<div style="font-family:var(--font-mono);font-size:12px;opacity:0.5;">Lade …</div>';
try {
const r = await fetch(`/api/aktuelle-themen/cluster?days=${days}&intra_threshold=0.55&min_cluster_size=2&antrag_threshold=0.4`);
const data = await r.json();
if (!data.clusters || !data.clusters.length) {
list.innerHTML = '<div style="font-family:var(--font-mono);font-size:12px;opacity:0.5;">Keine Themen-Cluster im Zeitfenster (jeder Cluster braucht mind. 2 inhaltlich ähnliche News).</div>';
return;
}
let html = '';
for (const c of data.clusters) {
html += '<div class="at-news-card">';
const tagPills = (c.top_tags || []).map(t => `<span class="at-tag">${t}</span>`).join('');
html += `<div class="at-news-head">Cluster · ${c.size} News · ${tagPills || '<em>keine Tags</em>'}</div>`;
html += '<div style="margin:6px 0;">';
for (const m of c.members.slice(0, 6)) {
html += `<div style="font-size:12px;line-height:1.5;"><span style="opacity:0.6;font-family:var(--font-mono);font-size:10px;">[${m.source}]</span> <a href="${m.url}" target="_blank" rel="noopener" style="color:var(--ecg-teal);text-decoration:none;">${m.titel}</a></div>`;
}
if (c.members.length > 6) {
html += `<div style="font-size:11px;opacity:0.6;">… und ${c.members.length - 6} weitere</div>`;
}
html += '</div>';
if (c.antrag_matches && c.antrag_matches.length) {
html += '<div class="at-matches"><div class="at-matches-label">Passende Anträge:</div>';
for (const m of c.antrag_matches) {
const sc = m.gwoe_score != null ? m.gwoe_score.toFixed(1) : '—';
const fr = (m.fraktionen || []).join(', ');
const firstNewsUrl = c.members[0].url;
html += '<div class="at-match">';
html += `<span class="at-score-pill ${atScoreClass(m.gwoe_score)}">${sc}</span>`;
html += `<a href="/antrag/${encodeURIComponent(m.drucksache)}" style="color:var(--ecg-teal);text-decoration:none;font-weight:500;">${m.drucksache}</a>`;
html += `<span style="opacity:0.85;">${m.title || ''}</span>`;
if (fr) html += `<span style="opacity:0.6;font-size:11px;">— ${fr}</span>`;
html += `<span class="at-sim">sim ${m.similarity}</span>`;
html += `<button class="at-presse-btn" onclick="generatePresse('${m.drucksache.replace(/'/g, "\\'")}', '${encodeURIComponent(firstNewsUrl)}', this)">PM-Vorschlag</button>`;
html += '</div>';
}
html += '</div>';
} else {
html += '<div class="at-matches"><div class="at-matches-label">Kein Antrag-Match — Themen-Cluster ohne GWÖ-Anker.</div></div>';
}
html += '</div>';
}
list.innerHTML = html;
} catch (e) {
list.innerHTML = `<div style="color:#c00;font-family:var(--font-mono);font-size:12px;">Fehler: ${e}</div>`;
}
}
async function loadAntraege() {
const days = document.getElementById('at-days').value;
const list = document.getElementById('at-antraege-list');
list.innerHTML = '<div style="font-family:var(--font-mono);font-size:12px;opacity:0.5;">Lade …</div>';
try {
const r = await fetch(`/api/aktuelle-themen/top-antraege?min_gwoe_score=8.0&days=${days}&min_similarity=0.4&top_k_news=5`);
const data = await r.json();
if (!data.antraege || !data.antraege.length) {
list.innerHTML = '<div style="font-family:var(--font-mono);font-size:12px;opacity:0.5;">Keine GWÖ-≥8-Anträge in der DB.</div>';
return;
}
let html = '';
for (const a of data.antraege) {
const sc = a.gwoe_score != null ? a.gwoe_score.toFixed(1) : '—';
const fr = (a.fraktionen || []).join(', ');
const newsBadge = a.news_count > 0
? `<span style="display:inline-block;padding:2px 9px;border-radius:11px;font-family:var(--font-mono);font-size:10px;font-weight:700;background:rgba(136,158,51,0.30);color:#3d4f0a;margin-right:6px;">${a.news_count} aktuelle News</span>`
: `<span style="display:inline-block;padding:2px 9px;border-radius:11px;font-family:var(--font-mono);font-size:10px;font-weight:700;background:rgba(150,150,150,0.20);color:#777;margin-right:6px;">keine News</span>`;
html += '<div class="at-news-card">';
html += `<div class="at-news-head">${newsBadge}<span class="at-score-pill ${atScoreClass(a.gwoe_score)}" style="margin-right:6px;">${sc}</span>${a.bundesland} · ${atFmtDatum(a.datum)}${fr ? ' · ' + fr : ''}</div>`;
html += `<h4 class="at-news-title"><a href="/antrag/${encodeURIComponent(a.drucksache)}" style="color:var(--ecg-teal);text-decoration:none;">${a.drucksache} — ${a.title || ''}</a></h4>`;
if (a.antrag_zusammenfassung) {
html += `<div class="at-news-summary">${a.antrag_zusammenfassung.slice(0, 200)}${a.antrag_zusammenfassung.length > 200 ? '…' : ''}</div>`;
}
if (a.top_news && a.top_news.length) {
html += '<div class="at-matches"><div class="at-matches-label">Aktuelle News:</div>';
for (const n of a.top_news) {
html += '<div class="at-match">';
html += `<span style="font-family:var(--font-mono);font-size:10px;opacity:0.6;">[${n.source}]</span>`;
html += `<a href="${n.url}" target="_blank" rel="noopener" style="color:var(--ecg-teal);text-decoration:none;">${n.titel}</a>`;
html += `<span class="at-sim">sim ${n.similarity}</span>`;
html += `<button class="at-presse-btn" onclick="generatePresse('${a.drucksache.replace(/'/g, "\\'")}', '${encodeURIComponent(n.url)}', this)">PM-Vorschlag</button>`;
html += '</div>';
}
html += '</div>';
} else {
html += '<div class="at-matches"><div class="at-matches-label">Keine aktuellen News passen — Top-Antrag wartet auf passende mediale Welle.</div></div>';
}
html += '</div>';
}
list.innerHTML = html;
} catch (e) {
list.innerHTML = `<div style="color:#c00;font-family:var(--font-mono);font-size:12px;">Fehler: ${e}</div>`;
}
}
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
async function loadZeitreihe() {
const meta = document.getElementById('at-zeitreihe-meta');
try {
const r = await fetch('/api/aktuelle-themen/zeitreihe?days=30');
const data = await r.json();
if (_atZeitreiheChart) _atZeitreiheChart.destroy();
if (!data.buckets || !data.buckets.length) {
meta.textContent = 'Noch keine News-Artikel in der DB.';
return;
}
const colors = ['rgba(0,157,165,0.7)', 'rgba(247,148,29,0.7)', 'rgba(136,158,51,0.7)',
'rgba(200,30,30,0.7)', 'rgba(150,100,200,0.7)'];
const datasets = data.sources.map((s, i) => ({
label: s,
data: data.series[s],
backgroundColor: colors[i % colors.length],
borderColor: colors[i % colors.length].replace('0.7', '1'),
fill: true,
tension: 0.2,
}));
const ctx = document.getElementById('at-zeitreihe-chart');
_atZeitreiheChart = new Chart(ctx, {
type: 'line',
data: { labels: data.buckets, datasets: datasets },
options: {
responsive: true,
onClick: (event, elements) => {
// Aus Chart.getElementsAtEventForMode: nehme das X-Achsen-Element
const points = _atZeitreiheChart.getElementsAtEventForMode(
event, 'index', { intersect: false }, false,
);
if (points.length > 0) {
const dayLabel = data.buckets[points[0].index];
if (dayLabel) {
_atSingleDate = dayLabel;
// Auf den News-Tab wechseln
const newsTabBtn = document.querySelector('.at-tab[data-tab="news"]');
if (newsTabBtn) newsTabBtn.click();
}
}
},
onHover: (event, elements) => {
event.native.target.style.cursor = elements.length > 0 ? 'pointer' : 'default';
},
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
scales: {
y: { beginAtZero: true, stacked: true, title: { display: true, text: 'Artikel/Tag' } },
x: { title: { display: true, text: 'Datum (klick öffnet den News-Tab gefiltert)' } },
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
},
plugins: {
legend: { position: 'bottom' }
}
}
});
const total = Object.values(data.series).reduce((s, arr) => s + arr.reduce((a, b) => a + b, 0), 0);
meta.innerHTML = `${total} News-Artikel über ${data.buckets.length} Tage, ${data.sources.length} Quellen. <strong>Klick auf einen Tag</strong> wechselt zum News-Tab gefiltert auf diesen Tag.`;
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
} catch (e) {
meta.textContent = 'Fehler: ' + e;
}
}
async function loadDrafts() {
const wrap = document.getElementById('at-drafts-list');
try {
const r = await fetch('/api/aktuelle-themen/drafts?limit=10');
const data = await r.json();
if (!data.drafts || !data.drafts.length) {
wrap.innerHTML = '<div style="font-family:var(--font-mono);font-size:12px;opacity:0.5;">Noch keine Pressemitteilungen generiert.</div>';
return;
}
let html = '';
for (const d of data.drafts) {
html += '<div class="at-news-card" style="cursor:pointer;" onclick="showDraft(' + d.id + ')">';
html += `<div class="at-news-head">${atFmtDatum(d.created_at)} · DS ${d.drucksache} (${d.bundesland})</div>`;
html += `<h4 class="at-news-title">${d.titel}</h4>`;
html += `<div class="at-news-tags">Bezug: ${d.news_titel}</div>`;
html += '</div>';
}
wrap.innerHTML = html;
} catch (e) {
wrap.innerHTML = `<div style="color:#c00;font-family:var(--font-mono);font-size:12px;">Fehler: ${e}</div>`;
}
}
async function generatePresse(drucksache, newsUrlEnc, btn) {
feat(#170 followup): PM-Generator Idempotenz + qwen-max + Wrapper-Verbesserungen User-Feedback nach Live-Test: **1. Idempotenz** — Pressemitteilungen wurden ungespeichert generiert, doppelter Klick erzeugte doppelten Draft + LLM-Kosten. - Neuer Helper `_find_existing_draft(drucksache, news_url)` der den neuesten Draft fuer das Paar zurueckgibt - `generate_draft()` prueft per Default zuerst den Lookup, liefert existing zurueck mit `_was_existing=True` (kein LLM-Call) - `force=True` Parameter fuer bewusste Neu-Generierung - Endpoint nimmt `?force=true` Query-Param entgegen - UI: Modal zeigt klar "Bestehender Entwurf vs Neu generiert" Banner, mit "Neu generieren"-Button im existing-Banner **2. Premium-Modell statt Default** — User wollte hoehere Sprachqualitaet ("Opus oder sowas"). Da das Projekt Qwen via DashScope nutzt (kein Anthropic), Wechsel auf `settings.llm_model_premium` (qwen-max). - Tradeoff: ~3× teurer (~6 Cent statt 2 Cent) und ~2× langsamer (~30 s statt 15 s) — aber spuerbare Qualitaetsverbesserung in Pressemitteilungs-Diktion - confirm-Dialog im Frontend nennt jetzt 6 Cent + 30 s **3. Wrapper-Verbesserungen** — `auto-fetch-news.sh` aufgeraeumt: - Container-Check (skip wenn down) analog zu run-digest.sh - START/END-Timestamps - Ausfuehrliche cron-install-Doku im Header - Auto-Backfill: wenn erster Run >= 100 Embeddings (Limit gehit), wird embed_pending_articles bis zu 500 weitere nachgeholt Tests: 5 neue (idempotency, force, _find_existing_draft × 3). Suite 1053 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:10:20 +02:00
if (!confirm(`Pressemitteilung für ${drucksache} anzeigen / generieren?\n\nFalls bereits ein Entwurf existiert, wird dieser ohne LLM-Call zurückgegeben.\nSonst wird mit qwen-max generiert (~6 Cent, ~30 s).`)) return;
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
btn.textContent = '…';
btn.disabled = true;
try {
const r = await fetch(`/api/aktuelle-themen/generate-presse?drucksache=${encodeURIComponent(drucksache)}&news_url=${newsUrlEnc}`, {
method: 'POST',
});
if (!r.ok) {
const err = await r.json();
alert('Fehler: ' + (err.detail || r.statusText));
btn.textContent = 'PM-Vorschlag';
btn.disabled = false;
return;
}
const data = await r.json();
showDraftFromData(data);
feat(#170 followup): PM-Generator Idempotenz + qwen-max + Wrapper-Verbesserungen User-Feedback nach Live-Test: **1. Idempotenz** — Pressemitteilungen wurden ungespeichert generiert, doppelter Klick erzeugte doppelten Draft + LLM-Kosten. - Neuer Helper `_find_existing_draft(drucksache, news_url)` der den neuesten Draft fuer das Paar zurueckgibt - `generate_draft()` prueft per Default zuerst den Lookup, liefert existing zurueck mit `_was_existing=True` (kein LLM-Call) - `force=True` Parameter fuer bewusste Neu-Generierung - Endpoint nimmt `?force=true` Query-Param entgegen - UI: Modal zeigt klar "Bestehender Entwurf vs Neu generiert" Banner, mit "Neu generieren"-Button im existing-Banner **2. Premium-Modell statt Default** — User wollte hoehere Sprachqualitaet ("Opus oder sowas"). Da das Projekt Qwen via DashScope nutzt (kein Anthropic), Wechsel auf `settings.llm_model_premium` (qwen-max). - Tradeoff: ~3× teurer (~6 Cent statt 2 Cent) und ~2× langsamer (~30 s statt 15 s) — aber spuerbare Qualitaetsverbesserung in Pressemitteilungs-Diktion - confirm-Dialog im Frontend nennt jetzt 6 Cent + 30 s **3. Wrapper-Verbesserungen** — `auto-fetch-news.sh` aufgeraeumt: - Container-Check (skip wenn down) analog zu run-digest.sh - START/END-Timestamps - Ausfuehrliche cron-install-Doku im Header - Auto-Backfill: wenn erster Run >= 100 Embeddings (Limit gehit), wird embed_pending_articles bis zu 500 weitere nachgeholt Tests: 5 neue (idempotency, force, _find_existing_draft × 3). Suite 1053 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:10:20 +02:00
if (!data._was_existing) loadDrafts(); // Nur bei NEU laden
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
} catch (e) {
alert('Fehler: ' + e);
} finally {
btn.textContent = 'PM-Vorschlag';
btn.disabled = false;
}
}
async function generateThread(drucksache, newsUrlEnc) {
if (!confirm(`Social-Thread (3-5 Posts) generieren?\n\nFalls bereits ein Thread für diese Drucksache+News existiert, wird dieser ohne LLM-Call zurückgegeben.\nSonst wird mit qwen-max generiert (~6 Cent, ~30 s).`)) return;
try {
const r = await fetch(`/api/aktuelle-themen/generate-presse?drucksache=${encodeURIComponent(drucksache)}&news_url=${newsUrlEnc}&style=thread`, {
method: 'POST',
});
if (!r.ok) {
const err = await r.json();
alert('Fehler: ' + (err.detail || r.statusText));
return;
}
const data = await r.json();
showDraftFromData(data);
if (!data._was_existing) loadDrafts();
} catch (e) {
alert('Fehler: ' + e);
}
}
feat(#170 followup): PM-Generator Idempotenz + qwen-max + Wrapper-Verbesserungen User-Feedback nach Live-Test: **1. Idempotenz** — Pressemitteilungen wurden ungespeichert generiert, doppelter Klick erzeugte doppelten Draft + LLM-Kosten. - Neuer Helper `_find_existing_draft(drucksache, news_url)` der den neuesten Draft fuer das Paar zurueckgibt - `generate_draft()` prueft per Default zuerst den Lookup, liefert existing zurueck mit `_was_existing=True` (kein LLM-Call) - `force=True` Parameter fuer bewusste Neu-Generierung - Endpoint nimmt `?force=true` Query-Param entgegen - UI: Modal zeigt klar "Bestehender Entwurf vs Neu generiert" Banner, mit "Neu generieren"-Button im existing-Banner **2. Premium-Modell statt Default** — User wollte hoehere Sprachqualitaet ("Opus oder sowas"). Da das Projekt Qwen via DashScope nutzt (kein Anthropic), Wechsel auf `settings.llm_model_premium` (qwen-max). - Tradeoff: ~3× teurer (~6 Cent statt 2 Cent) und ~2× langsamer (~30 s statt 15 s) — aber spuerbare Qualitaetsverbesserung in Pressemitteilungs-Diktion - confirm-Dialog im Frontend nennt jetzt 6 Cent + 30 s **3. Wrapper-Verbesserungen** — `auto-fetch-news.sh` aufgeraeumt: - Container-Check (skip wenn down) analog zu run-digest.sh - START/END-Timestamps - Ausfuehrliche cron-install-Doku im Header - Auto-Backfill: wenn erster Run >= 100 Embeddings (Limit gehit), wird embed_pending_articles bis zu 500 weitere nachgeholt Tests: 5 neue (idempotency, force, _find_existing_draft × 3). Suite 1053 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:10:20 +02:00
async function regeneratePresse(drucksache, newsUrlEnc) {
if (!confirm(`Wirklich neu generieren?\n\nDas macht einen NEUEN LLM-Call (~6 Cent, ~30 s) und legt einen weiteren Draft an.`)) return;
try {
const r = await fetch(`/api/aktuelle-themen/generate-presse?drucksache=${encodeURIComponent(drucksache)}&news_url=${newsUrlEnc}&force=true`, {
method: 'POST',
});
if (!r.ok) {
const err = await r.json();
alert('Fehler: ' + (err.detail || r.statusText));
return;
}
const data = await r.json();
showDraftFromData(data);
loadDrafts();
} catch (e) {
alert('Fehler: ' + e);
}
}
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
async function showDraftFromData(d) {
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
const backdrop = document.getElementById('at-modal-backdrop');
document.getElementById('at-modal-title').textContent = d.titel;
feat(#170 followup): PM-Generator Idempotenz + qwen-max + Wrapper-Verbesserungen User-Feedback nach Live-Test: **1. Idempotenz** — Pressemitteilungen wurden ungespeichert generiert, doppelter Klick erzeugte doppelten Draft + LLM-Kosten. - Neuer Helper `_find_existing_draft(drucksache, news_url)` der den neuesten Draft fuer das Paar zurueckgibt - `generate_draft()` prueft per Default zuerst den Lookup, liefert existing zurueck mit `_was_existing=True` (kein LLM-Call) - `force=True` Parameter fuer bewusste Neu-Generierung - Endpoint nimmt `?force=true` Query-Param entgegen - UI: Modal zeigt klar "Bestehender Entwurf vs Neu generiert" Banner, mit "Neu generieren"-Button im existing-Banner **2. Premium-Modell statt Default** — User wollte hoehere Sprachqualitaet ("Opus oder sowas"). Da das Projekt Qwen via DashScope nutzt (kein Anthropic), Wechsel auf `settings.llm_model_premium` (qwen-max). - Tradeoff: ~3× teurer (~6 Cent statt 2 Cent) und ~2× langsamer (~30 s statt 15 s) — aber spuerbare Qualitaetsverbesserung in Pressemitteilungs-Diktion - confirm-Dialog im Frontend nennt jetzt 6 Cent + 30 s **3. Wrapper-Verbesserungen** — `auto-fetch-news.sh` aufgeraeumt: - Container-Check (skip wenn down) analog zu run-digest.sh - START/END-Timestamps - Ausfuehrliche cron-install-Doku im Header - Auto-Backfill: wenn erster Run >= 100 Embeddings (Limit gehit), wird embed_pending_articles bis zu 500 weitere nachgeholt Tests: 5 neue (idempotency, force, _find_existing_draft × 3). Suite 1053 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:10:20 +02:00
const isExisting = d._was_existing === true;
const newsUrlEnc = encodeURIComponent(d.news_url);
const dsEnc = d.drucksache.replace(/'/g, "\\'");
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
// Versionen abfragen — falls >1, Dropdown anzeigen
let versionsHtml = '';
try {
const vr = await fetch(`/api/aktuelle-themen/drafts-versions?drucksache=${encodeURIComponent(d.drucksache)}&news_url=${newsUrlEnc}`);
const vd = await vr.json();
if (vd.versions && vd.versions.length > 1) {
versionsHtml = '<select onchange="loadVersion(this.value)" style="margin-left:8px;font-family:var(--font-mono);font-size:10px;padding:2px 6px;">';
for (const v of vd.versions) {
const sel = (v.id === d.id) ? ' selected' : '';
versionsHtml += `<option value="${v.id}"${sel}>v${v.id} — ${(v.created_at || '').slice(0,16)} (${v.model})</option>`;
}
versionsHtml += '</select>';
}
} catch (e) { /* silent */ }
const styleLabel = (d.style === 'thread') ? '🐦 Thread (3-5 Posts)' : '📰 Klassische PM';
const otherStyle = (d.style === 'thread') ? 'pm' : 'thread';
const otherLabel = (otherStyle === 'thread') ? 'Auch als Thread' : 'Auch als PM';
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
const banner = isExisting
? `<div style="font-family:var(--font-mono);font-size:10px;opacity:0.85;background:rgba(247,148,29,0.18);padding:6px 8px;border-radius:3px;margin-bottom:8px;">
Bestehender Entwurf vom ${(d.created_at || '').slice(0,10)} · ${styleLabel} · Modell: ${d.model || '—'} · kein LLM-Call
feat(#170 followup): PM-Generator Idempotenz + qwen-max + Wrapper-Verbesserungen User-Feedback nach Live-Test: **1. Idempotenz** — Pressemitteilungen wurden ungespeichert generiert, doppelter Klick erzeugte doppelten Draft + LLM-Kosten. - Neuer Helper `_find_existing_draft(drucksache, news_url)` der den neuesten Draft fuer das Paar zurueckgibt - `generate_draft()` prueft per Default zuerst den Lookup, liefert existing zurueck mit `_was_existing=True` (kein LLM-Call) - `force=True` Parameter fuer bewusste Neu-Generierung - Endpoint nimmt `?force=true` Query-Param entgegen - UI: Modal zeigt klar "Bestehender Entwurf vs Neu generiert" Banner, mit "Neu generieren"-Button im existing-Banner **2. Premium-Modell statt Default** — User wollte hoehere Sprachqualitaet ("Opus oder sowas"). Da das Projekt Qwen via DashScope nutzt (kein Anthropic), Wechsel auf `settings.llm_model_premium` (qwen-max). - Tradeoff: ~3× teurer (~6 Cent statt 2 Cent) und ~2× langsamer (~30 s statt 15 s) — aber spuerbare Qualitaetsverbesserung in Pressemitteilungs-Diktion - confirm-Dialog im Frontend nennt jetzt 6 Cent + 30 s **3. Wrapper-Verbesserungen** — `auto-fetch-news.sh` aufgeraeumt: - Container-Check (skip wenn down) analog zu run-digest.sh - START/END-Timestamps - Ausfuehrliche cron-install-Doku im Header - Auto-Backfill: wenn erster Run >= 100 Embeddings (Limit gehit), wird embed_pending_articles bis zu 500 weitere nachgeholt Tests: 5 neue (idempotency, force, _find_existing_draft × 3). Suite 1053 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:10:20 +02:00
<button type="button" onclick="regeneratePresse('${dsEnc}', '${newsUrlEnc}')" style="margin-left:8px;font-family:var(--font-mono);font-size:10px;padding:2px 8px;border:1px solid var(--ecg-border);border-radius:3px;background:var(--ecg-card-bg);cursor:pointer;">Neu generieren</button>
<button type="button" onclick="generate${otherStyle === 'thread' ? 'Thread' : 'Presse'}('${dsEnc}', '${newsUrlEnc}', this)" style="margin-left:4px;font-family:var(--font-mono);font-size:10px;padding:2px 8px;border:1px solid var(--ecg-border);border-radius:3px;background:var(--ecg-card-bg);cursor:pointer;">${otherLabel}</button>
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
${versionsHtml}
feat(#170 followup): PM-Generator Idempotenz + qwen-max + Wrapper-Verbesserungen User-Feedback nach Live-Test: **1. Idempotenz** — Pressemitteilungen wurden ungespeichert generiert, doppelter Klick erzeugte doppelten Draft + LLM-Kosten. - Neuer Helper `_find_existing_draft(drucksache, news_url)` der den neuesten Draft fuer das Paar zurueckgibt - `generate_draft()` prueft per Default zuerst den Lookup, liefert existing zurueck mit `_was_existing=True` (kein LLM-Call) - `force=True` Parameter fuer bewusste Neu-Generierung - Endpoint nimmt `?force=true` Query-Param entgegen - UI: Modal zeigt klar "Bestehender Entwurf vs Neu generiert" Banner, mit "Neu generieren"-Button im existing-Banner **2. Premium-Modell statt Default** — User wollte hoehere Sprachqualitaet ("Opus oder sowas"). Da das Projekt Qwen via DashScope nutzt (kein Anthropic), Wechsel auf `settings.llm_model_premium` (qwen-max). - Tradeoff: ~3× teurer (~6 Cent statt 2 Cent) und ~2× langsamer (~30 s statt 15 s) — aber spuerbare Qualitaetsverbesserung in Pressemitteilungs-Diktion - confirm-Dialog im Frontend nennt jetzt 6 Cent + 30 s **3. Wrapper-Verbesserungen** — `auto-fetch-news.sh` aufgeraeumt: - Container-Check (skip wenn down) analog zu run-digest.sh - START/END-Timestamps - Ausfuehrliche cron-install-Doku im Header - Auto-Backfill: wenn erster Run >= 100 Embeddings (Limit gehit), wird embed_pending_articles bis zu 500 weitere nachgeholt Tests: 5 neue (idempotency, force, _find_existing_draft × 3). Suite 1053 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:10:20 +02:00
</div>`
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
: `<div style="font-family:var(--font-mono);font-size:10px;opacity:0.85;background:rgba(136,158,51,0.18);padding:6px 8px;border-radius:3px;margin-bottom:8px;">
Neu generiert · ${styleLabel} · Modell: ${d.model || '—'} ${versionsHtml}
feat(#170 followup): PM-Generator Idempotenz + qwen-max + Wrapper-Verbesserungen User-Feedback nach Live-Test: **1. Idempotenz** — Pressemitteilungen wurden ungespeichert generiert, doppelter Klick erzeugte doppelten Draft + LLM-Kosten. - Neuer Helper `_find_existing_draft(drucksache, news_url)` der den neuesten Draft fuer das Paar zurueckgibt - `generate_draft()` prueft per Default zuerst den Lookup, liefert existing zurueck mit `_was_existing=True` (kein LLM-Call) - `force=True` Parameter fuer bewusste Neu-Generierung - Endpoint nimmt `?force=true` Query-Param entgegen - UI: Modal zeigt klar "Bestehender Entwurf vs Neu generiert" Banner, mit "Neu generieren"-Button im existing-Banner **2. Premium-Modell statt Default** — User wollte hoehere Sprachqualitaet ("Opus oder sowas"). Da das Projekt Qwen via DashScope nutzt (kein Anthropic), Wechsel auf `settings.llm_model_premium` (qwen-max). - Tradeoff: ~3× teurer (~6 Cent statt 2 Cent) und ~2× langsamer (~30 s statt 15 s) — aber spuerbare Qualitaetsverbesserung in Pressemitteilungs-Diktion - confirm-Dialog im Frontend nennt jetzt 6 Cent + 30 s **3. Wrapper-Verbesserungen** — `auto-fetch-news.sh` aufgeraeumt: - Container-Check (skip wenn down) analog zu run-digest.sh - START/END-Timestamps - Ausfuehrliche cron-install-Doku im Header - Auto-Backfill: wenn erster Run >= 100 Embeddings (Limit gehit), wird embed_pending_articles bis zu 500 weitere nachgeholt Tests: 5 neue (idempotency, force, _find_existing_draft × 3). Suite 1053 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:10:20 +02:00
</div>`;
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
// Action-Buttons: Mail + Clipboard
const mailtoBody = encodeURIComponent(d.body + '\n\n— Bezug: ' + d.news_titel + ' (' + d.news_url + ')');
const mailtoSubject = encodeURIComponent(d.titel);
const mailto = `mailto:?subject=${mailtoSubject}&body=${mailtoBody}`;
const isMailtoTooLong = mailto.length > 1900;
const actionRow = `<div style="display:flex;gap:8px;margin:10px 0 12px;flex-wrap:wrap;">
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
${isMailtoTooLong ? '<span style="font-family:var(--font-mono);font-size:10px;opacity:0.6;">PM zu lang für Mail-Link — Clipboard nutzen.</span>'
: `<a href="${mailto}" style="font-family:var(--font-mono);font-size:11px;padding:5px 12px;border:1px solid var(--ecg-border);border-radius:3px;background:var(--ecg-card-bg);color:var(--ecg-dark);text-decoration:none;">📧 Per Mail versenden</a>`}
<button type="button" data-pm-id="${d.id}" onclick="copyDraftToClipboard(this)" style="font-family:var(--font-mono);font-size:11px;padding:5px 12px;border:1px solid var(--ecg-border);border-radius:3px;background:var(--ecg-card-bg);color:var(--ecg-dark);cursor:pointer;">📋 In Zwischenablage kopieren</button>
<a href="/api/aktuelle-themen/drafts/pdf/${d.id}" target="_blank" rel="noopener" style="font-family:var(--font-mono);font-size:11px;padding:5px 12px;border:1px solid var(--ecg-border);border-radius:3px;background:var(--ecg-card-bg);color:var(--ecg-dark);text-decoration:none;">📄 PDF</a>
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
</div>`;
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
document.getElementById('at-modal-body').innerHTML =
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
banner +
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
`<div style="font-family:var(--font-mono);font-size:11px;opacity:0.6;margin-bottom:10px;">
DS ${d.drucksache} (${d.bundesland}) · Bezug zu: <a href="${d.news_url}" target="_blank" rel="noopener" style="color:var(--ecg-teal);">${d.news_titel}</a>
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
</div>` +
actionRow +
feat: Stand-Dashboard, Score-Histogram, PM-Markdown, Live-Polling, Cluster-Indicator Sechs zusammengehoerige UX/Performance-Erweiterungen: **1. /v2/admin/stand — System-Stand-Dashboard** KPI-Kacheln (Bewertungen, Plenum-Votes, Match, Vote-Orphans, News, PM- Drafts, Bookmarks) + GWÖ-Score-Histogram + Per-BL-Tabelle + News-Source- Tabelle. Auto-Refresh 30 s. Endpoint /api/admin/stand liefert alles in einem Roundtrip. Nav-Eintrag "Stand" in der Admin-Sektion. **2. /auswertungen Score-Histogram-Tab** 4. Tab "Score-Verteilung" mit Bar-Chart 0–10. Endpoint /api/auswertungen/score-histogram liefert Buckets, optional gefiltert nach Bundesland + Wahlperiode. Reagiert auf den globalen BL-Filter. **3. PM-Body Markdown-Rendering** Mini-Renderer im Modal: **bold** / __bold__ / *italic* / _italic_ / - list-bullets / Doppel-Newline-Paragraphen. Kein externer Markdown- Parser, keine neue Dependency. Body wird HTML-escaped, Patterns dann zu Tags umgesetzt. **4. Performance-Cache fuer themen_matching** TTL-Cache (60 s) fuer aggregate_top_themen und aggregate_news_cluster. Cache-Key inkl. aller Filter-Parameter. Automatische Invalidation in news_aggregator.run_aggregator nach erfolgreichem Insert/Embed. 4 neue Tests fuer cache_get/set/clear-Verhalten. **5. Stimmverhalten Banner Live-Update** Statt setTimeout(800) jetzt pollQueueUntilDrained: alle 4 s GET /api/queue/status, Banner zeigt pending + elapsed live. Bei pending=0 zwei Polls in Folge: Banner + Stimmverhalten-Charts neu laden. Max 5 Min Polling-Timeout. Bricht ab wenn Tab gewechselt wird. **6. Antrag-Detail Cluster-Indicator** News-Match-Box im Antrag-Detail laedt parallel /aktuelle-themen/cluster und mappt URL → Cluster. Pro News-Card ein "🔗 Cluster (N News)"-Badge mit Hover-Tooltip der anderen Cluster-Members. Macht thematische Bündel sichtbar, ohne Pop-Out auf den Cluster-Tab. Suite: 1088 → 1092 grün (4 Cache-Tests). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 02:49:06 +02:00
`<div style="font-size:13px;line-height:1.6;">${renderPmBody(d.body)}</div>`;
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
backdrop.style.display = 'flex';
}
feat: Stand-Dashboard, Score-Histogram, PM-Markdown, Live-Polling, Cluster-Indicator Sechs zusammengehoerige UX/Performance-Erweiterungen: **1. /v2/admin/stand — System-Stand-Dashboard** KPI-Kacheln (Bewertungen, Plenum-Votes, Match, Vote-Orphans, News, PM- Drafts, Bookmarks) + GWÖ-Score-Histogram + Per-BL-Tabelle + News-Source- Tabelle. Auto-Refresh 30 s. Endpoint /api/admin/stand liefert alles in einem Roundtrip. Nav-Eintrag "Stand" in der Admin-Sektion. **2. /auswertungen Score-Histogram-Tab** 4. Tab "Score-Verteilung" mit Bar-Chart 0–10. Endpoint /api/auswertungen/score-histogram liefert Buckets, optional gefiltert nach Bundesland + Wahlperiode. Reagiert auf den globalen BL-Filter. **3. PM-Body Markdown-Rendering** Mini-Renderer im Modal: **bold** / __bold__ / *italic* / _italic_ / - list-bullets / Doppel-Newline-Paragraphen. Kein externer Markdown- Parser, keine neue Dependency. Body wird HTML-escaped, Patterns dann zu Tags umgesetzt. **4. Performance-Cache fuer themen_matching** TTL-Cache (60 s) fuer aggregate_top_themen und aggregate_news_cluster. Cache-Key inkl. aller Filter-Parameter. Automatische Invalidation in news_aggregator.run_aggregator nach erfolgreichem Insert/Embed. 4 neue Tests fuer cache_get/set/clear-Verhalten. **5. Stimmverhalten Banner Live-Update** Statt setTimeout(800) jetzt pollQueueUntilDrained: alle 4 s GET /api/queue/status, Banner zeigt pending + elapsed live. Bei pending=0 zwei Polls in Folge: Banner + Stimmverhalten-Charts neu laden. Max 5 Min Polling-Timeout. Bricht ab wenn Tab gewechselt wird. **6. Antrag-Detail Cluster-Indicator** News-Match-Box im Antrag-Detail laedt parallel /aktuelle-themen/cluster und mappt URL → Cluster. Pro News-Card ein "🔗 Cluster (N News)"-Badge mit Hover-Tooltip der anderen Cluster-Members. Macht thematische Bündel sichtbar, ohne Pop-Out auf den Cluster-Tab. Suite: 1088 → 1092 grün (4 Cache-Tests). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 02:49:06 +02:00
// Mini-Markdown-Renderer fuer PM-Body — interpretiert **bold**, *italic*,
// __bold__, _italic_ + Listen + Paragraphen-Breaks. KEIN externer Markdown-
// Parser noetig (kein dependency, schnell, isoliert testbar).
function renderPmBody(body) {
if (!body) return '';
// 1. HTML escapen
let s = body.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
// 2. **bold** + __bold__
s = s.replace(/\*\*([^*\n]+?)\*\*/g, '<strong>$1</strong>');
s = s.replace(/__([^_\n]+?)__/g, '<strong>$1</strong>');
// 3. *italic* + _italic_ (vorsichtig — nur wenn nicht zw. Ziffern)
s = s.replace(/(?<![*\w])\*([^*\n]+?)\*(?![*\w])/g, '<em>$1</em>');
s = s.replace(/(?<![_\w])_([^_\n]+?)_(?![_\w])/g, '<em>$1</em>');
// 4. Listen: "- " oder "* " am Zeilenanfang → Bullet-Punkte
// (vorsichtig: Bindestrich mitten im Wort soll bleiben)
const lines = s.split('\n');
const out = [];
let inList = false;
for (const line of lines) {
if (/^\s*[-*]\s+/.test(line)) {
if (!inList) { out.push('<ul style="margin:0.5em 0;padding-left:1.4em;">'); inList = true; }
out.push('<li>' + line.replace(/^\s*[-*]\s+/, '') + '</li>');
} else {
if (inList) { out.push('</ul>'); inList = false; }
out.push(line);
}
}
if (inList) out.push('</ul>');
s = out.join('\n');
// 5. Doppel-Newlines → </p><p>
const paras = s.split(/\n\s*\n/);
return paras.map(p => {
const trimmed = p.trim();
if (!trimmed) return '';
// Wenn der Block schon mit Tag startet (z.B. <ul>), kein <p>-Wrap
if (trimmed.startsWith('<')) return trimmed;
return '<p style="margin:0 0 0.9em;">' + trimmed.replace(/\n/g, '<br>') + '</p>';
}).filter(Boolean).join('\n');
}
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
async function loadVersion(draftId) {
try {
const r = await fetch(`/api/aktuelle-themen/drafts/${draftId}`);
const d = await r.json();
showDraftFromData(d);
} catch (e) { alert('Fehler: ' + e); }
}
async function copyDraftToClipboard(btn) {
// Daten aus dem Modal-State holen — vermeidet HTML-Attribut-Quoting-
// Probleme mit JSON.stringify (siehe ADR 0011 Folge-Erkenntnis).
const draftId = btn.dataset.pmId;
let titel = '', body = '';
try {
const r = await fetch(`/api/aktuelle-themen/drafts/${draftId}`);
if (r.ok) {
const d = await r.json();
titel = d.titel || '';
body = d.body || '';
}
} catch (_) {}
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
const text = titel + '\n\n' + body;
try {
await navigator.clipboard.writeText(text);
const orig = btn.textContent;
btn.textContent = '✓ kopiert';
setTimeout(() => { btn.textContent = orig; }, 1800);
} catch (e) {
alert('Clipboard-Fehler: ' + e + '\n\nText:\n' + text);
}
}
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
async function showDraft(id) {
try {
const r = await fetch(`/api/aktuelle-themen/drafts/${id}`);
const d = await r.json();
showDraftFromData(d);
} catch (e) {
alert('Fehler: ' + e);
}
}
function atCloseModal(ev) {
if (!ev || ev.target.id === 'at-modal-backdrop') {
document.getElementById('at-modal-backdrop').style.display = 'none';
}
}
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') document.getElementById('at-modal-backdrop').style.display = 'none';
});
feat(#170 followup 2): Pre-Filter, Cluster, Antrags-Initiative, PM-Versionierung, Mail-Link User-Feedback: Aktuelle-Themen-Dashboard war "Detective-Modus" — durch viele News scrollen, Match-Stärke selbst interpretieren. Komplett-Refactor zur kuratierten Sicht mit Tabs. **1. Pre-Filter + GWÖ-Relevanz-Score (#134)** `compute_relevance(matches)`: Score = max(antrag.gwoe_score × similarity). Level: high (≥4.0) / mid (≥2.5) / low (>0) / none. Pro News in der UI ein farbiger Pill (gruen/orange/grau) + Reason-Text: "GWÖ-9.0/10-Antrag „Klimaschutzgesetz" (GRÜNE) passt mit Similarity 0.55." Default-Filter "Nur GWÖ-relevant" aktiv (only_relevant=true) — zeigt nur high/mid News, blendet Rauschen aus. Toggle-Checkbox. `/api/aktuelle-themen/top` neuer Param `only_relevant=true|false`. **2. PM-Versionierung im Modal (#135)** `list_drafts_for(drucksache, news_url)`: alle Versionen, neueste oben. Endpoint `/api/aktuelle-themen/drafts-versions`. Modal zeigt Dropdown wenn >1 Version, Switch ohne LLM-Call. Force-Regen bleibt als Button im "bestehender Entwurf"-Banner. **3. News-Cluster-View (#136)** `aggregate_news_cluster(intra_threshold=0.55, min_cluster_size=2)`: Greedy-Embedding-Cluster + zentralster Antrags-Match per Centroid- Vektor. Zweiter Tab "Themen-Cluster": 5 News über "Pflege" → 1 Cluster mit gemeinsamem Antrag-Vorschlag, statt 5 separate Cards. Endpoint: `/api/aktuelle-themen/cluster`. **4. Mail-Direkt-Link + Clipboard (#137)** Im PM-Modal zwei Buttons: - "📧 Per Mail versenden" (mailto: mit subject + body, ~1900 Char Limit) - "📋 In Zwischenablage kopieren" (navigator.clipboard.writeText) - Bei langem PM (>1900 Char): mailto-Link wird ausgegraut, Hinweis "PM zu lang für Mail-Link — Clipboard nutzen" **5. Antrags-Initiative (#138)** `aggregate_top_antraege_with_news(min_gwoe_score=8.0, days=14)`: Reverse-Sicht — pro Antrag mit GWÖ ≥ 8 die News-Resonanz. Antraege ohne Match werden trotzdem angezeigt mit "keine News"-Pill. Dritter Tab "GWÖ-Top-Anträge". Endpoint `.../top-antraege`. **UI-Restrukturierung:** statt einer langen Scroll-Liste jetzt 5 Tabs mit gemeinsamer Filter-Bar: - News × Anträge (Default, kuratiert via Pre-Filter) - Themen-Cluster (Bündel ähnlicher News) - GWÖ-Top-Anträge (Reverse) - News-Volumen (Chart) - PM-Entwürfe (Drafts-Liste) Default min_similarity 0.40 → 0.50 erhoeht (weniger Rauschen). Tests: 14 neue (compute_relevance × 5, only_relevant + sort × 3, cluster × 3, top_antraege × 3). Suite 1067 gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:41:31 +02:00
// Init: News-Tab als Default
loadActiveTab();
feat(#170): Aktuelle-Themen-Dashboard — News × Anträge × Pressemitteilungen Vollständiges 4-Phasen-Feature: **Phase 1 — News-Aggregator** (`app/news_aggregator.py`) - Tagesschau-API (`/api2u/news?ressort=...`) für inland/ausland/wirtschaft/wissen - Bundestag-RSS für aktuellethemen / pressemitteilungen / hib - DB-Tabelle `news_articles` (URL-PK, idempotent) - Embeddings via existierender qwen-v4-Pipeline - Cron-Script `scripts/auto-fetch-news.sh` - Bewusst NICHT: RND.de (robots.txt bannt explizit ClaudeBot, GPTBot, CCBot, ChatGPT-User, Google-Extended). Nur AI-erlaubende, öffentlich- rechtliche/parlamentarische Quellen - Volltexte werden NICHT persistiert (nur Titel + erster Satz) **Phase 2 — Themen × Anträge Matching** (`app/themen_matching.py`) - News-Embedding × Assessment-summary_embedding via Cosine-Similarity - `find_anträge_for_news`: pro News die Top-K passenden Anträge - `find_news_for_antrag`: pro Antrag Top-K News mit Datums-Fenster (90d) - `aggregate_top_themen`: primärer Dashboard-Endpoint - `aggregate_themen_zeitreihe`: News-Volumen pro Tag × Source **Phase 3 — Dashboard-View** (`/aktuelle-themen`) - Neuer linker Nav-Eintrag „Aktuelle Themen" - Stacked-Area-Chart News-Volumen pro Quelle (30d) - Pro News-Card: Titel + Summary + Tags + Top-3-Antrags-Match-Liste mit GWÖ-Score-Pill, Drucksache-Link, PM-Vorschlag-Button - Filter: Zeitfenster, Top-N, min_similarity - Auth-protected (require_auth) **Phase 4 — Pressemitteilungs-Generator** (`app/presse_generator.py`) - LLM-Prompt-Template (200-250 Worte, GWÖ-Sicht, JSON-Output) - Reuse von `QwenBewerter` aus app/adapters/qwen_bewerter.py - DB-Tabelle `presse_drafts` (Persistenz) - POST `/api/aktuelle-themen/generate-presse` rate-limited 5/min, auth-only (LLM-Kosten) - GET `/api/aktuelle-themen/drafts` + `/drafts/{id}` für Liste/Detail - Manueller Trigger via UI-Button, kein Auto-Versand - Modal-Anzeige des generierten Texts **Compliance:** - robots.txt-respektierend (ClaudeBot-Bann von RND vermieden, AI- erlaubende Quellen verwendet) - UI zeigt nur Titel+URL+Datum+erster Satz, keine Volltext-Reproduktion - Pressemitteilungen sind explizit Drafts, nicht Auto-Versand - LLM-Calls rate-limited, auth-only **Tests:** 43 neue Tests (19 news_aggregator + 16 themen_matching + 8 presse_generator). Suite jetzt 1048 grün. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:39:36 +02:00
</script>
{% endblock %}