Commit graph

2 commits

Author SHA1 Message Date
20b4a195b3 feat(tradein): Phase 2-3 of #582 — backfill houses + canonical reverse audit
Builds on Phase 1 audit infra (PR #583). Forward-geocode 4141 houses
без coords + reverse audit 4452 already-geocoded houses через Yandex
Geocoder API. Verified mismatch 870м для «Малышева 125» — наш point
в другом квартале от реального здания.

Scripts (new):
- `backfill_house_coords.py` (619 LOC) — driver:
  * Forward mode: UPDATE houses SET lat/lon когда precision ∈ {exact, number}
  * `--audit-only`: reverse геокод + distance check, flag >50м как mismatch
  * Per-row SAVEPOINT, idempotent через UNIQUE (house_id, audit_batch)
  * EKB locality bias (ll=60.6122,56.8389, spn=0.6,0.4)
  * Rate-limit 50ms между запросами, 25k/день квота Yandex покрывает 8.5k houses

- `_yandex_reverse.py` (+123 LOC) — added `forward_via_api()`,
  precision/kind в YandexReverseResult dataclass

- `audit_address_sample.sql` (-7 +47) — FIX: убрана зависимость от FDW
  `gendesign_ekb_districts_geom` (нет на проде). Простой
  `ORDER BY random()` без district stratification. Bind-param контракт
  сохранён (`:limit_per_district` * 8 = 200 default).

Tests:
- `test_backfill_house_coords.py` (510 LOC) — 20 tests: request shape,
  precision filter, UPDATE shape, backfill happy/imprecise/no_match/error,
  audit ok/mismatch/no_match, resumability, missing-key exit, mode routing
- Existing `test_audit_address_mismatch.py` still green
- 39/39 pass, ruff clean

Decisions (см. PR body):
- Precision filter = {exact, number} only (street/range/near → audit-imprecise)
- Mismatch threshold 50м (consistent с Phase 1 report)
- API-only (no Playwright fallback) — exit если key missing

Closes #582 (Phase 2-3 portion; Phase 1 was #583)
2026-05-27 11:30:25 +05:00
02ad9a1f58 feat(tradein): Phase 1 of #582 — address mismatch audit infrastructure
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).
2026-05-25 12:50:30 +05:00