From 6f81ee73d7e2674dfc11a1d9532b1510f0948c62 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Fri, 3 Jul 2026 10:48:15 +0300 Subject: [PATCH] =?UTF-8?q?fix(tradein/estimator):=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D1=81=D1=8C=D1=8E=D1=82=20=D0=B7=D0=B5?= =?UTF-8?q?=D0=BB=D1=91=D0=BD=D1=8B=D0=B9=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5?= =?UTF-8?q?=20=D0=B4=D0=B5=D0=B4=D1=83=D0=BF-=D0=BA=D0=BB=D1=8E=D1=87?= =?UTF-8?q?=D0=B0=20(#2265)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tradein-mvp/backend/app/services/estimator.py | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/tradein-mvp/backend/app/services/estimator.py b/tradein-mvp/backend/app/services/estimator.py index 48cc5c8c..cf506ff1 100644 --- a/tradein-mvp/backend/app/services/estimator.py +++ b/tradein-mvp/backend/app/services/estimator.py @@ -4586,8 +4586,10 @@ _DEDUP_PRICE_BUCKET_RUB = 100_000 # Типы улиц (для отсечения от названия). District-маркеры (мкр/м-н/квартал/ # район) СЮДА НЕ входят — иначе «мкр. ЖБИ, улица Сыромолотова» матчилось бы на -# «мкр ЖБИ» вместо реальной улицы; они убираются как шум в _ADDR_NOISE_WORDS. -_STREET_TYPE_WORDS = ( +# «мкр ЖБИ» вместо реальной улицы; они убираются как шум в _DEDUP_ADDR_NOISE_WORDS. +# NB: имена с префиксом _DEDUP_ — чтобы НЕ конфликтовать с модульным +# _STREET_NAME_RE (extract_street_name, другой парсер выше). +_DEDUP_STREET_TYPE_WORDS = ( "улица", "ул", "переулок", @@ -4613,30 +4615,32 @@ _STREET_TYPE_WORDS = ( "линия", ) # Длинные типы раньше коротких, чтобы «улица» матчилось раньше «ул». -_STREET_TYPE_ALT = "|".join(re.escape(w) for w in sorted(_STREET_TYPE_WORDS, key=len, reverse=True)) +_DEDUP_STREET_TYPE_ALT = "|".join( + re.escape(w) for w in sorted(_DEDUP_STREET_TYPE_WORDS, key=len, reverse=True) +) # «<тип>[.] <название>»: название = опциональная ведущая цифро-группа + 1-3 # буквенных слова, до запятой/цифры. Ведущая цифра нужна для нумерованных улиц # («8 Марта», «40 лет Октября», «22 Партсъезда»); она разрешена ТОЛЬКО перед # буквенным словом (?:\d+\s+)?[а-яё]…, поэтому НОМЕР ДОМА (число ПОСЛЕ названия, # не сопровождаемое буквой) в токен не попадает: «Белинского 86» → «белинского». -_STREET_NAME_RE = re.compile( - rf"(?:^|[,\s])(?:{_STREET_TYPE_ALT})\.?\s+" +_DEDUP_STREET_NAME_RE = re.compile( + rf"(?:^|[,\s])(?:{_DEDUP_STREET_TYPE_ALT})\.?\s+" r"((?:\d+\s+)?[а-яё][а-яё\-]+(?:\s+[а-яё][а-яё\-]+){0,2})", flags=re.IGNORECASE, ) # Номер дома = первое число ПОСЛЕ названия улицы (опц. «д.»/«дом»), с литерой/ # корпусом («46», «8а», «12/3»). Ищется в остатке строки от конца street-матча — # ведущая цифра нумерованной улицы («8 Марта») сюда не попадает. -_HOUSE_NO_RE = re.compile( +_DEDUP_HOUSE_NO_RE = re.compile( r"\b(?:д(?:ом)?\.?\s*)?(\d+(?:[а-яё]|/\d+)?)\b", flags=re.IGNORECASE, ) # Слова-маркеры дома, которые могли попасть в захват названия (напр. «ленина д 5»). -_HOUSE_MARKER_WORDS = frozenset( +_DEDUP_HOUSE_MARKER_WORDS = frozenset( {"дом", "д", "корпус", "корп", "к", "строение", "стр", "литера", "лит", "владение", "вл"} ) # Шум-токены (город/страна/район/микрорайон) на случай, если попали в название. -_ADDR_NOISE_WORDS = frozenset( +_DEDUP_ADDR_NOISE_WORDS = frozenset( { "россия", "рф", @@ -4666,17 +4670,17 @@ def _parse_street_house(addr: str | None) -> tuple[str, str]: if not addr: return "", "" s = addr.strip().lower() - m = _STREET_NAME_RE.search(s) + m = _DEDUP_STREET_NAME_RE.search(s) if not m: return "", "" words = [ w for w in m.group(1).split() - if w and w not in _HOUSE_MARKER_WORDS and w not in _ADDR_NOISE_WORDS + if w and w not in _DEDUP_HOUSE_MARKER_WORDS and w not in _DEDUP_ADDR_NOISE_WORDS ] street = " ".join(words).strip() house = "" - hm = _HOUSE_NO_RE.search(s, m.end()) + hm = _DEDUP_HOUSE_NO_RE.search(s, m.end()) if hm: house = hm.group(1) return street, house