fix(tradein): чинит кадастр-матчер геокодера — резолв адресов из своей базы (~60% упавших) #1780

Merged
lekss361 merged 1 commit from fix/tradein-cadastral-geocode-matcher into main 2026-06-19 11:11:14 +00:00
Owner

Проблема (прод-аудит)

За 60 дней — 15 оценок упали с address_not_geocoded у реальных юзеров (praktika/kopylov/user2): валидные адреса ЕКБ из автокомплита не геокодились. Проверено на проде: geocode()/suggest() искали в нашей базе gendesign_cad_buildings через readable_address ILIKE '%' || query || '%' по сырой строке. Но в базе формат «ул. Серова, д. 27» — подстроки «Серова 27» (или DaData-формы «620144, …, ул Серова, д 27») там нет → 0 hits → проваливалось на Yandex (403, мёртвый ключ) → fail. Данные были — матчер не находил.

Фикс

В geocoder.py добавлены два хелпера, подключены как primary кадастр-шаг в geocode() (2a) и suggest() (tier 1a); legacy raw-ILIKE остаётся как 2b фоллбэк, Yandex/Nominatim — нетронуты.

  • _parse_street_house(address) — вытаскивает (улица, дом) из обеих форм: полный DaData («620091, …, пр-кт Космонавтов, д 26а, кв 335» → ("космонавтов","26а"), кв/корпус отбрасываются) и ручной ввод («Серова 27», «8 Марта 204» — улица с цифры).
  • _cadastral_house_match(db, street, house) — матч по cad_buildings: ILIKE по улице + regex с якорем номера дома к маркеру д./дом/строение (убивает ложняк типа «(1-83)» внутри «д 169а»), фильтр город ЕКБ, исключение деревень (Северка/Шабровский/СНТ). psycopg v3: bound-параметры + CAST(:x AS text), в regex только цифры дома (regex-safe). Graceful try/except → None.

Валидация на проде (до кода)

  • 11/16 контрольных адресов матчатся из своей базы, 0 ложных срабатываний (якорь к дом-маркеру убрал «(1-83)»).
  • На полной выборке упавших геокодов: 6/10 (60%) резолвятся из своей базы, без DaData/Yandex.
  • Координаты сверены: Серова 27→(56.81188,60.59739), Щербакова 77→(56.77698,60.66160), Сурикова 31, Шаумяна 28/30, Малышева 30, 8 Марта 204 — всё корректно.

Остаток ~40% (Космонавтов 7б/26а, Педагогическая 15, Фрунзе 75) физически отсутствует в наборе ~36k — закрывается отдельно (дозалив кадастра / фоллбэк-геокодер / прокидывание suggest-координат).

Тесты

pytest117 passed (новый test_geocoder_cadastral_matcher.py: parser-кейсы по всем формам + mocked matcher + wiring; плюс существующие geocoder/cadastral-reverse/dadata). ruff check чисто. Pre-commit green (фикс root cause B011 в test_cadastral_reverse, без --no-verify).

## Проблема (прод-аудит) За 60 дней — **15 оценок упали с `address_not_geocoded`** у реальных юзеров (praktika/kopylov/user2): валидные адреса ЕКБ из автокомплита не геокодились. Проверено на проде: `geocode()`/`suggest()` искали в нашей базе `gendesign_cad_buildings` через `readable_address ILIKE '%' || query || '%'` по **сырой строке**. Но в базе формат «ул. Серова, д. 27» — подстроки «Серова 27» (или DaData-формы «620144, …, ул Серова, д 27») там нет → **0 hits** → проваливалось на Yandex (403, мёртвый ключ) → fail. **Данные были — матчер не находил.** ## Фикс В `geocoder.py` добавлены два хелпера, подключены как **primary** кадастр-шаг в `geocode()` (2a) и `suggest()` (tier 1a); legacy raw-ILIKE остаётся как 2b фоллбэк, Yandex/Nominatim — нетронуты. - **`_parse_street_house(address)`** — вытаскивает (улица, дом) из обеих форм: полный DaData («620091, …, пр-кт Космонавтов, д 26а, кв 335» → `("космонавтов","26а")`, кв/корпус отбрасываются) и ручной ввод («Серова 27», «8 Марта 204» — улица с цифры). - **`_cadastral_house_match(db, street, house)`** — матч по cad_buildings: ILIKE по улице + **regex с якорем номера дома к маркеру `д./дом/строение`** (убивает ложняк типа «(1-83)» внутри «д 169а»), фильтр город ЕКБ, исключение деревень (Северка/Шабровский/СНТ). psycopg v3: bound-параметры + `CAST(:x AS text)`, в regex только цифры дома (regex-safe). Graceful try/except → None. ## Валидация на проде (до кода) - **11/16** контрольных адресов матчатся из своей базы, **0 ложных срабатываний** (якорь к дом-маркеру убрал «(1-83)»). - На полной выборке упавших геокодов: **6/10 (60%) резолвятся из своей базы, без DaData/Yandex.** - Координаты сверены: Серова 27→(56.81188,60.59739), Щербакова 77→(56.77698,60.66160), Сурикова 31, Шаумяна 28/30, Малышева 30, 8 Марта 204 — всё корректно. Остаток ~40% (Космонавтов 7б/26а, Педагогическая 15, Фрунзе 75) **физически отсутствует** в наборе ~36k — закрывается отдельно (дозалив кадастра / фоллбэк-геокодер / прокидывание suggest-координат). ## Тесты `pytest` — **117 passed** (новый `test_geocoder_cadastral_matcher.py`: parser-кейсы по всем формам + mocked matcher + wiring; плюс существующие geocoder/cadastral-reverse/dadata). `ruff check` чисто. Pre-commit green (фикс root cause B011 в test_cadastral_reverse, без `--no-verify`).
lekss361 added 1 commit 2026-06-19 11:06:12 +00:00
fix(tradein): parse street+house for cadastral geocode match (resolve ~60% of failing addresses from local DB)
All checks were successful
CI / changes (pull_request) Successful in 6s
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
c2a1dc8aeb
lekss361 merged commit 41b3f08303 into main 2026-06-19 11:11:14 +00:00
lekss361 deleted branch fix/tradein-cadastral-geocode-matcher 2026-06-19 11:11:14 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1780
No description provided.