feat(site-finder): X1 score breakdown + verbal explain (#47) #87

Closed
lekss361 wants to merge 8 commits from feat/site-finder-score-breakdown into main
2 changed files with 39 additions and 3 deletions
Showing only changes of commit b5a218160d - Show all commits

View file

@ -902,7 +902,9 @@ def analyze_parcel(
else: else:
center_bonus = 0.0 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: if center_bonus > 0:
factors_detailed.append( factors_detailed.append(
{ {
@ -911,7 +913,7 @@ def analyze_parcel(
"category_ru": "Центральность", "category_ru": "Центральность",
"group": "Локация", "group": "Локация",
"value": round(dist_to_center_km, 2), "value": round(dist_to_center_km, 2),
"weight": center_bonus, "weight": 1.0,
"contribution": round(center_bonus, 2), "contribution": round(center_bonus, 2),
"verbal": ( "verbal": (
f"Близость к центру ЕКБ ({dist_to_center_km:.1f}км) — " f"Близость к центру ЕКБ ({dist_to_center_km:.1f}км) — "

View file

@ -111,7 +111,8 @@ export function ScoreBreakdownPanel({
color: "#6b7280", color: "#6b7280",
}} }}
> >
{byGroup.map((g) => ( {/* Positive groups — visible в баре */}
{positiveGroups.map((g) => (
<div <div
key={g.group} key={g.group}
style={{ display: "flex", alignItems: "center", gap: 4 }} style={{ display: "flex", alignItems: "center", gap: 4 }}
@ -134,6 +135,39 @@ export function ScoreBreakdownPanel({
</div> </div>
))} ))}
</div> </div>
{/* Negative groups отдельной "drag" линией под баром (legend для bar
использует только positive, чтобы не было orphan swatches без сегмента) */}
{byGroup
.filter((g) => g.contribution < 0)
.map((g) => (
<div
key={`neg-${g.group}`}
style={{
display: "flex",
alignItems: "center",
gap: 4,
marginTop: 6,
fontSize: 12,
color: "#6b7280",
}}
>
<span
style={{
display: "inline-block",
width: 10,
height: 10,
borderRadius: 2,
background: GROUP_COLORS[g.group] ?? GROUP_COLORS.Прочее,
}}
/>
<span>
Снижают балл {g.group}:{" "}
<strong style={{ color: "#dc2626" }}>
{fmtContribution(g.contribution)}
</strong>
</span>
</div>
))}
</div> </div>
)} )}