fix(site-finder): X1 logic bugs from PR #87 re-review
1. score_top_3_negatives — rewrite to unambiguously "most-negative first" via
explicit `sorted(negatives, key=contribution)[:3]` (ascending sort). Раньше
использовался trick `[-3:][::-1]` на desc-sorted — давал тот же результат
для N>=3 negatives, но неинтуитивно читать.
2. _compute_confidence(recent_deals_count) — guard `int(... or 0)` через
try/except (ValueError, TypeError). Защищает от non-numeric строки в
external/legacy market_trend payload.
3. Style: `import datetime as _dt` перенесён из function-scope в module-level.
Per auto-review on d0fa8a3.
This commit is contained in:
parent
d0fa8a3126
commit
a6e95b65c2
1 changed files with 17 additions and 9 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
import datetime as _dt
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
|
|
@ -579,8 +580,6 @@ def _compute_confidence(
|
||||||
доступны на main. Композитный балл = avg of subscore'ов; caveats — list
|
доступны на main. Композитный балл = avg of subscore'ов; caveats — list
|
||||||
конкретных проблем для UI ("Нет данных N, score K ненадёжен").
|
конкретных проблем для UI ("Нет данных N, score K ненадёжен").
|
||||||
"""
|
"""
|
||||||
import datetime as _dt
|
|
||||||
|
|
||||||
caveats: list[str] = []
|
caveats: list[str] = []
|
||||||
subscores: dict[str, float] = {}
|
subscores: dict[str, float] = {}
|
||||||
|
|
||||||
|
|
@ -616,9 +615,15 @@ def _compute_confidence(
|
||||||
if not district_row:
|
if not district_row:
|
||||||
caveats.append("Район не определён (вне границ ЕКБ?) — медианные цены недоступны")
|
caveats.append("Район не определён (вне границ ЕКБ?) — медианные цены недоступны")
|
||||||
|
|
||||||
# 4) Market trend — есть ли rosreestr_deals
|
# 4) Market trend — есть ли rosreestr_deals.
|
||||||
if market_trend and market_trend.get("recent_deals_count"):
|
# Guard `int(... or 0)` — recent_deals_count иногда приходит как non-numeric
|
||||||
n_recent = int(market_trend["recent_deals_count"])
|
# из external/legacy paths; без guard int() крашнет 500.
|
||||||
|
n_recent_raw = (market_trend or {}).get("recent_deals_count")
|
||||||
|
try:
|
||||||
|
n_recent = int(n_recent_raw) if n_recent_raw is not None else 0
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
n_recent = 0
|
||||||
|
if n_recent > 0:
|
||||||
# порог 5 сделок за 6 мес — достаточно для тренда
|
# порог 5 сделок за 6 мес — достаточно для тренда
|
||||||
subscores["market_trend"] = min(1.0, n_recent / 10.0)
|
subscores["market_trend"] = min(1.0, n_recent / 10.0)
|
||||||
if n_recent < 5:
|
if n_recent < 5:
|
||||||
|
|
@ -1300,11 +1305,14 @@ def analyze_parcel(
|
||||||
|
|
||||||
factors_sorted = sorted(factors_detailed, key=lambda x: x["contribution"], reverse=True)
|
factors_sorted = sorted(factors_detailed, key=lambda x: x["contribution"], reverse=True)
|
||||||
# Convention: оба top-list'а отсортированы "dominant first":
|
# Convention: оба top-list'а отсортированы "dominant first":
|
||||||
# positives → most-positive first (просто [:3] от descending sort)
|
# positives → most-positive first (factors_sorted desc → [:3])
|
||||||
# negatives → most-negative first ([-3:][::-1] = последние три (наиболее
|
# negatives → most-negative first (sort negatives asc → [:3])
|
||||||
# отрицательные) реверсом, чтобы first item был самым большим минусом)
|
# Раньше использовался trick [-3:][::-1] на desc-sorted — это давало тот же
|
||||||
|
# результат для N>=3 negatives, но был неинтуитивно читать; explicit sort asc
|
||||||
|
# короче и не зависит от тонкостей slicing.
|
||||||
score_top_3_positives = [f for f in factors_sorted if f["contribution"] > 0][:3]
|
score_top_3_positives = [f for f in factors_sorted if f["contribution"] > 0][:3]
|
||||||
score_top_3_negatives = [f for f in factors_sorted if f["contribution"] < 0][-3:][::-1]
|
negatives_only = [f for f in factors_sorted if f["contribution"] < 0]
|
||||||
|
score_top_3_negatives = sorted(negatives_only, key=lambda x: x["contribution"])[:3]
|
||||||
|
|
||||||
# By-group totals — для stacked-bar в UI. count это int, contribution* — float.
|
# By-group totals — для stacked-bar в UI. count это int, contribution* — float.
|
||||||
group_totals: dict[str, dict[str, float | int]] = {}
|
group_totals: dict[str, dict[str, float | int]] = {}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue