refactor: use svd_labels for fallback labels in explorer and axis_classifier (Task 4)

main
Sven Geboers 1 month ago
parent 36b58ad50d
commit 5b3cf23d36
  1. 27
      analysis/axis_classifier.py
  2. 9
      explorer.py
  3. 52
      tests/test_axis_label_fallback.py

@ -13,7 +13,7 @@ import numpy as np
import re import re
import json import json
from analysis.svd_labels import get_svd_label from analysis.svd_labels import get_svd_label, get_fallback_labels
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -44,16 +44,21 @@ _LABELS = {
def display_label_for_modal(modal_label: Optional[str], axis: str) -> str: def display_label_for_modal(modal_label: Optional[str], axis: str) -> str:
"""Return a user-facing axis label for a modal/internal label. """Return a user-facing axis label for a modal/internal label.
Keeps existing behavior: map numeric fallback names 'As 1' / 'Stempatroon As 1' Maps numeric fallback names 'As 1' / 'Stempatroon As 1' to the
to the conventional semantic defaults used in the UI. Any other label is semantic labels from SVD_THEMES. Any other label is returned unchanged.
returned unchanged; None is treated as the semantic fallback for the axis. None is treated as the semantic fallback for the axis.
""" """
if modal_label is None: if modal_label is None:
return "Links\u2013Rechts" if axis == "x" else "Progressief\u2013Conservatief" # Fallback to component 1 (x) or 2 (y)
comp = 1 if axis == "x" else 2
return get_svd_label(comp)
# Map "As 1" / "As 2" to semantic labels
if axis == "x" and modal_label in ("As 1", "Stempatroon As 1"): if axis == "x" and modal_label in ("As 1", "Stempatroon As 1"):
return "Links\u2013Rechts" return get_svd_label(1)
if axis == "y" and modal_label in ("As 2", "Stempatroon As 2"): if axis == "y" and modal_label in ("As 2", "Stempatroon As 2"):
return "Progressief\u2013Conservatief" return get_svd_label(2)
return modal_label return modal_label
@ -430,7 +435,8 @@ def _assign_label(
Returns (label, interpretation_string, quality_score). Returns (label, interpretation_string, quality_score).
""" """
orientation = "horizontale" if axis == "x" else "verticale" orientation = "horizontale" if axis == "x" else "verticale"
fallback_label = _LABELS["fallback_x"] if axis == "x" else _LABELS["fallback_y"] _x_fallback, _y_fallback = get_fallback_labels()
fallback_label = _x_fallback if axis == "x" else _y_fallback
quality = max(abs(r_lr), abs(r_co), abs(r_pc)) quality = max(abs(r_lr), abs(r_co), abs(r_pc))
if abs(r_lr) >= _THRESHOLD: if abs(r_lr) >= _THRESHOLD:
@ -608,13 +614,14 @@ def classify_axes(
# ── Final label resolution ──────────────────────────────────────────── # ── Final label resolution ────────────────────────────────────────────
# If both motion and ideology paths produced nothing, use generic fallback. # If both motion and ideology paths produced nothing, use generic fallback.
_x_fallback, _y_fallback = get_fallback_labels()
if x_lbl is None: if x_lbl is None:
x_lbl = _LABELS["fallback_x"] x_lbl = _x_fallback
x_int = _INTERPRETATION_TEMPLATES["fallback"].format( x_int = _INTERPRETATION_TEMPLATES["fallback"].format(
orientation="horizontale" orientation="horizontale"
) )
if y_lbl is None: if y_lbl is None:
y_lbl = _LABELS["fallback_y"] y_lbl = _y_fallback
y_int = _INTERPRETATION_TEMPLATES["fallback"].format( y_int = _INTERPRETATION_TEMPLATES["fallback"].format(
orientation="verticale" orientation="verticale"
) )

@ -1612,15 +1612,18 @@ def build_compass_tab(db_path: str, window_size: str) -> None:
# Use the classifier helper to map internal/modal labels (e.g. "As 1") to # Use the classifier helper to map internal/modal labels (e.g. "As 1") to
# user-facing labels. Import at function-time to avoid module import cycles # user-facing labels. Import at function-time to avoid module import cycles
# and keep explorer lightweight. If the helper is unavailable fall back to # and keep explorer lightweight. If the helper is unavailable fall back to
# conventional semantic defaults so the UI remains readable. # labels from the unified svd_labels module.
try: try:
from analysis.axis_classifier import display_label_for_modal from analysis.axis_classifier import display_label_for_modal
_x_label = display_label_for_modal(_raw_x, "x") _x_label = display_label_for_modal(_raw_x, "x")
_y_label = display_label_for_modal(_raw_y, "y") _y_label = display_label_for_modal(_raw_y, "y")
except Exception: except Exception:
_x_label = _raw_x or "EU-integratie–Nationalisme" from analysis.svd_labels import get_fallback_labels
_y_label = _raw_y or "Populistisch–Institutioneel"
_x_fallback, _y_fallback = get_fallback_labels()
_x_label = _raw_x or _x_fallback
_y_label = _raw_y or _y_fallback
if level == "Partijen": if level == "Partijen":
# Aggregate to party centroids # Aggregate to party centroids

@ -4,21 +4,34 @@ from analysis import axis_classifier
def test_display_label_for_modal(): def test_display_label_for_modal():
assert axis_classifier.display_label_for_modal("As 1", "x") == "Links\u2013Rechts" """Test that display_label_for_modal uses SVD_THEMES for fallback labels."""
assert ( # None should return fallback from SVD_THEMES
axis_classifier.display_label_for_modal("Stempatroon As 1", "x") x_label = axis_classifier.display_label_for_modal(None, "x")
== "Links\u2013Rechts" y_label = axis_classifier.display_label_for_modal(None, "y")
)
assert ( # Should return component 1 and 2 labels from SVD_THEMES
axis_classifier.display_label_for_modal("As 2", "y") assert "EU-integratie" in x_label or "Nationalisme" in x_label
== "Conservatief\u2013Progressief" assert "Populistisch" in y_label or "Institutioneel" in y_label
)
assert (
axis_classifier.display_label_for_modal("Stempatroon As 2", "y") def test_display_label_for_modal_maps_as_labels():
== "Conservatief\u2013Progressief" """Test that 'As 1' and 'As 2' are mapped to semantic labels."""
) x_label = axis_classifier.display_label_for_modal("As 1", "x")
# None maps to conventional fallback y_label = axis_classifier.display_label_for_modal("As 2", "y")
assert axis_classifier.display_label_for_modal(None, "x") == "Links\u2013Rechts"
# Should return component 1 and 2 labels
assert "EU-integratie" in x_label or "Nationalisme" in x_label
assert "Populistisch" in y_label or "Institutioneel" in y_label
def test_display_label_for_modal_stempatroon():
"""Test that 'Stempatroon As N' are mapped to semantic labels."""
x_label = axis_classifier.display_label_for_modal("Stempatroon As 1", "x")
y_label = axis_classifier.display_label_for_modal("Stempatroon As 2", "y")
# Should return component 1 and 2 labels
assert "EU-integratie" in x_label or "Nationalisme" in x_label
assert "Populistisch" in y_label or "Institutioneel" in y_label
def test_classify_axes_modal_fallback(monkeypatch, tmp_path): def test_classify_axes_modal_fallback(monkeypatch, tmp_path):
@ -69,5 +82,10 @@ def test_classify_axes_modal_fallback(monkeypatch, tmp_path):
if not enriched or not isinstance(enriched, dict): if not enriched or not isinstance(enriched, dict):
pytest.skip("classify_axes returned no enrichment in this environment") pytest.skip("classify_axes returned no enrichment in this environment")
assert enriched["x_label"] == "Links\u2013Rechts" # Should now return SVD component labels instead of hardcoded values
assert enriched["y_label"] == "Progressief\u2013Conservatief" assert (
"EU-integratie" in enriched["x_label"] or "Nationalisme" in enriched["x_label"]
)
assert (
"Populistisch" in enriched["y_label"] or "Institutioneel" in enriched["y_label"]
)

Loading…
Cancel
Save