diff --git a/backend/app/api/v1/parcels.py b/backend/app/api/v1/parcels.py index 9f8e7029..7743f5cb 100644 --- a/backend/app/api/v1/parcels.py +++ b/backend/app/api/v1/parcels.py @@ -1,3 +1,4 @@ +import datetime as _dt import json import logging import math @@ -579,8 +580,6 @@ def _compute_confidence( доступны на main. Композитный балл = avg of subscore'ов; caveats — list конкретных проблем для UI ("Нет данных N, score K ненадёжен"). """ - import datetime as _dt - caveats: list[str] = [] subscores: dict[str, float] = {} @@ -616,9 +615,15 @@ def _compute_confidence( if not district_row: caveats.append("Район не определён (вне границ ЕКБ?) — медианные цены недоступны") - # 4) Market trend — есть ли rosreestr_deals - if market_trend and market_trend.get("recent_deals_count"): - n_recent = int(market_trend["recent_deals_count"]) + # 4) Market trend — есть ли rosreestr_deals. + # Guard `int(... or 0)` — recent_deals_count иногда приходит как non-numeric + # из 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 мес — достаточно для тренда subscores["market_trend"] = min(1.0, n_recent / 10.0) if n_recent < 5: @@ -1300,11 +1305,14 @@ def analyze_parcel( factors_sorted = sorted(factors_detailed, key=lambda x: x["contribution"], reverse=True) # Convention: оба top-list'а отсортированы "dominant first": - # positives → most-positive first (просто [:3] от descending sort) - # negatives → most-negative first ([-3:][::-1] = последние три (наиболее - # отрицательные) реверсом, чтобы first item был самым большим минусом) + # positives → most-positive first (factors_sorted desc → [:3]) + # negatives → most-negative first (sort negatives asc → [:3]) + # Раньше использовался 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_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. group_totals: dict[str, dict[str, float | int]] = {}