From b5a218160d83b2b032140696b3cdc65b4e2a5be3 Mon Sep 17 00:00:00 2001 From: lekss361 Date: Tue, 12 May 2026 00:54:32 +0300 Subject: [PATCH] fix(site-finder): address PR #87 auto-review minor feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backend (parcels.py): - Centrality factor: weight=1.0 вместо weight=center_bonus (semantic — decay для bonus не применяется, contribution = bonus IS the value). Защищает future PDF-export/UI который мог бы показать weight отдельно от contribution. - group_totals type: dict[str, dict[str, float | int]] — count это int, contribution и contribution_pct это float. Уточняет hint для future mypy. - Top-3 sort convention — добавлен inline-комментарий: positives "[:3] от descending" (most-positive first); negatives "[-3:][::-1]" (most-negative first). Оба "dominant first". Frontend (ScoreBreakdownPanel.tsx): - Stacked bar legend orphan fix: positive groups идут в legend под баром (как до того); negative groups показываются отдельной строкой ниже "Снижают балл — Шум/трамвай: −0.46". Никаких swatch'ей без bar-сегмента. Per auto-review on 1d1c169. --- backend/app/api/v1/parcels.py | 6 ++-- .../site-finder/ScoreBreakdownPanel.tsx | 36 ++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/backend/app/api/v1/parcels.py b/backend/app/api/v1/parcels.py index 56dba94b..b2b73422 100644 --- a/backend/app/api/v1/parcels.py +++ b/backend/app/api/v1/parcels.py @@ -902,7 +902,9 @@ def analyze_parcel( else: center_bonus = 0.0 - # X1 (#47): centrality как отдельный synthetic factor в breakdown + # X1 (#47): centrality как отдельный synthetic factor в breakdown. + # NB: для centrality decay не применяется (bonus IS the value), поэтому + # weight=1.0 семантически — "no decay multiplier"; contribution = center_bonus. if center_bonus > 0: factors_detailed.append( { @@ -911,7 +913,7 @@ def analyze_parcel( "category_ru": "Центральность", "group": "Локация", "value": round(dist_to_center_km, 2), - "weight": center_bonus, + "weight": 1.0, "contribution": round(center_bonus, 2), "verbal": ( f"Близость к центру ЕКБ ({dist_to_center_km:.1f}км) — " diff --git a/frontend/src/components/site-finder/ScoreBreakdownPanel.tsx b/frontend/src/components/site-finder/ScoreBreakdownPanel.tsx index 74d382a1..5f86c52e 100644 --- a/frontend/src/components/site-finder/ScoreBreakdownPanel.tsx +++ b/frontend/src/components/site-finder/ScoreBreakdownPanel.tsx @@ -111,7 +111,8 @@ export function ScoreBreakdownPanel({ color: "#6b7280", }} > - {byGroup.map((g) => ( + {/* Positive groups — visible в баре */} + {positiveGroups.map((g) => (
))}
+ {/* Negative groups — отдельной "drag" линией под баром (legend для bar + использует только positive, чтобы не было orphan swatches без сегмента) */} + {byGroup + .filter((g) => g.contribution < 0) + .map((g) => ( +
+ + + Снижают балл — {g.group}:{" "} + + {fmtContribution(g.contribution)} + + +
+ ))} )}