fix(tradein): sync n_analogs to anchor count when anchor drives headline (#695) (#739)
All checks were successful
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 40s
Deploy Trade-In / deploy (push) Successful in 33s
Deploy Trade-In / test (push) Successful in 24s

Co-authored-by: bot-backend <bot-backend@gendsgn.local>
Co-committed-by: bot-backend <bot-backend@gendsgn.local>
This commit is contained in:
bot-backend 2026-05-30 15:25:35 +00:00 committed by bot-reviewer
parent 2b3eb0b23d
commit 34a13d71c3
2 changed files with 28 additions and 0 deletions

View file

@ -1818,6 +1818,14 @@ async def estimate_quality(
f"Оценка построена по {anchor['n']} аналогам из {tier_label}" f"Оценка построена по {anchor['n']} аналогам из {tier_label}"
f"{' (топ-уровень в доме)' if anchor['used_uplift'] else ''}." f"{' (топ-уровень в доме)' if anchor['used_uplift'] else ''}."
) + repair_note ) + repair_note
# #695 (QA fixup): когда якорь подменяет headline и список аналогов на
# комплы дома, n_analogs ДОЛЖЕН считаться по anchor-популяции, а не по
# радиусу (listings_clean). Иначе все UI-счётчики («Аналогов N»,
# «Показано N из M», «по N аналогам», гистограмма) расходятся: radius=4 vs
# anchor=5 → артефакт «Показано 5 из 4». anchor['n'] = число комплов, на
# которых построена оценка (= показываемый analogs, capped 10). Для anchor-
# пути это и есть «полное число найденных» по контракту n_analogs (#698).
n_analogs = anchor["n"]
# ── #651: IMV / Yandex BLEND (killer accuracy fix) — SECONDARY, Tier D only ── # ── #651: IMV / Yandex BLEND (killer accuracy fix) — SECONDARY, Tier D only ──
# Радиусная медиана системно занижает премиум/видовые квартиры (нет class/ # Радиусная медиана системно занижает премиум/видовые квартиры (нет class/

View file

@ -947,3 +947,23 @@ def test_explanation_radius_path_keeps_base_text() -> None:
exp = est.confidence_explanation or "" exp = est.confidence_explanation or ""
assert "Найдено" in exp assert "Найдено" in exp
assert "Оценка построена по" not in exp # якорной фразы нет на чистом радиусе assert "Оценка построена по" not in exp # якорной фразы нет на чистом радиусе
# ── #695 QA fixup: n_analogs синхронизируется с anchor-популяцией (radius≠anchor) ──
def test_anchor_n_analogs_syncs_to_anchor_count() -> None:
"""#695 QA-fail: anchor подменяет headline+список на комплы дома, но n_analogs
раньше оставался радиусным «Показано 5 из 4». radius=5 anchor=3 намеренно;
n_analogs ДОЛЖЕН стать 3 (anchor), согласуясь с «по 3 аналогам» в explanation."""
radius = [_make_listing(price_per_m2=200_000.0) for _ in range(5)]
est = _run_estimate(anchor_comps=_SB_COMPS_PREMIUM, anchor_tier="A", radius_analogs=radius)
assert est.n_analogs == 3 # = len(_SB_COMPS_PREMIUM), НЕ 5 (радиус)
assert "по 3 аналогам" in (est.confidence_explanation or "")
def test_radius_path_n_analogs_unchanged() -> None:
"""#695 контроль: без якоря n_analogs = радиусный счёт (поведение не меняется)."""
radius = [_make_listing(price_per_m2=200_000.0) for _ in range(5)]
est = _run_estimate(anchor_comps=[], anchor_tier=None, radius_analogs=radius)
assert est.n_analogs == 5