fix: forward sentiment-adjustment fields in the scores API response
Deploy / lint (push) Successful in 6s
Deploy / test (push) Successful in 54s
Deploy / deploy (push) Successful in 31s

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 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 10:04:06 +02:00
parent 94ed3207d7
commit a9f4686157
2 changed files with 49 additions and 0 deletions
+4
View File
@@ -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"])
@@ -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