fix(estimator): топ-5 фиксов аудита «Мера» (anchor gate, analog_tier, freshness, MAD-order, data-age) #1836

Merged
lekss361 merged 6 commits from feat/estimator-audit-top5 into main 2026-06-20 12:27:14 +00:00
2 changed files with 27 additions and 0 deletions
Showing only changes of commit 6b99f7d238 - Show all commits

View file

@ -206,6 +206,15 @@ class AggregatedEstimate(BaseModel):
# «approximate» (qc_geo≥2: населённый пункт/город/регион/не распознан).
# None если DaData не отрабатывала (адрес не геокодирован) / credentials не заданы.
address_precision: Literal["house", "street", "approximate"] | None = None
# analog_tier — стабильный enum-тир источника аналогов (#audit-2).
# Значения (дословно, фронт на них завязан):
# "same_building" — Tier A: якорь из комплов ТОГО ЖЕ дома
# "micro_radius" — Tier C: якорь из micro-radius (≤500 м)
# "district" — radius-путь (Tier 0/S/H): когортный/узкий радиус
# "city" — radius Tier W: широкий fallback
# null — нет данных / оценка не построена
# НЕ удаляет/заменяет confidence_explanation (фронт fallback'ает на него).
analog_tier: Literal["same_building", "micro_radius", "district", "city"] | None = None
# ── Параметры оценённой квартиры — нужны, чтобы восстановить карточку
# при открытии оценки по ссылке (?id=), когда формы-инпута уже нет ──
area_m2: float | None = None

View file

@ -2985,6 +2985,22 @@ async def estimate_quality(
else None
)
# #audit-2: структурный analog_tier — стабильный enum для фронта.
# anchor-путь: anchor_tier "A" → "same_building", "C" → "micro_radius".
# radius-путь: analog_tier "W" → "city", остальные → "district".
# None только если нет аналогов (median=0, insufficient_data=True).
if median_price > 0:
if anchor_tier == "A":
api_analog_tier: str | None = "same_building"
elif anchor_tier == "C":
api_analog_tier = "micro_radius"
elif analog_tier == "W":
api_analog_tier = "city"
else:
api_analog_tier = "district"
else:
api_analog_tier = None
return AggregatedEstimate(
estimate_id=estimate_id,
median_price_rub=median_price,
@ -3049,6 +3065,7 @@ async def estimate_quality(
house_fias_id=dadata.house_fias_id if dadata else None,
metro_nearest=(dadata.metro if dadata and dadata.metro else []),
address_precision=_qc_geo_to_precision(dadata.qc_geo if dadata else None),
analog_tier=api_analog_tier, # type: ignore[arg-type]
)
@ -4249,4 +4266,5 @@ def _empty_estimate(
cian_valuation=None,
# Адрес не геокодирован (DaData не отрабатывала) → точность неизвестна.
address_precision=None,
analog_tier=None, # нет данных при empty estimate
)