From aa6bb341a20ddae0f19daec1f9da5e25c0d10925 Mon Sep 17 00:00:00 2001 From: Seungpyo1007 Date: Fri, 18 Sep 2026 19:16:33 +0900 Subject: [PATCH] fix: honor configured phone scoring weights --- .github/workflows/weekly-refresh.yml | 1 + app/services/scoring/phones.py | 20 +++++++++++++++----- tests/unit/test_scoring_phones.py | 20 ++++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/.github/workflows/weekly-refresh.yml b/.github/workflows/weekly-refresh.yml index 0dde21a..8d02897 100644 --- a/.github/workflows/weekly-refresh.yml +++ b/.github/workflows/weekly-refresh.yml @@ -54,6 +54,7 @@ jobs: repository: GetTechAPI/TechAPI path: techapi token: ${{ secrets.TECHENGINEBOT_TOKEN || secrets.TECHAPI_TOKEN || secrets.GITHUB_TOKEN }} + fetch-depth: 0 - uses: actions/setup-python@v6 with: diff --git a/app/services/scoring/phones.py b/app/services/scoring/phones.py index f6b9a21..ee2b7f6 100644 --- a/app/services/scoring/phones.py +++ b/app/services/scoring/phones.py @@ -124,14 +124,17 @@ def _display(display: dict[str, Any], scales: dict[str, ReferenceScale]) -> floa def _value( - overall: float | None, msrp_usd: int | None, scales: dict[str, ReferenceScale] + overall: float | None, + msrp_usd: int | None, + scales: dict[str, ReferenceScale], + weights: dict[str, float], ) -> float | None: if overall is None or not msrp_usd: return None affordability = capability(float(msrp_usd), scales["msrp_usd"]) if affordability is None: return None - return round(overall * 0.5 + affordability * 0.5, 1) + return combine([(overall, weights["overall"]), (affordability, weights["affordability"])]) def score_phone( @@ -153,9 +156,16 @@ def score_phone( scales, ) display = _display(phone.display, scales) - components = [s for s in (perf.index, camera, battery, display) if s is not None] - overall = round(sum(components) / len(components), 1) if components else None - value = _value(overall, phone.msrp_usd, scales) + overall_weights = cfg.weights["phone_overall"] + overall = combine( + [ + (perf.index, overall_weights["performance"]), + (camera, overall_weights["camera"]), + (battery, overall_weights["battery"]), + (display, overall_weights["display"]), + ] + ) + value = _value(overall, phone.msrp_usd, scales, cfg.weights["value"]) return PhoneScore( algorithm_version=settings.scoring_algorithm_version, overall=overall, diff --git a/tests/unit/test_scoring_phones.py b/tests/unit/test_scoring_phones.py index 82dd849..aaf4cc2 100644 --- a/tests/unit/test_scoring_phones.py +++ b/tests/unit/test_scoring_phones.py @@ -2,11 +2,13 @@ from __future__ import annotations +from dataclasses import replace from datetime import date from app.models.smartphone import Smartphone from app.models.soc import SoC from app.services.scoring import ALGORITHM_VERSION, DatasetStats, score_phone +from app.services.scoring.config import load_config def _soc(**overrides: object) -> SoC: @@ -92,6 +94,24 @@ def test_value_requires_msrp() -> None: assert score_phone(_phone(msrp_usd=None), _soc()).value is None +def test_overall_and_value_use_configured_weights() -> None: + cfg = load_config() + weights = {name: values.copy() for name, values in cfg.weights.items()} + weights["phone_overall"] = { + "performance": 1.0, + "camera": 0.0, + "battery": 0.0, + "display": 0.0, + } + weights["value"] = {"overall": 1.0, "affordability": 0.0} + configured = replace(cfg, weights=weights) + + score = score_phone(_phone(), _soc(), config=configured) + + assert score.overall == score.performance + assert score.value == score.overall + + def test_relative_fields_filled_only_with_stats() -> None: soc = _soc() plain = score_phone(_phone(), soc)