gendesign/tradein-mvp/backend/tests/test_deals_sanitize.py
bot-backend d5ed6f0fe4 fix(tradein): sanitize ДКП deal outliers (floor / ppm²) (#699)
actual_deals carried non-market/broken rows (floor:100, ~39.7K/m² for 125m²)
that fed dkp_corridor + expected_sold with noise. Add absolute guard-bands
for EKB vtorichka:
- ppm² in [50K, 800K] (market ~100-400K, premium up to ~680K; below/above is
  not arms-length — shares, encumbered, typos)
- floor in [1, 60] and floor <= total_floors (floor:100 / 30-in-a-5-storey).

_fetch_deals filters rows via _is_plausible_deal (display + downstream);
_fetch_dkp_corridor adds the ppm² band to its SQL (expected_sold corridor).
Relative Tukey outlier filter on listings unchanged.

Refs #699
2026-05-30 17:29:29 +03:00

52 lines
1.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""#699 — ДКП-выброс sanitize: _is_plausible_deal guard-bands (floor + ppm²)."""
import os
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost/test_db")
from app.services.estimator import (
DEAL_MAX_FLOOR,
DEAL_MAX_PPM2,
DEAL_MIN_PPM2,
_is_plausible_deal,
)
def test_drops_implausible_floor_100() -> None:
# репорт-кейс: floor:100 — заведомо битый этаж.
assert _is_plausible_deal(150_000, 100, None) is False
def test_drops_submarket_ppm2_3970() -> None:
# репорт-кейс: 4.98М за 125 м² = ~39.7К/м² — нерыночный.
assert _is_plausible_deal(39_700, 5, 9) is False
def test_drops_floor_above_total_floors() -> None:
# 30-й этаж в 5-этажке физически невозможен.
assert _is_plausible_deal(150_000, 30, 5) is False
def test_drops_ppm2_above_band() -> None:
assert _is_plausible_deal(DEAL_MAX_PPM2 + 1, 5, 9) is False
def test_keeps_normal_deal() -> None:
assert _is_plausible_deal(150_000, 5, 9) is True
def test_keeps_high_floor_in_tall_building() -> None:
# 30-й этаж в 30-этажке — реально для ЕКБ, не выброс.
assert _is_plausible_deal(160_000, 30, 30) is True
def test_keeps_at_band_edges() -> None:
assert _is_plausible_deal(DEAL_MIN_PPM2, 1, None) is True
assert _is_plausible_deal(DEAL_MAX_PPM2, DEAL_MAX_FLOOR, None) is True
def test_null_fields_not_judged() -> None:
# ppm²/floor=None — нечем судить, оставляем (None-safe).
assert _is_plausible_deal(None, None, None) is True
assert _is_plausible_deal(None, 100, None) is False # floor всё равно судим
assert _is_plausible_deal(39_700, None, None) is False # ppm² всё равно судим