gendesign/tradein-mvp/backend
bot-backend 51c79f9324
All checks were successful
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 55s
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
fix(tradein/estimator): analogs cards match n_analogs, no cross-source dups
Two display-layer bugs surfaced by pre-launch smoke (golden-path address
"Екатеринбург, улица Малышева, 51"):

1. n_analogs (same-building anchor path) was computed from the post-MAD-clip
   comp population (_compute_same_building_anchor: n = len(surviving ppm2)),
   but the displayed analogs cards were built from the ORIGINAL, un-clipped
   anchor_comps list. A MAD-clip-excluded price outlier (e.g. a cross-source
   duplicate priced ~4.5x above the rest) never affected the median/n_analogs,
   yet still showed up as a card.

   Fix: _compute_same_building_anchor now returns the final surviving "comps"
   list (len == n), and the caller sets anchor_comps_used = anchor["comps"]
   instead of the raw pre-clip anchor_comps.

2. The statistical cross-source dedup (_dedup_cross_source) requires the same
   price_bucket (rounded price_rub) to consider two listings duplicates. Real
   cross-posts of the same physical unit almost always have a small asking-
   price drift between platforms, which can straddle a bucket boundary and
   let the duplicate survive into both the price population and the UI.

   Fix: a new display-only dedup pass (_dedup_display_lots /
   _union_find_phys_dedup) uses the same physical key (cadnum-or-street +
   floor + area_bucket) but WITHOUT price_bucket, applied only when building
   the displayed analogs_lots — never touches n_analogs/median/cv. To avoid
   over-merging genuinely distinct same-building units that happen to share
   floor/area, it requires the two lots to come from DIFFERENT sources
   (require_diff_source guard) — same-source pairs are left untouched.

The radius-path (non-anchor) branch had a related count-consistency gap:
n_analogs counted only PRICED listings_clean entries, while the displayed
cards were sliced from the full (unfiltered) listings_clean, occasionally
letting price-less entries inflate the card count beyond n_analogs. Cards
are now built from the same priced subset.

Frozen backtest regression gate (tests/test_backtest_regression_gate.py,
hermetic replay of committed fixture) is byte-identical after this change —
zero delta on median/expected_sold/coverage/calibration, confirming this is
display-only and does not touch pricing.

Added tests/test_estimator_analogs_display_consistency.py: full-pipeline
regression for both root causes (outlier duplicate pair fully excluded from
both count and cards; non-outlier duplicate pair collapses in cards only,
honestly documenting len(analogs) <= n_analogs when the display-only dedup
catches a residual cross-source dup the stat dedup missed) + unit tests on
_dedup_display_lots. Updated test_estimator_quarter_index.py's hand-rolled
_compute_same_building_anchor test double to match the new "comps" contract.
2026-07-13 23:04:33 +03:00
..
app fix(tradein/estimator): analogs cards match n_analogs, no cross-source dups 2026-07-13 23:04:33 +03:00
data/sql feat(tradein/db): user_events table — unified login/IP audit + behavior event log (#2515) 2026-07-13 19:47:26 +00:00
scripts feat(tradein/backtest): --city per-city валидация + corridor city-parity 2026-07-12 20:49:04 +03:00
tests fix(tradein/estimator): analogs cards match n_analogs, no cross-source dups 2026-07-13 23:04:33 +03:00
.dockerignore feat: add tradein-mvp subproject (Trade-In Estimator под /trade-in) 2026-05-21 00:25:39 +03:00
Dockerfile chore(scraper-kit): docker-context wiring + restore backend workspace dep (#2137) 2026-07-02 14:21:38 +00:00
pyproject.toml fix(tradein/pdf): честный embedded-шрифт (PT Sans/PT Serif) + matplotlib-график вместо div (#2427) 2026-07-04 14:39:37 +00:00