feat(tradein/estimator): deal_city_price_bands по ключу (region_code, city) — миграция 298, refresh per-region (#3051 sub-PR B) #3431

Merged
lekss361 merged 1 commit from feat/3051-bands-region-key into main 2026-09-08 23:32:47 +00:00
Owner

Sub-PR B трека «Москва» (#3051). Предпосылка: в deals теперь 212 937 московских ДКП (region 77) рядом со 108 623 строками 66. Refresh bands считал region_stats (p1 → floor tier region_fallback) по всем не-ЕКБ сделкам — с Москвой в пуле floor ~250 малых городов области уехал бы вверх и их сделки вырезались бы из коридоров. Расписание deal_city_price_bands_refresh на проде выключено main-сессией до деплоя этого PR (следующий запуск был бы 07:04 UTC 09.09).

Что

  • Миграция 298 deal_city_price_bands_region.sql: ADD COLUMN IF NOT EXISTS region_code int NOT NULL DEFAULT 66; смена PK (city)(region_code, city) через DO-guard по pg_get_constraintdef (идемпотентно при обоих состояниях); страховочный DELETE строки Москва в регионе 66; re-seed той же derivation, что 194, но GROUP BY region_code[, city], region_stats JOIN по региону вместо CROSS JOIN, исключение ЕКБ только как NOT (region_code=66 AND city='Екатеринбург'), doc_type='ДКП'; SET LOCAL lock_timeout.
  • app/tasks/deal_city_price_bands_refresh.py: _REDERIVE_SQL = тот же SQL (сверено построчно), counters + regions.
  • estimator.py: _load_city_price_bands{(region_code, city): (min, max)}; _is_plausible_deal(..., region_code=DEFAULT_REGION_CODE) (позиционные вызовы не тронуты); _fetch_deals берёт region_code из строки (NULL → 66); оба LEFT JOIN deal_city_price_bands в _fetch_dkp_corridorON b.region_code = d.region_code AND b.city = d.city.
  • scripts/backtest_estimator.py: band по региону (дефолт 66, CLI-флага нет — out of scope).

Регрессия 66

Набор (city, ppm2_min, ppm2_max, n_deals, tier) для 66 байт-идентичен прежнему: те же фильтры/пороги 30/10/клампы 8000/800000/700000; doc_type='ДКП' не сужает (backfill 288 — 100% строк); Москва больше не подмешивается в pooled p1. После деплоя: разовый refresh и сверка со snapshot deal_city_price_bands_bak_0908 (383 строки, снят до правок).

ruff чисто; pytest 76 passed / 2 skipped по затронутым тестам. Ревью: две линзы (регрессия 66; миграция + потребители) — APPROVE обе. Тесты по новому ключу — отдельным заходом после живого refresh.

Refs #3051. Связано: #3422, #3423, #3430.

🤖 Generated with Claude Code

Sub-PR B трека «Москва» (#3051). Предпосылка: в `deals` теперь 212 937 московских ДКП (region 77) рядом со 108 623 строками 66. Refresh bands считал `region_stats` (p1 → floor tier `region_fallback`) по **всем** не-ЕКБ сделкам — с Москвой в пуле floor ~250 малых городов области уехал бы вверх и их сделки вырезались бы из коридоров. Расписание `deal_city_price_bands_refresh` на проде **выключено** main-сессией до деплоя этого PR (следующий запуск был бы 07:04 UTC 09.09). ## Что - **Миграция 298** `deal_city_price_bands_region.sql`: `ADD COLUMN IF NOT EXISTS region_code int NOT NULL DEFAULT 66`; смена PK `(city)` → `(region_code, city)` через DO-guard по `pg_get_constraintdef` (идемпотентно при обоих состояниях); страховочный `DELETE` строки `Москва` в регионе 66; re-seed той же derivation, что 194, но `GROUP BY region_code[, city]`, `region_stats` JOIN по региону вместо CROSS JOIN, исключение ЕКБ только как `NOT (region_code=66 AND city='Екатеринбург')`, `doc_type='ДКП'`; `SET LOCAL lock_timeout`. - `app/tasks/deal_city_price_bands_refresh.py`: `_REDERIVE_SQL` = тот же SQL (сверено построчно), counters + `regions`. - `estimator.py`: `_load_city_price_bands` → `{(region_code, city): (min, max)}`; `_is_plausible_deal(..., region_code=DEFAULT_REGION_CODE)` (позиционные вызовы не тронуты); `_fetch_deals` берёт `region_code` из строки (NULL → 66); оба `LEFT JOIN deal_city_price_bands` в `_fetch_dkp_corridor` — `ON b.region_code = d.region_code AND b.city = d.city`. - `scripts/backtest_estimator.py`: band по региону (дефолт 66, CLI-флага нет — out of scope). ## Регрессия 66 Набор `(city, ppm2_min, ppm2_max, n_deals, tier)` для 66 байт-идентичен прежнему: те же фильтры/пороги 30/10/клампы 8000/800000/700000; `doc_type='ДКП'` не сужает (backfill 288 — 100% строк); Москва больше не подмешивается в pooled p1. После деплоя: разовый refresh и сверка со snapshot `deal_city_price_bands_bak_0908` (383 строки, снят до правок). `ruff` чисто; `pytest` 76 passed / 2 skipped по затронутым тестам. Ревью: две линзы (регрессия 66; миграция + потребители) — ✅ APPROVE обе. Тесты по новому ключу — отдельным заходом после живого refresh. Refs #3051. Связано: #3422, #3423, #3430. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
lekss361 added 1 commit 2026-09-08 23:26:13 +00:00
feat(tradein/estimator): deal_city_price_bands по ключу (region_code, city) — миграция 298, refresh per-region, потребители (#3051 sub-PR B)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI / openapi-codegen-check (pull_request) Has been skipped
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 Trade-In / backend-tests (pull_request) Successful in 5m10s
1d9adb4a24
После импорта 212 937 московских ДКП (region_code=77) region_stats
refresh'а (p1 для tier region_fallback) считался бы по пулу 66+77 и
поднял бы floor ~250 малым городам области. Ключ bands становится
(region_code, city): миграция 298 (колонка DEFAULT 66, смена PK через
DO-guard, re-seed per-region, ЕКБ-исключение только для 66, doc_type=ДКП),
тот же SQL в refresh-джобе, estimator/backtest читают bands по паре.
Для 66 набор строк байт-идентичен прежнему (ревью двумя линзами).
lekss361 merged commit 12476df7a2 into main 2026-09-08 23:32:47 +00:00
lekss361 deleted branch feat/3051-bands-region-key 2026-09-08 23:32:47 +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#3431
No description provided.