gendesign/tradein-mvp/backend/app/services
bot-backend deffd31156
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 8s
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 Trade-In / backend-tests (pull_request) Successful in 50s
fix(tradein/estimator): восстановить и канонизировать корпус дома в дедупе (#2436)
Регрессия post-#2429: _DEDUP_HOUSE_NO_RE захватывал целиком только слэш-нотацию
корпуса ("65/4"), а глухую ("65к4") и с пробелом ("65 к4") усекал до голой
цифры ("65") — #2429 лишь остановил ложный захват буквы через `(?!\w)`, но не
восстановил отброшенную цифру корпуса. guard_house в union() затем сравнивал
"65" (avito/cian) != "65/4" (domklik) для ОДНОГО и того же физического дома и
блокировал кросс-source слияние — 226 дублирующих групп в проде.

Фикс:
- _DEDUP_HOUSE_NO_RE: добавлена альтернатива для корпус-словоформы
  (к/корп/корпус, глухо или с пробелом, ТРЕБУЕТ цифру корпуса сразу за собой) —
  пробуется ДО bare-letter альтернативы, чтобы "65к4" захватывался целиком, а
  не усекался.
- Новая _normalize_house_no(): канонизирует ЛЮБУЮ нотацию корпуса ("65к4",
  "65 к4", "65, корпус 4", "65/4") к единому "65/4". Литер без цифры за буквой
  ("24г") — другой физический суффикс, НЕ трогается. Идемпотентна.
- Вызывается в единой точке — оба return _parse_street_house (включая
  bare-street ветку #2291) — так что guard_house всегда видит канонические
  значения независимо от source-формата.

Тесты: rewrite test_parse_street_house_glued_corpus_letter_partial_capture
(пиновал баг "10к1" -> "10" как норму) в
test_parse_street_house_glued_corpus_canonicalizes_to_slash ("10к1"/"10 к1" ->
"10/1"); обновлены 3 существующих кейса в test_parse_street_house_extended_formats
и stale-комментарии в 2 e2e тестах; +14 новых тестов (5 unit normalize_house_no,
parametrized cross-notation agreement, e2e slash-vs-glued merge в обе стороны
порядка union(), adversarial liter-vs-corpus non-merge, guard всё ещё блокирует
разные номера корпуса, apartment-marker "кв." не путается с "корп.", dangling
"65к" без цифры = литер).

Live-верификация на проде (read-only, house_id_fk+floor+area+price кластеры,
count(DISTINCT source)>1): 4841 кандидат-кластера / 11756 строк. OLD-логика
(текущий прод, post-#2429) 5441 survivor-строк, NEW (этот фикс) 5261 —
186 кластеров изменились, 185 полностью схлопнулись в 1 представителя (192-й
частично: 4->3). Zero true guard-violations — автоматическая проверка по всем
4841 кластерам подтвердила, что ни один мёрдж не объединил лоты с ГЕНУИННО
разными (после нормализации) номерами корпуса без авторитетного совпадения
кадастрового номера. 6 кластеров показали ПРОТИВОПОЛОЖНЫЙ эффект (было 1
survivor, стало 2) — во всех 6 один источник (обычно domklik) даёт "голый"
номер дома без корпуса ("77"), а другой — конкретный корпус ("77к1"/"77к4");
раньше баг СЛУЧАЙНО делал их равными через усечение, сейчас guard корректно
их не сливает (та же консервативная семантика "genuinely-different -> не
сливаем", что уже применяется к "65/4" vs "65/5"); отдельный,
architecturally-orthogonal вопрос (стоит ли считать голый номер wildcard'ом
для любого корпуса) — вне scope этого фикса, не блокирует.
2026-07-04 19:50:52 +03:00
..
exporters fix(tradein/pdf): честный embedded-шрифт (PT Sans/PT Serif) + matplotlib-график вместо div (#2427) 2026-07-04 14:39:37 +00:00
matching fix(tradein/pii): удалить телефоны продавцов (NULL) + PII клиента (DROP) (#2217, #1969) 2026-07-03 23:00:42 +03:00
__init__.py feat: add tradein-mvp subproject (Trade-In Estimator под /trade-in) 2026-05-21 00:25:39 +03:00
account_quota.py fix(tradein): atomic conditional quota increment — close TOCTOU (#747) (#749) 2026-05-30 15:52:34 +00:00
brand.py feat: add tradein-mvp subproject (Trade-In Estimator под /trade-in) 2026-05-21 00:25:39 +03:00
buildings_query.py fix(tradein/sale-share): гард по этажности — отсечь near-neighbor mis-match 2026-06-29 00:31:09 +03:00
cache.py feat(tradein): Phase 3.2 — /api/v1/search endpoint + Redis cache (#479) 2026-05-23 14:44:22 +00:00
cian_price_history.py chore(tradein/scrapers): удалить весь legacy scrapers/ каталог — final E (#2397, #2277) 2026-07-04 15:58:15 +03:00
cian_session.py chore(tradein/scrapers): удалить весь legacy scrapers/ каталог — final E (#2397, #2277) 2026-07-04 15:58:15 +03:00
dadata.py fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1») 2026-06-15 20:21:11 +05:00
estimator.py fix(tradein/estimator): восстановить и канонизировать корпус дома в дедупе (#2436) 2026-07-04 19:50:52 +03:00
gar_flats_loader.py feat(tradein): канонический матч ГАР↔houses (0%→41% yield) + tradein_canon_addr + --match-only (#2063) 2026-06-28 15:50:07 +00:00
geocoder.py feat(tradein/matching): fias_exact tier + estimate fias persist + suggest fias_id contract 2026-07-02 20:49:04 +03:00
house_dedup_merge.py refactor(tradein/scheduler): удалить legacy scheduler_loop + scraper-scheduling, kit единственный путь (#2397 Part C) 2026-07-04 13:00:30 +03:00
house_imv_backfill.py refactor(tradein/scrapers): удалить legacy scrape_pipeline.py + тест-хирургия (#2397 Part E1) 2026-07-04 13:56:06 +03:00
house_metadata.py feat(tradein): postgres_fdw live read of gendesign.cad_buildings (replaces snapshot) (#493) 2026-05-24 08:57:30 +00:00
image_sanitizer.py fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1») 2026-06-15 20:21:11 +05:00
location_coef.py feat(tradein): location-coef MVP через FDW-мост к OSM POI Птицы (#2045) 2026-07-03 23:44:55 +03:00
product_handlers.py fix(tradein/scrapers): подключить geoportal coords backfill в scheduler (#1967) (#2346) 2026-07-03 23:25:47 +00:00
proxy_pool.py feat(proxy-pool): pool service (acquire/release/health) + healthcheck scheduler task (#2162) 2026-07-02 16:41:39 +00:00
rosreestr_poll.py feat(tradein): scheduled monthly Rosreestr new-quarter poll → ingest alert (#888) (#894) 2026-05-31 13:13:55 +00:00
sber_index.py fix(tradein): correct SberIndex hedonic/asking dataset-paths + 404 warning (#902) 2026-06-17 22:50:52 +03:00
scheduler.py refactor(tradein/scheduler): удалить legacy scheduler_loop + scraper-scheduling, kit единственный путь (#2397 Part C) 2026-07-04 13:00:30 +03:00
scrape_runs.py fix(tradein): persist scrape_runs.total_seen/new_count from sweep counters (#1926) (#1928) 2026-06-26 18:09:17 +00:00
scraper_adapters.py feat(tradein/scraper-kit): shared base/utils для providers/* (#2358, Foundation) 2026-07-04 03:50:55 +03:00
scraper_settings.py chore(tradein): деактивировать N1.ru scraper — источник переехал в Cian 2026-06-16 14:50:51 +03:00
search_query.py feat(tradein): segment=vtorichka|novostroyki|all query-param on listings (#1188) 2026-06-17 21:43:01 +03:00
yandex_address_backfill.py fix(tradein): yandex address-enrich — city-agnostic title regex (спутники ЕКБ) (#875) 2026-05-31 09:08:29 +00:00
yandex_price_history.py chore(tradein/scrapers): удалить весь legacy scrapers/ каталог — final E (#2397, #2277) 2026-07-04 15:58:15 +03:00
zhkh_flats_loader.py fix(tradein): cadastre ЖКХ-fallback + COALESCE merge-key + median-floors guard 2026-06-29 01:32:48 +03:00