5 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8fdcd8df62 |
fix(tradein/geocoder): word-boundary EKB short-circuit + gate suggest() + gazetteer (#11)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI Trade-In / 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 56s
Deep-review fast-follows on the non-EKB city gate:
1. Word-boundary the "екатеринбург" short-circuit in _names_non_ekb_city
(new _EKATERINBURG_RE = \bекатеринбург\b, .search() instead of substring
`in`). A bare-substring check matched "Екатеринбургское шоссе" (a real
street in satellite towns like Pervouralsk) as if it named EKB, wrongly
keeping EKB-only local tiers on for a non-EKB address. Still returns False
for the existing homonym false-positive test case (word boundary is
satisfied there — "екатеринбург" appears as a standalone token).
2. Gate suggest()'s Tier 1 (same root cause as geocode()): the EKB-only
cadastral matchers _cadastral_house_match / _cadastral_forward_sync ran
unconditionally, so a non-EKB oblast autocomplete query could surface an
EKB building via street+house collision. Gated behind
`not _names_non_ekb_city(query)`, mirroring geocode()'s use_local_ekb.
External suggest tiers (DaData/Yandex/Nominatim) untouched — still the
path for non-EKB autocomplete.
3. Extended SVERDLOVSK_OBLAST_CITIES with unambiguous, deal-heavy oblast
cities: алапаевск, сухой лог, кушва, красноуральск, карпинск, нижняя
тура, верхний тагил, нижние серги. Deliberately did NOT add "лесной"
(reviewer flagged as reviewer-optional, left to judgment): DB check
(ekb_geoportal_buildings) confirms a real EKB street named exactly
"Лесной" (3 buildings) — an exact whole-word collision indistinguishable
from ЗАТО Лесной by word-boundary matching alone (unlike "Серова"/"Серов",
which are different word forms). Adding it would misclassify a bare
EKB address ("Лесной, 5", no "Екатеринбург" mention) as non-EKB.
Tests: word-boundary EKB regression test, suggest() gating test, two new
gazetteer entries (Верхняя Пышма multi-word, Сухой Лог).
|
||
|
|
2ef3e42da1 |
fix(tradein/geocoder): explicit Екатеринбург wins over oblast-city homonym (#11)
All checks were successful
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
CI Trade-In / backend-tests (pull_request) Successful in 51s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Prod-cache verification (deep-review) found 3 false-positives: EKB addresses that literally contain "екатеринбург" but also happen to contain a homonym of another oblast city inside a neighborhood/posyolok name — ЖК "Заречный" and пос. Сысерть, both administratively inside Ekaterinburg, not the ZATO Zarechny / town Sysert they share a name with. These were gated off the EKB-only local tiers; if Nominatim were momentarily down, a real EKB address would return None — a latent regression not present before this PR. Add an explicit-city short-circuit at the top of _names_non_ekb_city: if the normalized address contains "екатеринбург", return False immediately (EKB tiers stay on). Cannot affect the true-positive (non-EKB) cases, which never contain "екатеринбург". |
||
|
|
720cee2c8e |
fix(tradein/geocoder): gate EKB-local tiers for non-EKB oblast addresses (#11)
All checks were successful
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 53s
CI / changes (pull_request) Successful in 8s
CI Trade-In / changes (pull_request) Successful in 9s
geocode() ran EKB-only local tiers (2a _geoportal_house_match on
ekb_geoportal_buildings, 2c _cadastral_house_match, 2d
_cadastral_forward_sync — all strictly EKB or EKB-dominated) BEFORE the
already oblast-aware external providers (Yandex/Nominatim via OBLAST66_BBOX
+ region cross-check, shipped in
|
||
|
|
c0cbdc2f99 |
fix(tradein/geocoder): раз-хардкод геокодера/DaData на всю Свердловскую обл. (region 66)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 50s
CI Trade-In / changes (pull_request) Successful in 8s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
Геокодер был жёстко EKB-bound и ВЫБРАСЫВАЛ корректные non-EKB геокоды (bbox-accept-фильтры) → блокировал оценку любого адреса области. Обобщено до region-66. - OBLAST66_BBOX + is_within_oblast66_bbox (superset EKB-tight, EKB не затронут); EKB Yandex Tier1/2 fast-path сохранён. - Все accept-фильтры (Yandex/Nominatim forward+suggest, geocode() accept+typo) → oblast-66. - City-prefix по word-boundary _has_oblast_marker (не substring): EKB-улицы 'Серова 27'/'Ирбитская 5' и др. больше не роняют 'Екатеринбург,'-префикс (был silent wrong-city баг). - Two-pass tie-break в accept-циклах: tight-EKB кандидат приоритетнее → EKB-результат идентичен прежнему. - Region cross-check (Nominatim address.state / Yandex AdministrativeAreaName) отсекает соседние области (Тюмень и др.); bbox-fallback когда region отсутствует. - DaData suggest: region-параметр (hard-filter), убран no-op restrict_value. - +26 тестов: OBLAST66 superset TIGHT, far-town accept, Тюмень reject, marker +/- (street-collision), DaData region body. Foundation для A2 (геокодинг non-EKB сделок) и live-оценки адресов области. |
||
| 04550bc94f |
fix(tradein): drop non-EKB avito detail coords on ingest (audit #1871 P2)
avito_detail.parse_detail_html писал data-map-lat/lon прямо в listings UPDATE COALESCE без bbox-валидации — в отличие от geocoder.py, который фильтрует везде. Итог: 406 active avito-листингов с координатами Питер/Тюмень/Уфа просочились в ЕКБ-датасет (ложные аналоги через Tier A address-match оценщика). Prod-verified 2026-06-23. - geocoder: DRY-хелпер is_within_ekb_bbox(lat, lon, bbox) + именованные EKB_BBOX_TIGHT (geocoder-фильтр, числа не изменены) и EKB_BBOX_WIDE (ingest-guard); 2 inline tight-проверки → хелпер (поведение identical). - avito_detail: после извлечения lat/lon → reset None + logger.warning если вне wide-bbox; листинг падает в geocode-cron путь (COALESCE NULL не затирает прежнее хорошее значение). - tests: bbox-хелпер (ЕКБ True, Питер/Тюмень/Уфа False, inclusive, WIDE⊇TIGHT инвариант) + parse_detail_html guard end-to-end. WIDE строго содержит TIGHT — guard физически не режет ничего, что geocoder сам бы пропустил. NULL lat (14935, geocode backlog) — отдельная проблема, не в scope. One-time cleanup существующих 406 — отдельно (database-expert, prod data mutation). Refs #1871 |