-- 196_listings_city.sql -- Issue #2594 — критичный дефект: скрапер знает город в момент сбора (city_slug из -- CITY_LOCATIONS/CITY_ANCHORS, packages/scraper-kit/.../orchestration/pipeline.py), но -- НИКУДА его не пишет. Провайдеры (avito/cian) часто отдают адрес БЕЗ города в тексте -- ("ул. Победы, 30" вместо "Нижний Тагил, ул. Победы, 30") — cian даже явно вырезает -- location-часть перед записью (skip_types = {"location", "metro"}, providers/cian/serp.py). -- Без явного города такой адрес при геокодинге считается «город не назван» → попадает -- в EKB-only локальные реестры (ekb_geoportal_buildings/gendesign_cad_buildings) и -- коллизирует с одноимённой екатеринбургской улицей (Ленина/Победы/Тенистая — сотни -- совпадений) → объявление получает координаты Екатеринбурга и тянет медиану чужих цен. -- -- Fix: -- Add listings.city TEXT column. Проставляется НЕПОСРЕДСТВЕННО из контекста -- развёртки (город известен вызывающему коду — city_slug/CITY_LOCATIONS для oblast, -- "Екатеринбург" для EKB-развёрток) — НЕ парсингом текста адреса. См. -- scraper_kit.base.save_listings(..., city=...) + scraper_kit.orchestration.pipeline -- .resolve_city_name(). Раздельная колонка (а не дописывание города в address) — -- исходный текст адреса не портится, downstream text-парсеры (geocoder._parse_street_house, -- geocoder._names_non_ekb_city, estimator._parse_street_house, house-matching) продолжают -- работать НЕИЗМЕНЁННЫМИ на исходном сыром тексте — риск регрессии на bare-form адресах -- без street-маркера ("Дружинина, 33") исключён. -- -- Scope (#2594): только write-path для НОВЫХ листингов (go-forward). Бэкфилл city для -- уже накопленных строк (restore по тому, какая развёртка их когда-то принесла) — -- отдельная задача, НЕ эта миграция. -- -- Idempotency: -- ALTER TABLE ... ADD COLUMN IF NOT EXISTS — safe on re-run. -- BEGIN/COMMIT block. -- -- Dependencies: -- 002_core_tables.sql (listings table). BEGIN; ALTER TABLE listings ADD COLUMN IF NOT EXISTS city text; COMMENT ON COLUMN listings.city IS 'Город объявления (#2594) — проставляется из контекста развёртки ' '(city_slug city-sweep / "Екатеринбург" default), НЕ парсингом address. ' 'NULL — листинг записан до этой миграции ИЛИ путём, ещё не проставляющим город ' '(admin ad-hoc /admin/scrape, manual ingest-скрипты).'; COMMIT;