From a9f4686157ffb4e27bca134f4c483606eb93b807 Mon Sep 17 00:00:00 2001 From: Dennis Thiessen Date: Wed, 1 Jul 2026 10:04:06 +0200 Subject: [PATCH] fix: forward sentiment-adjustment fields in the scores API response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_score computed base_score / sentiment_score / sentiment_adjustment / max_sentiment_adjustment, but the router's _map_composite_breakdown built the response model from only the five original keys and silently dropped the rest — so the API always returned null for them. That's why the ticker page showed neither the "Composite = Base + Sentiment" caption nor the ± marker on the sentiment row despite the frontend and scoring service both supporting it. Pass the fields through, with a guard test so they can't be dropped again. Co-Authored-By: Claude Opus 4.8 --- app/routers/scores.py | 4 ++ tests/unit/test_scores_router_breakdown.py | 45 ++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/unit/test_scores_router_breakdown.py diff --git a/app/routers/scores.py b/app/routers/scores.py index a20c471..bb0b33f 100644 --- a/app/routers/scores.py +++ b/app/routers/scores.py @@ -39,6 +39,10 @@ def _map_composite_breakdown(raw: dict | None) -> CompositeBreakdownResponse | N missing_dimensions=raw["missing_dimensions"], renormalized_weights=raw["renormalized_weights"], formula=raw["formula"], + base_score=raw.get("base_score"), + sentiment_score=raw.get("sentiment_score"), + sentiment_adjustment=raw.get("sentiment_adjustment"), + max_sentiment_adjustment=raw.get("max_sentiment_adjustment"), ) router = APIRouter(tags=["scores"]) diff --git a/tests/unit/test_scores_router_breakdown.py b/tests/unit/test_scores_router_breakdown.py new file mode 100644 index 0000000..a8c0950 --- /dev/null +++ b/tests/unit/test_scores_router_breakdown.py @@ -0,0 +1,45 @@ +"""Guard: the scores router must forward the sentiment-adjustment breakdown fields. + +get_score computes base_score / sentiment_adjustment, but the router builds the +response model by hand — so it has to explicitly pass those through, or the ticker +page loses the "Composite = Base + Sentiment" caption and the ± dimension marker. +""" + +from app.routers.scores import _map_composite_breakdown + + +def test_forwards_sentiment_fields(): + raw = { + "weights": {"technical": 0.25}, + "available_dimensions": ["technical"], + "missing_dimensions": ["sentiment"], + "renormalized_weights": {"technical": 1.0}, + "formula": "x", + "base_score": 78.0, + "sentiment_score": 75.0, + "sentiment_adjustment": 5.0, + "max_sentiment_adjustment": 10.0, + } + model = _map_composite_breakdown(raw) + assert model is not None + assert model.base_score == 78.0 + assert model.sentiment_score == 75.0 + assert model.sentiment_adjustment == 5.0 + assert model.max_sentiment_adjustment == 10.0 + + +def test_none_stays_none(): + assert _map_composite_breakdown(None) is None + + +def test_old_shaped_dict_defaults_new_fields_to_none(): + raw = { + "weights": {}, + "available_dimensions": [], + "missing_dimensions": [], + "renormalized_weights": {}, + "formula": "x", + } + model = _map_composite_breakdown(raw) + assert model.sentiment_adjustment is None + assert model.base_score is None