Commit graph

4 commits

Author SHA1 Message Date
bot-backend
deeff7ed1f fix(tradein/estimator): Tier S radius-fallback house-number boundary + distance_m
Audit finding 1 (medium): the Tier S same-building radius-fallback matched
`address ILIKE short_addr + '%'` with no boundary after the house number, so
"ул. Ленина, 5%" collided with "..., 50/51/500/5а" (a different building).
MAX_ANALOGS_PER_ADDRESS=5 + `len(tier_s) >= 3` meant one stray building could
poison the same-building median. Fix reuses the already-vetted anchor Tier A
machinery (_normalize_building_key + new shared _house_boundary_regex helper)
instead of a bare string prefix, and replaces the hardcoded `0.0 AS distance_m`
with a real ST_Distance so anomalously-far "same building" matches are visible
in the data.

_extract_short_addr is left in place (unused by _fetch_analogs now, own tests
still pass) with a note explaining the supersession, rather than deleted, to
keep this PR scoped to the boundary bug.

Audit finding 2 (low): conflict_resolution.py's resolve_house_field/
resolve_listing_field priority system is confirmed dead in the production
merge path (matching/houses.py + listings.py use a simpler COALESCE
newest-wins pattern instead) per vault Decision_774_Matching_Architecture
(2026-05-31, code-archaeology + live-DB verified). Documented this in the
module docstring instead of deleting — removal + its ~30 dedicated tests is
already scoped as an independent "Path 2 / Sub-3" cleanup in that decision,
kept separate from this unrelated Tier-S bugfix. Not wired into the price-calc
path per audit instructions.

Tests: new tests/test_estimator_tier_s_house_boundary.py (boundary regex +
SQL-rendering checks); test_same_building_match.py updated to assert the new
boundary-regex SQL shape instead of the old bare ILIKE. Full suite: 2632
passed, 8 skipped, 1 deselected (same deselect as ci-tradein.yml).
2026-07-26 23:42:36 +03:00
bot-backend
67a852caf1 feat(tradein/matching): fias_exact tier + estimate fias persist + suggest fias_id contract
All checks were successful
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
2026-07-02 20:49:04 +03:00
384c4e7ac3 fix(tradein/matching): не сваливать листинги в чужой дом (P1-P3)
All checks were successful
CI / changes (pull_request) Successful in 8s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Три гарда в match_or_create_house (+ P2 в match_house_readonly):
- P1: не матчить/алиасить голую улицу (normalized_address без номера дома)
- P2: отклонять Tier-3 гео-кандидата, если номера домов различаются
- P3: не цементировать слабый гео-матч (0.7) как alias-ключ здания

Хелперы has_house_number/house_number_token (по хвостовому токену —
«улица 8 марта» это улица, не дом). Корень mis-bucketing: house 131237
стянул 8 разных Мамина-номеров + голую улицу. Юнит + поведенческие тесты,
603 matching/normalize/house зелёных. Аффектит только НОВЫЕ матчи;
чистка cemented-данных — отдельной миграцией (P4).
2026-06-29 00:06:13 +03:00
30a80ee608 feat(tradein): same-building матчинг аналогов по house_id (#6)
Estimator теперь резолвит target-адрес в КАНОНИЧЕСКИЙ house_id (read-only,
без создания записи) и матчит аналоги по listings.house_id_fk — детерминированный
Tier S «тот же дом» вместо хрупкого address-string prefix match. 99% листингов
слинкованы с houses, поэтому это срабатывает уже сегодня и точнее работает между
разнородными источниками (Avito/Cian/Yandex форматируют адрес по-разному).

- matching/houses.py: match_house_readonly() — read-only target→house резолвер
  (cadastr_exact → fingerprint → geo_proximity 50m), без INSERT / house_sources /
  advisory lock. Возвращает None если уверенного match нет.
- estimator.estimate_quality: после geocode+DaData резолвит target_house_id
  (cadastr от DaData → cadastr_exact заработает по мере backfill houses), graceful.
- estimator._fetch_analogs: новый param target_house_id + канонический Tier S
  (WHERE house_id_fk = target) ПЕРЕД address-prefix Tier S (теперь fallback).
  tier label 'S' сохранён (frontend уже рендерит).

Эффект: вместо «похожие в радиусе 800-1000м» — «вот что в ЭТОМ доме», прямой
выигрыш в точности оценки. Foundation под cadastr/FIAS exact match (DaData Q1/Q3).

Tests: 9 новых (match_house_readonly tiers + never-inserts + canonical Tier S
fires/fallback/too-few). Ruff clean. Pre-existing 4 fail в test_estimator_source_quota
(unpack 2≠3, красные на main до этого PR) — не затронуты.
2026-05-28 13:55:51 +05:00