fix(tradein/geocode): бэкфилл listings.city из слага города в URL Авито (#2594) #2606

Merged
lekss361 merged 1 commit from fix/tradein-backfill-listing-city-from-url into main 2026-07-31 23:00:00 +00:00
Owner

Summary

  • Issue #2594 шаг 3: бэкфилл listings.city (колонка добавлена в 196) для уже накопленных Avito-объявлений из слага города в source_url (первый сегмент пути, avito.ru/<slug>/...).
  • WHERE source='avito' AND city IS NULL — идемпотентно, не перетирает то, что уже проставил скрапер (go-forward write-path 196).
  • Маппинг только 6 городов Свердловской обл. (region 66), сверен побайтно с CITY_DISPLAY_NAMES/CITY_LOCATIONS в scraper_kit.orchestration.pipeline.
  • Срочность: ночной прогон geocode_missing_listings (2026-08-01 13:18 UTC) заберёт в очередь 148 активных объявлений Нижнего Тагила без city — без города такие адреса ("Хохрякова", "Калинина" — тёзки центральных ЕКБ-улиц) рискуют получить координаты Екатеринбурга. Этот бэкфилл проставляет им city до начала прогона.

Замеры (прод, read-only, до написания миграции)

1. Avito, city IS NULL, по слагу (наши 6 городов):

avito slug → город total (city_null) active
ekaterinburg Екатеринбург 26770 83
nizhniy_tagil Нижний Тагил 551 160
kamensk-uralskiy Каменск-Уральский 244 0
pervouralsk Первоуральск 95 0
verhnyaya_pyshma Верхняя Пышма 21 0
serov Серов 25 0
ИТОГО 27706

2. В очереди сегодняшнего геокодирования (lat IS NULL AND is_active AND address IS NOT NULL AND length(trim(address))>=5 AND (geocode_tried_at IS NULL OR < now()-7d)), из строк, которые эта миграция снабдит городом:

slug in_queue
ekaterinburg 72
nizhniy_tagil 148
ИТОГО 220

148 = ровно число из issue — подтверждает диагноз. Остальные 4 города — 0 в очереди сейчас (либо уже геокодированы, либо неактивны), но city для них всё равно бэкфиллится для корректности/будущих геокодов.

3. Слаги вне scope: 1650 distinct слагов на Avito всего, 1644 вне наших 6 городов, покрывают 16930 строк с city IS NULL, которые остаются NULL — отдельное решение владельца (см. Вне scope ниже).

4. После написания миграции — SELECT-версия UPDATE с идентичным WHERE: 27706 — совпадает с п.1 день-в-день (проверено).

Слаг Верхней Пышмы

Проверил фактические значения source_url на проде: встретился ТОЛЬКО вариант verhnyaya_pyshma (без k, avito_slug из CityLocation в pipeline.py). Второй вариант (verkhnyaya_pyshma — наш внутренний city_slug-ключ) в данных не встретился ни разу, поэтому в WHERE/CASE его не добавлял (дал бы 0 доп. строк, а лишняя ветка — риск будущей путаницы).

Аналогично для Каменска-Уральского: avito-слаг — kamensk-uralskiy (ЧЕРЕЗ ДЕФИС), а не kamensk_uralskiy (подчёркивание) как в примере из issue — сверено с CityLocation("kamensk-uralskiy", ...) в pipeline.py и с фактическими данными.

Вне scope (сознательно не трогаю)

  • Cian — хост НЕ индикатор города (ekb.cian.ru отдаёт областные объявления, включая тагильские, через тот же хост с параметром региона); бэкфилл по хосту дал бы неверный результат.
  • Domclick — 6488 на ekaterinburg.domclick.ru, 13 на голом domclick.ru; выгоды нет (нет объявлений без координат), 13 неопределённых строк — отдельный разбор.
  • Yandex — в URL (realty.yandex.ru/offer/<id>) города нет вовсе.
  • region_code — у 16912 чужих-региона строк он неверный (66) — отдельный пункт issue #2604, ждёт решения владельца.
  • Ничего не удаляется и не деактивируется.

Test plan

  • tests/test_migration_197_backfill_listings_city_from_url.py — новый, по образцу 165/191 (transactional, idempotent scope, no DDL, no psycopg trap, слаги/названия сверены с pipeline.py CITY_DISPLAY_NAMES).
  • tests/test_migrations_manifest.py — префикс 197 свободен, не коллизирует.
  • Полный uv run pytest -q --deselect tests/test_search_api.py::test_search_cache_hit (tradein-mvp/backend): 2948 passed, 9 skipped, 1 deselected — только известный pre-existing deselect, без новых красных.

Refs #2594

## Summary - Issue #2594 шаг 3: бэкфилл `listings.city` (колонка добавлена в 196) для уже накопленных Avito-объявлений из слага города в `source_url` (первый сегмент пути, `avito.ru/<slug>/...`). - `WHERE source='avito' AND city IS NULL` — идемпотентно, не перетирает то, что уже проставил скрапер (go-forward write-path 196). - Маппинг только 6 городов Свердловской обл. (region 66), сверен побайтно с `CITY_DISPLAY_NAMES`/`CITY_LOCATIONS` в `scraper_kit.orchestration.pipeline`. - Срочность: ночной прогон `geocode_missing_listings` (2026-08-01 13:18 UTC) заберёт в очередь 148 активных объявлений Нижнего Тагила без `city` — без города такие адреса ("Хохрякова", "Калинина" — тёзки центральных ЕКБ-улиц) рискуют получить координаты Екатеринбурга. Этот бэкфилл проставляет им city до начала прогона. ## Замеры (прод, read-only, до написания миграции) **1. Avito, `city IS NULL`, по слагу (наши 6 городов):** | avito slug | → город | total (city_null) | active | |---|---|---|---| | `ekaterinburg` | Екатеринбург | 26770 | 83 | | `nizhniy_tagil` | Нижний Тагил | 551 | 160 | | `kamensk-uralskiy` | Каменск-Уральский | 244 | 0 | | `pervouralsk` | Первоуральск | 95 | 0 | | `verhnyaya_pyshma` | Верхняя Пышма | 21 | 0 | | `serov` | Серов | 25 | 0 | | **ИТОГО** | | **27706** | | **2. В очереди сегодняшнего геокодирования** (`lat IS NULL AND is_active AND address IS NOT NULL AND length(trim(address))>=5 AND (geocode_tried_at IS NULL OR < now()-7d)`), из строк, которые эта миграция снабдит городом: | slug | in_queue | |---|---| | `ekaterinburg` | 72 | | `nizhniy_tagil` | **148** | | **ИТОГО** | **220** | 148 = ровно число из issue — подтверждает диагноз. Остальные 4 города — 0 в очереди сейчас (либо уже геокодированы, либо неактивны), но `city` для них всё равно бэкфиллится для корректности/будущих геокодов. **3. Слаги вне scope:** 1650 distinct слагов на Avito всего, 1644 вне наших 6 городов, покрывают 16930 строк с `city IS NULL`, которые остаются NULL — отдельное решение владельца (см. `Вне scope` ниже). **4. После написания миграции — SELECT-версия UPDATE с идентичным WHERE:** `27706` — совпадает с п.1 день-в-день (проверено). ## Слаг Верхней Пышмы Проверил фактические значения source_url на проде: встретился ТОЛЬКО вариант `verhnyaya_pyshma` (без `k`, avito_slug из `CityLocation` в pipeline.py). Второй вариант (`verkhnyaya_pyshma` — наш внутренний `city_slug`-ключ) в данных не встретился ни разу, поэтому в WHERE/CASE его не добавлял (дал бы 0 доп. строк, а лишняя ветка — риск будущей путаницы). Аналогично для Каменска-Уральского: avito-слаг — `kamensk-uralskiy` (ЧЕРЕЗ ДЕФИС), а не `kamensk_uralskiy` (подчёркивание) как в примере из issue — сверено с `CityLocation("kamensk-uralskiy", ...)` в pipeline.py и с фактическими данными. ## Вне scope (сознательно не трогаю) - **Cian** — хост НЕ индикатор города (`ekb.cian.ru` отдаёт областные объявления, включая тагильские, через тот же хост с параметром региона); бэкфилл по хосту дал бы неверный результат. - **Domclick** — 6488 на `ekaterinburg.domclick.ru`, 13 на голом `domclick.ru`; выгоды нет (нет объявлений без координат), 13 неопределённых строк — отдельный разбор. - **Yandex** — в URL (`realty.yandex.ru/offer/<id>`) города нет вовсе. - **`region_code`** — у 16912 чужих-региона строк он неверный (66) — отдельный пункт issue #2604, ждёт решения владельца. - Ничего не удаляется и не деактивируется. ## Test plan - [x] `tests/test_migration_197_backfill_listings_city_from_url.py` — новый, по образцу 165/191 (transactional, idempotent scope, no DDL, no psycopg trap, слаги/названия сверены с pipeline.py CITY_DISPLAY_NAMES). - [x] `tests/test_migrations_manifest.py` — префикс 197 свободен, не коллизирует. - [x] Полный `uv run pytest -q --deselect tests/test_search_api.py::test_search_cache_hit` (tradein-mvp/backend): **2948 passed, 9 skipped, 1 deselected** — только известный pre-existing deselect, без новых красных. Refs #2594
lekss361 added 1 commit 2026-07-31 22:47:20 +00:00
fix(tradein/geocode): бэкфилл listings.city из слага города в URL Авито (#2594)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
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 2m27s
ad76fe844a
lekss361 merged commit 87693aec9b into main 2026-07-31 23:00:00 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2606
No description provided.