6 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b1727ca39c |
feat(msk): импорт сырья по Москве в listings и region-aware геокодирование
Три куска, каждый нужен, чтобы поиск и оценка по Москве заработали end-to-end. 1. Импортёр msk_raw -> listings (app/tasks/msk_raw_import.py). Переиспользует штатный save_listings из кита: писатель уже параметризован регионом, свой не нужен. payload в msk_raw — сериализованный ScrapedLot один в один, так что импорт сводится к сборке модели и вызову писателя. Москва отбирается по префиксу административного округа в адресе, а не по bbox. Причина: адрес Циан не содержит города, а границы региона 77 захватывают ближний пояс области. Замер по проду: с округом 35 552, все внутри bbox 77; без округа внутри bbox 17 576 — это область. Отдельно отсекаются 212 карточек с адресом «Екатеринбург (Cian)», артефакт парсера. listing_segment ПЕРЕСЧИТЫВАЕТСЯ перед записью, а не копируется из payload. Кит ставит novostroyki по одному наличию offer.newbuilding.id. Замер по всем 60 464 карточкам: is_from_developer=true у НУЛЯ, false у 29 000, отсутствует у 31 464. Застройщик не продаёт ни одной карточки корпуса. В проде есть гвард (estimator.py): в аналоги идут строки только с listing_segment IS NULL или 'vtorichka' — копирование метки как есть выбросило бы 29 000 строк из подбора. Авито импортируется только с явным --allow-unfiltered: в его адресе нет ни города, ни округа, координат нет ни у одной из 50 335 карточек, отличить область от Москвы нечем. Прогон на проде: прочитано 60 464, записано 35 552, все с геометрией, 35 182 привязаны к дому, создано 10 960 домов. Повторный проход строки не дублирует — idempotency на dedup_hash, проверено. 2. Подсказки адреса стали региональными (geocoder.suggest, api/v1/geocode). Раньше suggest вообще не принимал регион: DaData звалась с жёстким region='Свердловская', Nominatim — с viewbox 66-го и bounded=1. Московский адрес давал ПУСТОЙ список молча, без ошибки; в коде это уже было описано как известный баг. Механику по регионам переиспользовали из geocode(), вторую не писали. Кадастровый тир для не-66 не зовётся: он на ЕКБ-данных. 3. Оценка перестала геокодировать Москву свердловским скоупом (estimator). geocode() звалась без региона, то есть с дефолтом 66, и московский адрес возвращал бы пустую оценку с причиной address_not_geocoded даже с рабочими подсказками. Регион запроса определяется по координатам через реестр, затем по city_hint, затем дефолт. Fast-path клиентских координат стал региононезависимым: OBLAST66_BBOX и REGIONS[66].bbox_region совпадают байт-в-байт, поэтому для 66 поведение прежнее, добавились координаты Москвы. Регресс-нейтральность по Свердловской области — главный критерий всех трёх кусков. Тесты: 1160 passed по затронутым областям. Известные ограничения. Границы 77 захватывают ближний пояс области, Химки резолвятся в Москву. У региона 77 нет ни одного тира обогащения, оценка поедет на аналогах и сделках. Ценовая полоса по Москве одна на весь город. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VQ8jqr4SFirX5tFLwdSrXh |
||
|
|
9898b6bc02 |
feat(tradein/geocoder): регион-параметризация геокодера — region_code в geocode()/known_city_hint, --region-code у скрипта сделок, region_code у admin geocode-missing (#3051)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 9s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m6s
Геокодер был жёстко привязан к Свердловской области: viewbox 66 +
bounded=1, accept только при state ~ 'свердловск' и точке в bbox 66,
known_city_hint знал лишь города области → для 212 937 московских сделок
(address 'Москва, <улица>') геокод давал None либо ложный хит по
одноимённой улице области, а cache-ключ без города смешивал регионы.
Теперь регион приходит от вызывающего (deals.region_code): viewbox и
bbox из REGIONS[code], state-маркер per region ('свердловск'/'москва'),
ЕКБ-тиры (geoportal/cadastral/local houses) только при 66, city-хинт
через словарь региона → cache-ключ '|city=москва'. Дефолт 66 везде —
для существующих вызовов поведение байт-идентично (ревью двумя
линзами). Побочно: geocode-missing по умолчанию больше не берёт
listings с region_code NULL (16 930 неактивных чужих городов, которые
и раньше геокодились впустую).
|
||
| 5ecd5361fd |
fix(tradein/geocode): гейт мусорного города вынести в общий хелпер и прошить в admin-путь (#2603)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI Trade-In / changes (pull_request) Successful in 7s
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 2m42s
Первый коммит починил только scripts/geocode_deals_nominatim.py — ручной скрипт. Тот же дефект оставался на живом пути: POST /admin/geocode-missing?target=deals отдавал сырой row["city"] в city_hint, а deals.city росреестровое и в хвосте распределения содержит не-города («Бессонова», «Билейский рыбопитомник»). Любой не-ЕКБ хинт жёстко закрывает EKB-локальные тиры и уезжает префиксом в запрос провайдеру, то есть мусорный хинт хуже отсутствия хинта. Гейт вынесен в geocoder.known_city_hint (сверка с SVERDLOVSK_OBLAST_CITIES — тем же набором, который уже питает _names_non_ekb_city / _ekb_local_tiers_allowed) и переиспользуется всеми тремя потребителями city_hint: скриптом, admin-ручкой и задачей geocode_missing. Копий функции нет — четвёртый потребитель, если появится, получит гейт сам. Тесты: мусорный город -> хинт не передаётся, валидный -> передаётся; проверено фальсификацией (без фикса все три новых теста краснеют). |
|||
|
|
3d075632a9 |
chore(tradein/geocoder): удалить Яндекс-геокодер (#2593)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 10s
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 2m28s
Убирает ядро Yandex Geocoder (forward/reverse/suggest lookups + region-check + bias-хелперы + EKB_BBOX dict) из app/services/geocoder.py — Yandex demo-key исчерпан, Nominatim/DaData/локальные ЕКБ-тиры (geoportal/cadastral) остаются единственными живыми провайдерами. Цепочка тиров после удаления: кэш → геопортал ЕКБ → кадастр (house-match) → кадастр (raw) → Nominatim; в подсказках дополнительно DaData. НЕ затронуто (намеренно): Yandex.Недвижимость как источник объявлений (source='yandex', yandex_city_sweep*, providers/yandex/serp.py:geocoderAddress), Avito geocoder (providers/avito/imv.py:_geocode), EKB_BBOX_TIGHT/WIDE, _nominatim_region_ok, scripts/*_yandex_reverse.py и их тесты, tests/fixtures/ yandex_geocode_sample.json (всё ещё используется test_audit_address_mismatch.py). _SNAP_PRECISIONS оставлен с "exact" (недостижимо без Yandex-tier, но дёшево хранить — parity с frontend MapPicker.tsx SNAP_PRECISIONS и не ломает test_snap_precision_useful_exact_and_number). |
||
|
|
b3c76c8c62 |
fix(tradein/geocoder): city_hint должен доходить до локальных тиров + не терять ЕКБ-привязку в Tier 4 (#2576)
All checks were successful
CI / changes (pull_request) Successful in 9s
CI Trade-In / changes (pull_request) Successful in 9s
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 2m19s
Deep-review PR #2580 нашёл два блокера в предыдущем фиксе (#2576): C1 — city_hint не участвовал в гейте локальных ЕКБ-only тиров (geoportal/cadastral, `use_local_ekb` в geocode() и Tier 1 в suggest()). Явный city_hint="Нижний Тагил" для "Ленина 1" всё равно попадал на ЕКБ-only базы, получал "точный" ЕКБ-хит и при этом city_ambiguous=False (хинт ведь был) — то есть система теперь ложно-уверенно утверждала неверный город. Фикс: city_hint участвует в той же проверке _names_non_ekb_city (гейт/ gazetteer #2582 не трогаю — только добавляю вход). C2 — снятие суффикса ", Екатеринбург" в _nominatim_suggest для случая "город неизвестен" регрессило часть реальных ЕКБ-адресов: без текстовой подсказки о городе Nominatim иногда предпочитает street-level матч в соседнем городе-спутнике (эмпирика ревьюера: "Победы 20" без суффикса → Верхняя Пышма вместо ЖК "Парк Победы" в Екатеринбурге). Решение — dual-query: bare (честный, без города) И ЕКБ-suffixed запросы объединяются (не заменяют друг друга), оба честных кандидата остаются в подсказках, пользователь выбирает сам. Extra round-trip только для последнего fallback-тира (cadastral/DaData/Yandex уже не сработали) — не задевает основной трафик. Заодно (🟠, дешёвая правка): city_hint прокинут в _nominatim_lookup — с тех пор как Yandex-ключ недействителен (#2585), это единственный живой внешний провайдер, и его tie-break (предпочитает tight-ЕКБ bbox) без города не различает одноимённые улицы внутри региона. 4 новых теста (C1×2, C2×2 + dedupe) — проверено что все 4 падают на коде до этого коммита через git stash (только geocoder.py, тесты оставлены). |
||
|
|
4b1a282234 |
fix(tradein/geocoder): не подставлять Екатеринбург молча — явный city_hint (#2576)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 10s
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 2m17s
Раньше _yandex_lookup/_yandex_suggest/_nominatim_suggest молча подставляли "Екатеринбург, " в запрос, если в адресе не было маркера города/области. Житель Нижнего Тагила, вводя «Ленина, 1», получал уверенно неверную цену по екатеринбургской улице Ленина (обе улицы называются одинаково) — фронт город вообще не передаёт. - geocode()/suggest() принимают опциональный city_hint: str | None; без него внешние тиры больше НЕ подставляют город, а bias (ll/spn) смещается на всю область (OBLAST66_VIEWBOX) вместо ЕКБ-центра. Явный маркер города в адресе или city_hint сохраняют прежнее поведение (ЕКБ-путь не деградирует). - GeocodeResult.city_ambiguous — честный флаг «город определил провайдер, а не пользователь» (не эвристика на корректность), проброшен в AggregatedEstimate.target_city_ambiguous (ephemeral, не персистится). - Cache-ключ geocode_cache учитывает city_hint (address|city=...) — без hint'а формат не меняется (backward-compat), с hint'ом разные города для одного текста адреса больше не делят одну запись. - API: /api/v1/geocode/lookup, /suggest и POST /trade-in/estimate получили опциональный city_hint — контракт не ломается (default None). 23 новых теста в test_geocoder_city_hint.py; проверено что они падают (ImportError на _cache_key) на коде до фикса через git stash. |