fix(tradein/scraper): проставлять город объявления из контекста развёртки (#2594) #2598
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#2598
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tradein-listing-city-from-sweep"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #2594 частично (см. «что осталось»). Критический дефект: скрапер знает город в момент сбора, но не записывает его — объявления области рискуют получить координаты Екатеринбурга.
Замер, с которого всё началось: сбор Авито дал 243 объявления, координаты у 12, город в адресе — у 5. Приходит
пр-т Вагоностроителей,70,ул. Победы,30— тагильские улицы без города. В екатеринбургском реестре улица Победы есть в 89 домах, Ленина в 112, Тенистая в 17.Решение: отдельная колонка, а не дописывание в адрес
Выбрана колонка
listings.city(миграция 196), заполняется из контекста развёртки.Почему не дописывать город в
address— исполнитель проверил разбор адреса: для адресов без указателя типа улицы («Дружинина, 33») парсер берёт весь текст до номера дома как название улицы. Префикс «Нижний Тагил, » попал бы в поле улицы и сломал бы сопоставление домов сразу в трёх местах. Существующая функция вырезает из адреса только «Екатеринбург», а расширять её означало бы трогать геокодер, который только что переработан. Отдельная колонка этого риска не несёт: исходный текст адреса не меняется.Детали
save_listings(..., city=...)пишет колонку, при конфликте —COALESCE(EXCLUDED.city, listings.city), чтобы вызов без города не затирал уже известный. Новая функцияresolve_city_name(city_slug)переводит слаг в название и проброшена во все боевые пути записи: городские развёртки Авито, Яндекса, Циана, плюс полные загрузки и новостройки (они подтверждённо екатеринбургские по докстрингам).Симметрия соблюдена:
resolve_city_name(None)даёт «Екатеринбург», так что екатеринбургские объявления тоже получают город — асимметрии «у области есть, у ЕКБ нет» не возникает.Домклик получает честный
Noneдля незнакомого идентификатора города вместо угадывания — его областные развёртки ещё не подключены.Другие источники — проверены
⚠️ Что осталось (важно, отдельный PR)
Колонка заполняется, но петля пока не замкнута: ночное геокодирование и бэкфилл координат по-прежнему решают по тексту адреса и новую колонку не читают. То есть новые объявления получат правильный город в базе, но при геокодировании bare-адрес всё ещё может уйти в Екатеринбург.
Исполнитель осознанно не стал это делать здесь: трогать геокодер было прямо запрещено (он только что переработан в #2582/#2580), а задача сужена до пути записи. Следующий шаг — передавать город из колонки как подсказку в геокодирование, правя только вызывающий код.
Бэкфилл накопленных строк (384 Тагил, 267 Каменск-Уральский, 114 Первоуральск) — третий шаг.
Тесты: +62. Все 19 новых падали на старом коде (
KeyError: 'city', функции не существует). Полный прогон: 2961 passed, 1 pre-existing.Скрапер знает город в момент сбора (city_slug из CITY_LOCATIONS/CITY_ANCHORS, scraper_kit.orchestration.pipeline), но раньше нигде его не записывал. Провайдеры (avito/cian) часто отдают адрес БЕЗ города в тексте ("ул. Победы, 30" вместо "Нижний Тагил, ул. Победы, 30" — cian даже явно вырезает location-часть перед записью, providers/cian/serp.py _format_address skip_types={"location",...}). Без города такой адрес при геокодинге считался "город не назван" и коллизировал с одноимённой екатеринбургской улицей (Ленина/Победы/Тенистая — сотни совпадений в ЕКБ-реестрах) → объявление получало координаты Екатеринбурга. Fix: отдельная колонка listings.city (196_listings_city.sql), проставляется из sweep-контекста через save_listings(..., city=...) — НЕ парсингом/дописыванием в address. Раздельная колонка не портит исходный текст адреса: downstream text-парсеры (geocoder._parse_street_house/_names_non_ekb_city, estimator house-matching) продолжают работать на исходном сыром тексте неизменёнными — дописывание города в address ломало бы bare-form адреса без street-маркера ("Дружинина, 33" без "ул.") в этих же парсерах. Симметрия: EKB-варианты city-sweep функций (city_slug=None) тоже получают city="Екатеринбург" — resolve_city_name(None) даёт тот же ЕКБ-дефолт, что и get_city_location/get_city_anchors. Проставлено во всех продовых write-путях: run_avito_city_sweep/run_yandex_city_sweep/run_cian_city_sweep (city_slug-aware), run_avito_newbuilding_sweep/run_cian_full_load/run_yandex_full_load/ run_avito_full_load (подтверждённо EKB-only по докстрингам), run_domclick_city_sweep (EKB city_id, oblast B2 ещё не wired — честный None для неизвестного city_id). Scope: только write-path для НОВЫХ листингов. Бэкфилл накопленных строк и консультация city в geocode_missing_listings/backfill_coords_from_geoportal (gate там пока text-only, _names_non_ekb_city) — geocoder.py намеренно не тронут (#2582/#2580) — отдельные follow-up задачи.