feat(#177 Phase 13): Matrix-Coloring + Programm-Treue mit Begründung

- **Matrix-Coloring konsistent**: Symbol wird jetzt aus rating
  abgeleitet (rating_symbol-Macro) statt vom LLM übernommen. Bisher
  wurde z.B. rating=4 + symbol="+" geliefert → Template zeigte "+"
  aber mit m-pp-Klasse (kräftiges Grün) → "++/+ wirkten gleichfarbig".
  Stichprobe: 7/30 Assessments hatten rating/symbol-Mismatch.
- **„Antragsteller:in" / „Regierungsfraktion"** als Pill ausgeschrieben
  statt 1-Buchstabe-Badges A/R.
- **Programm-Treue mit Begründung sichtbar**: Wahlprogramm- und
  Parteiprogramm-Begründung als Block direkt unter den Score-Chips.
  Vorher nur Tooltip — auf Mobil schwer zugänglich.
- **„Redline" → „Verbesserungsvorschläge"** in beiden Heading-Pfaden.

Layout-Umstellung (Matrix↔Vote oben, Programm-Treue↔Verbesserung unten)
in #177 als Follow-up — braucht gerichtete Session mit Browser-Vorschau.

Refs: #177

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dotty Dotter 2026-05-06 23:38:01 +02:00
parent a06bcb4d89
commit c87b99f778
2 changed files with 44 additions and 21 deletions

View File

@ -63,6 +63,18 @@
{% else %}m-n{% endif %} {% else %}m-n{% endif %}
{% endmacro %} {% endmacro %}
{# Symbol IMMER aus Rating ableiten — LLM-Outputs sind manchmal
inkonsistent (rating=4 + symbol="+"), was zu „++/+ gleichfarbig"
wirkt. Diese Funktion ist die kanonische Quelle. #}
{% macro rating_symbol(r) -%}
{%- if r is none or r == 0 -%}○
{%- elif r >= 4 -%}++
{%- elif r >= 1 -%}+
{%- elif r <= -4 -%}
{%- else -%}
{%- endif -%}
{%- endmacro %}
<div class="v2-matrix-mini" role="table" aria-label="GWÖ-Matrix 5×5"> <div class="v2-matrix-mini" role="table" aria-label="GWÖ-Matrix 5×5">
{# Header-Zeile #} {# Header-Zeile #}
<div class="hdr" role="columnheader"></div> <div class="hdr" role="columnheader"></div>
@ -77,7 +89,7 @@
{% set key = r ~ c %} {% set key = r ~ c %}
{% set cell = matrix[key] if matrix is defined and key in matrix else {} %} {% set cell = matrix[key] if matrix is defined and key in matrix else {} %}
{% set rating = cell.rating | default(0) | int %} {% set rating = cell.rating | default(0) | int %}
{% set symbol = cell.symbol | default("○") %} {% set symbol = rating_symbol(rating) | trim %}
<div class="{{ rating_class(rating) | trim }}" <div class="{{ rating_class(rating) | trim }}"
role="cell" role="cell"
title="{{ key }}: {{ symbol }}" title="{{ key }}: {{ symbol }}"

View File

@ -183,9 +183,9 @@
</div> </div>
{% endif %} {% endif %}
{# Redline-Vorschläge: alle verbesserungen rendern wenn vorhanden #} {# Verbesserungsvorschläge: alle verbesserungen rendern wenn vorhanden #}
{% if antrag.verbesserungen %} {% if antrag.verbesserungen %}
<h3 class="v2-h3" style="margin-top:24px;">Redline-Vorschläge</h3> <h3 class="v2-h3" style="margin-top:24px;">Verbesserungsvorschläge</h3>
{% for v in antrag.verbesserungen %} {% for v in antrag.verbesserungen %}
<div style="margin-bottom:16px;"> <div style="margin-bottom:16px;">
{% if antrag.verbesserungen | length > 1 %} {% if antrag.verbesserungen | length > 1 %}
@ -207,7 +207,7 @@
</div> </div>
{% endfor %} {% endfor %}
{% elif antrag.redline and antrag.redline.segments %} {% elif antrag.redline and antrag.redline.segments %}
<h3 class="v2-h3" style="margin-top:24px;">Redline-Vorschlag</h3> <h3 class="v2-h3" style="margin-top:24px;">Verbesserungsvorschlag</h3>
{% from "v2/components/redline.html" import redline %} {% from "v2/components/redline.html" import redline %}
{{ redline(segments=antrag.redline.segments) }} {{ redline(segments=antrag.redline.segments) }}
{% endif %} {% endif %}
@ -353,29 +353,40 @@
{{ matrix_mini(antrag.matrix) }} {{ matrix_mini(antrag.matrix) }}
{% endif %} {% endif %}
{# Fraktions-Score-Tabelle (Fix 2+3): auch Fraktionen ohne Zitate sichtbar #} {# Fraktions-Score-Tabelle: Score-Chips + ausgeschriebene Rolle +
sichtbare Begründung für jeden Score (#177). #}
{% if antrag.fraktions_scores %} {% if antrag.fraktions_scores %}
<h3 class="v2-h3" style="margin-top:24px;">Programm-Treue pro Fraktion</h3> <h3 class="v2-h3" style="margin-top:24px;">Programm-Treue pro Fraktion</h3>
<div class="v2-fraktions-scores"> <div class="v2-fraktions-scores">
{% for fs in antrag.fraktions_scores %} {% for fs in antrag.fraktions_scores %}
<div class="v2-fraktion-row"> {% set wp_score = fs.wahlprogramm.score | float %}
<div class="v2-fraktion-label"> {% set pp_score = fs.parteiprogramm.score | float %}
{{ fs.fraktion }} <div class="v2-fraktion-row" style="display:block;border-bottom:1px solid var(--hairline);padding:10px 0;">
{% if fs.ist_antragsteller %}<span class="v2-badge-antragsteller" title="A — Antragstellende Fraktion: hat den Antrag eingereicht.">A</span>{% endif %} <div style="display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;margin-bottom:6px;">
{% if fs.ist_regierung %}<span class="v2-badge-regierung" title="R — Regierungsfraktion: trägt die aktuelle Mehrheit im Landtag.">R</span>{% endif %} <span class="v2-fraktion-label" style="font-weight:700;">{{ fs.fraktion }}</span>
</div> {% if fs.ist_antragsteller %}<span style="font-family:var(--font-mono);font-size:10px;color:#bf6c10;background:rgba(247,148,29,0.15);padding:1px 6px;border-radius:3px;">Antragsteller:in</span>{% endif %}
<div class="v2-fraktion-scores"> {% if fs.ist_regierung %}<span style="font-family:var(--font-mono);font-size:10px;color:#1e6a90;background:rgba(0,157,165,0.15);padding:1px 6px;border-radius:3px;">Regierungsfraktion</span>{% endif %}
{% set wp_score = fs.wahlprogramm.score | float %} <span style="margin-left:auto;display:inline-flex;gap:4px;">
{% set pp_score = fs.parteiprogramm.score | float %} <span class="v2-score-chip {% if wp_score >= 7 %}chip-green{% elif wp_score >= 4 %}chip-mid{% else %}chip-red{% endif %}">
<span class="v2-score-chip {% if wp_score >= 7 %}chip-green{% elif wp_score >= 4 %}chip-mid{% else %}chip-red{% endif %}" WP {{ "%.0f"|format(wp_score) }}/10
title="WP — Wahlprogramm-Treue (010): wie gut passt der Antrag zum aktuellen Wahlprogramm dieser Fraktion?&#10;&#10;{{ fs.wahlprogramm.begruendung }}"> </span>
WP {{ "%.0f"|format(wp_score) }}/10 <span class="v2-score-chip {% if pp_score >= 7 %}chip-green{% elif pp_score >= 4 %}chip-mid{% else %}chip-red{% endif %}">
</span> PP {{ "%.0f"|format(pp_score) }}/10
<span class="v2-score-chip {% if pp_score >= 7 %}chip-green{% elif pp_score >= 4 %}chip-mid{% else %}chip-red{% endif %}" </span>
title="PP — Parteiprogramm-Treue (010): wie gut passt der Antrag zum Grundsatzprogramm dieser Partei?&#10;&#10;{{ fs.parteiprogramm.begruendung }}">
PP {{ "%.0f"|format(pp_score) }}/10
</span> </span>
</div> </div>
{% if fs.wahlprogramm.begruendung %}
<div style="font-size:12px;line-height:1.5;color:var(--ecg-dark);opacity:0.85;margin-bottom:4px;">
<strong style="font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:0.05em;color:#0d6f76;">Wahlprogramm</strong>
— {{ fs.wahlprogramm.begruendung }}
</div>
{% endif %}
{% if fs.parteiprogramm.begruendung %}
<div style="font-size:12px;line-height:1.5;color:var(--ecg-dark);opacity:0.85;">
<strong style="font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:0.05em;color:#0d6f76;">Parteiprogramm</strong>
— {{ fs.parteiprogramm.begruendung }}
</div>
{% endif %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>