feat(tradein): Phase 1 of #582 — address mismatch audit (200-house Yandex baseline) #583

Merged
Light1YT merged 1 commit from feat/tradein-address-audit-phase1 into main 2026-05-26 05:18:56 +00:00
Owner

Связь с issue

Closes Phase 1 of #582. Phases 2-5 в отдельных PR.

Что ships

Phase 1 audit infrastructure для quantification расхождения между нашими (address, lat/lon) парами и тем, что Yandex.Карты говорит для тех же координат.

Файлы

Файл Что
data/sql/066_address_mismatch_audit.sql Таблица address_mismatch_audit + FDW foreign table gendesign_ekb_districts_geom
scripts/audit_address_sample.sql Stratified sampling: 8 районов × 25 = 200
scripts/_yandex_reverse.py API + Playwright reverse-helpers, CAPTCHA-aware
scripts/audit_address_mismatch.py Driver: argparse, resumable, два режима
scripts/address_audit_report.sql p50/p75/p95 distance + top-20 outliers + per-district
tests/test_audit_address_mismatch.py 19 unit-тестов
tests/fixtures/yandex_geocode_sample.json Mock-фикстура для API parser
pyproject.toml playwright>=1.45 в [dependency-groups].dev

Verification

  • pytest tests/test_audit_address_mismatch.py: 19 passed in 1.00s
  • ruff check scripts/ tests/: clean
  • ruff format --check: clean

⚠ Devops runbook (перед запуском audit на проде)

Миграция 066 создаёт FDW foreign table gendesign_ekb_districts_geom. Sampling-запрос требует, чтобы роль tradein_fdw_reader имела SELECT на gendesign.public.ekb_districts_geom. Если grant отсутствует, sampling вернёт 0 строк и script тихо запишет в лог loaded sample: 0 houses.

Проверить:

-- на gendesign master DB:
\dp public.ekb_districts_geom
-- должен быть GRANT SELECT для tradein_fdw_reader

Зависимости / блокеры

  • Phase 3 (backfill) требует Yandex Geocoder API key (issue #402 / task #36) — без него massive backfill через web-скрейп нереалистичен.
  • Phase 1 audit работает БЕЗ ключа (Playwright mode) — ~17 мин на 200 строк, риск CAPTCHA.

Запуск audit

# с API ключом (рекомендуется):
export YANDEX_GEOCODER_API_KEY=...
uv run python scripts/audit_address_mismatch.py --batch 2026-05-25_run1

# без ключа — Playwright:
uv run python scripts/audit_address_mismatch.py --batch 2026-05-25_run1 --mode playwright

# отчёт:
psql -v batch=2026-05-25_run1 -f scripts/address_audit_report.sql

Review trail

  • tech-analyst план Phase 1 (sampling, Yandex extraction, anti-bot).
  • backend-engineer реализация: 8 файлов, 19 тестов.
  • code-reviewer verdict: LGTM, 5 minor non-blocking — все применены в main session (per-row commit comments, O(N²)→O(N) count, skip trailing sleep, redundant index removed, bare except → logger.debug).
## Связь с issue Closes Phase 1 of #582. Phases 2-5 в отдельных PR. ## Что ships **Phase 1 audit infrastructure** для quantification расхождения между нашими `(address, lat/lon)` парами и тем, что Yandex.Карты говорит для тех же координат. ### Файлы | Файл | Что | |---|---| | `data/sql/066_address_mismatch_audit.sql` | Таблица `address_mismatch_audit` + FDW foreign table `gendesign_ekb_districts_geom` | | `scripts/audit_address_sample.sql` | Stratified sampling: 8 районов × 25 = 200 | | `scripts/_yandex_reverse.py` | API + Playwright reverse-helpers, CAPTCHA-aware | | `scripts/audit_address_mismatch.py` | Driver: argparse, resumable, два режима | | `scripts/address_audit_report.sql` | p50/p75/p95 distance + top-20 outliers + per-district | | `tests/test_audit_address_mismatch.py` | 19 unit-тестов | | `tests/fixtures/yandex_geocode_sample.json` | Mock-фикстура для API parser | | `pyproject.toml` | `playwright>=1.45` в `[dependency-groups].dev` | ### Verification - `pytest tests/test_audit_address_mismatch.py`: **19 passed in 1.00s** - `ruff check scripts/ tests/`: clean - `ruff format --check`: clean ## ⚠ Devops runbook (перед запуском audit на проде) Миграция 066 создаёт FDW foreign table `gendesign_ekb_districts_geom`. Sampling-запрос требует, чтобы роль `tradein_fdw_reader` имела `SELECT` на `gendesign.public.ekb_districts_geom`. Если grant отсутствует, sampling вернёт 0 строк и script тихо запишет в лог `loaded sample: 0 houses`. Проверить: ```sql -- на gendesign master DB: \dp public.ekb_districts_geom -- должен быть GRANT SELECT для tradein_fdw_reader ``` ## Зависимости / блокеры - **Phase 3 (backfill)** требует Yandex Geocoder API key (issue #402 / task #36) — без него massive backfill через web-скрейп нереалистичен. - **Phase 1 audit** работает БЕЗ ключа (Playwright mode) — ~17 мин на 200 строк, риск CAPTCHA. ## Запуск audit ```bash # с API ключом (рекомендуется): export YANDEX_GEOCODER_API_KEY=... uv run python scripts/audit_address_mismatch.py --batch 2026-05-25_run1 # без ключа — Playwright: uv run python scripts/audit_address_mismatch.py --batch 2026-05-25_run1 --mode playwright # отчёт: psql -v batch=2026-05-25_run1 -f scripts/address_audit_report.sql ``` ## Review trail - `tech-analyst` план Phase 1 (sampling, Yandex extraction, anti-bot). - `backend-engineer` реализация: 8 файлов, 19 тестов. - `code-reviewer` verdict: **LGTM**, 5 minor non-blocking — все применены в main session (per-row commit comments, O(N²)→O(N) count, skip trailing sleep, redundant index removed, bare except → logger.debug).
Light1YT added 1 commit 2026-05-25 07:51:01 +00:00
Stratified 200-house Yandex reverse-geocode audit comparing our DB's
(address, lat/lon) pair against what Yandex.Карты returns for those
coordinates. Лежит фундамент данных для Phases 2-5 (canonical = Yandex
per user decision).

What ships:
- Migration 066: address_mismatch_audit table + FDW foreign table
  gendesign_ekb_districts_geom для stratified sampling.
- Sampling SQL: 8 EKB districts × 25 houses = 200.
- Driver scripts/audit_address_mismatch.py с двумя режимами:
  API (если YANDEX_GEOCODER_API_KEY есть) и Playwright (CAPTCHA-aware,
  персистентный контекст). Resumable по --batch.
- Report SQL: p50/p75/p95 distance, top-20 outliers, per-district breakdown.
- 19 unit-тестов: normalize, distance bind, API parser, resume idempotency,
  captcha → status=blocked.

Devops note: после деплоя миграции — выдать tradein_fdw_reader SELECT на
gendesign.public.ekb_districts_geom перед первым запуском audit.

Refs #582 (Phase 1 of 5). Phase 3 backfill заблокирован до Yandex API
key (#402).
Light1YT merged commit fc12b2c160 into main 2026-05-26 05:18:56 +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#583
No description provided.