fix(tradein/estimator): восстановить и канонизировать корпус дома в дедупе (регрессия #2429) #2432
Merged
lekss361
merged 1 commit from 2026-07-04 17:08:40 +00:00
fix/tradein-dedup-corpus-format-normalize into main
1 commit
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
deffd31156 |
fix(tradein/estimator): восстановить и канонизировать корпус дома в дедупе (#2436)
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
Регрессия 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 этого фикса, не блокирует.
|