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 "екатеринбург".
This commit is contained in:
bot-backend 2026-07-12 23:50:49 +03:00
parent 720cee2c8e
commit 2ef3e42da1
2 changed files with 13 additions and 0 deletions

View file

@ -193,8 +193,18 @@ def _names_non_ekb_city(address: str) -> bool:
<город>»). Используется чтобы НЕ пускать EKB-only локальные тиры
(geoportal/cadastral) на не-ЕКБ адрес: улица+дом, совпадающие с ЕКБ-домом
(проспект Ленина 1 есть в обоих городах), иначе адрес снапается в ЕКБ (#11).
Явное "Екатеринбург" в адресе имеет приоритет над любым омонимом другого
города региона иначе ЖК/посёлки с названием, совпадающим с другим
городом области (ЖК «Заречный», пос. «Сысерть» внутри ЕКБ), ложно
отсекали бы EKB-only тиры для реального ЕКБ-адреса.
"""
normalized = " ".join(address.lower().split())
# Явное "Екатеринбург" в адресе → это ЕКБ, даже если в тексте встречается
# омоним соседнего города (ЖК «Заречный», пос. Сысерть в составе МО «город
# Екатеринбург» и т.п. — реальные false-positive из прод-кэша).
if "екатеринбург" in normalized:
return False
for m in _NON_EKB_CITY_RE.finditer(normalized):
prefix_words = normalized[: m.start()].split()
if prefix_words and prefix_words[-1] in _DISTRICT_PREFIXES:

View file

@ -187,6 +187,9 @@ def test_has_oblast_marker(text: str, expected: bool) -> None:
("Серова 27", False), # word-boundary — НЕ город "Серов"
("мкр Заречный, ул. Ленина 5", False), # район внутри города, не ЗАТО Заречный
("Каменск-Уральский, ул. Ленина 1", True),
# явное "Екатеринбург" перевешивает омоним соседнего города в названии
# ЖК/района внутри города (реальный false-positive из прод-кэша, #11):
("24, опалихинская улица, заречный, Верх-Исетский район, Екатеринбург", False),
],
)
def test_names_non_ekb_city(address: str, expected: bool) -> None: