feat(sf): opportunity-ЗУ harvest → ird_overlays (future-supply, #1086) #1100

Merged
bot-reviewer merged 1 commit from feat/1086-opportunity-harvest into main 2026-06-06 19:32:46 +00:00
Owner

Closes #1086.

Future-supply: НСПД opportunity-ЗУ по агломерации ЕКБ

Свободная / выставляемая под застройку земля — DIRECT-сигналы «земля под застройку». В центре города такие ЗУ редки (точечный probe = 0), но по агломерации присутствуют → сплошной grid-walk по кварталам, как в ИРД-harvest.

Что добавлено

  • backend/app/workers/tasks/opportunity_harvest.py — Celery-task harvest_opportunity_overlays: grid-walk opportunity-слоёв НСПД по кварталам ЕКБ (66:41:*) → upsert в ird_overlays (м.132) с layer_kind='opportunity_*'.
    • Переиспользует движок ИРД-harvest (не дублирует): тот же _upsert_feature (ST_Transform 3857→4326, SAVEPOINT per-row, ON CONFLICT по (source_layer_id, geom_data_id)), те же кварталы _ekb_quarters. Отличие — только набор слоёв + opportunity_* бакет.
  • nspd_client.LAYERS: добавлен krt_territories: 37430 (территории ККР). Остальные 4 opportunity-слоя (37299/37294/37298/36473) уже были.
  • celery_app.py include[] + beat_schedule.py opportunity-harvest-monthly (1-е число месяца 05:30 МСК — реже ИРД weekly, opportunity-земля меняется медленнее).
  • tests/workers/test_opportunity_harvest.py — 3 теста (mock NSPDClient+SessionLocal): grid-walk по всем слоям + layer_kind prefix, skip пустого квартала, регистрация ККР.

Opportunity-слои → layer_kind

LAYERS key NSPD id layer_kind
auction_parcels 37299 opportunity_auction
scheme_parcels 37294 opportunity_scheme
free_parcels 37298 opportunity_free
future_parcels 36473 opportunity_future
krt_territories 37430 opportunity_krt

Валидация

  • pytest test_opportunity_harvest.py test_ird_harvest.py6 passed (общий движок не сломан)
  • ruff check → clean (line-100)
  • import-smoke: celery_app + build_beat_schedule()opportunity-harvest-monthly зарегистрирован (crontab 30 5 1 * * )

Scope

Self-contained: НЕ трогает parcels.py. analyze-чтение opportunity-overlay — через тот же ird_overlay_lookup (фильтр по layer_kind LIKE 'opportunity_%'), wiring в отдельном flag-gated PR. Прогон на VPS — gated до деплоя (B6).

Closes #1086. ## Future-supply: НСПД opportunity-ЗУ по агломерации ЕКБ Свободная / выставляемая под застройку земля — DIRECT-сигналы «земля под застройку». В центре города такие ЗУ редки (точечный probe = 0), но по агломерации присутствуют → **сплошной grid-walk** по кварталам, как в ИРД-harvest. ### Что добавлено - **`backend/app/workers/tasks/opportunity_harvest.py`** — Celery-task `harvest_opportunity_overlays`: grid-walk opportunity-слоёв НСПД по кварталам ЕКБ (66:41:*) → upsert в `ird_overlays` (м.132) с `layer_kind='opportunity_*'`. - **Переиспользует движок ИРД-harvest** (не дублирует): тот же `_upsert_feature` (ST_Transform 3857→4326, SAVEPOINT per-row, ON CONFLICT по `(source_layer_id, geom_data_id)`), те же кварталы `_ekb_quarters`. Отличие — только набор слоёв + `opportunity_*` бакет. - **`nspd_client.LAYERS`**: добавлен `krt_territories: 37430` (территории ККР). Остальные 4 opportunity-слоя (37299/37294/37298/36473) уже были. - **`celery_app.py`** include[] + **`beat_schedule.py`** `opportunity-harvest-monthly` (1-е число месяца 05:30 МСК — реже ИРД weekly, opportunity-земля меняется медленнее). - **`tests/workers/test_opportunity_harvest.py`** — 3 теста (mock NSPDClient+SessionLocal): grid-walk по всем слоям + `layer_kind` prefix, skip пустого квартала, регистрация ККР. ### Opportunity-слои → layer_kind | LAYERS key | NSPD id | layer_kind | |---|---|---| | auction_parcels | 37299 | opportunity_auction | | scheme_parcels | 37294 | opportunity_scheme | | free_parcels | 37298 | opportunity_free | | future_parcels | 36473 | opportunity_future | | krt_territories | 37430 | opportunity_krt | ### Валидация - `pytest test_opportunity_harvest.py test_ird_harvest.py` → **6 passed** (общий движок не сломан) - `ruff check` → clean (line-100) - import-smoke: `celery_app` + `build_beat_schedule()` → `opportunity-harvest-monthly` зарегистрирован (crontab `30 5 1 * *` ✅) ### Scope Self-contained: НЕ трогает `parcels.py`. analyze-чтение opportunity-overlay — через тот же `ird_overlay_lookup` (фильтр по `layer_kind LIKE 'opportunity_%'`), wiring в отдельном flag-gated PR. Прогон на VPS — gated до деплоя (B6).
lekss361 added 1 commit 2026-06-06 19:23:42 +00:00
feat(sf): opportunity-ЗУ harvest → ird_overlays (future-supply, #1086)
All checks were successful
CI / changes (push) Successful in 6s
CI / changes (pull_request) Successful in 7s
CI / backend-tests (push) Successful in 5m49s
CI / backend-tests (pull_request) Successful in 5m47s
34616042cf
lekss361 added the
scope/backend
site-finder
status/review
labels 2026-06-06 19:23:48 +00:00
bot-reviewer reviewed 2026-06-06 19:32:35 +00:00
bot-reviewer left a comment
Collaborator

APPROVE

Тонкая обёртка над уже отревьюенным движком #1090 (DRY — _upsert_feature/_ekb_quarters импортируются, не дублируются):

  • Движок reviewed: тот же SAVEPOINT per-feature / ON CONFLICT (source_layer_id, geom_data_id) / ST_Transform / per-quarter-layer-feature error-isolation / commit-once / db.close finally (верифицирован в #1090).
  • LAYERS: krt_territories:37430 добавлен; test_krt_layer_registered верифицирует. Все 5 opportunity-ключей резолвятся — test_harvest_explicit_quarters ассертит ==5 executes (real LAYERS, не мокнут) → 6 passed подтверждает полноту набора.
  • layer_kind opportunity_* в ird_overlays.layer_kind (TEXT, без enum в м.132) — новые значения валидны; analyze-фильтр LIKE 'opportunity_%'.
  • Beat 30 5 1 * * (monthly) без коллизий (ird-harvest mon 05:00, прочие иные). celery_app.include корректен.
  • Self-contained, parcels.py не тронут, прод-прогон gated в B6.

Минор (advisory): cross-module импорт приватных _upsert_feature/_ekb_quarters — приемлемо (DRY > дублирование движка), консистентно с #1090.

Merge → deploy-verify.

<!-- gendesign-review-bot: sha=3461604 verdict=approve --> ✅ **APPROVE** Тонкая обёртка над уже отревьюенным движком #1090 (DRY — `_upsert_feature`/`_ekb_quarters` импортируются, не дублируются): - **Движок reviewed**: тот же SAVEPOINT per-feature / ON CONFLICT (source_layer_id, geom_data_id) / ST_Transform / per-quarter-layer-feature error-isolation / commit-once / db.close finally (верифицирован в #1090). - **LAYERS**: `krt_territories:37430` добавлен; `test_krt_layer_registered` верифицирует. Все 5 opportunity-ключей резолвятся — `test_harvest_explicit_quarters` ассертит `==5 executes` (real LAYERS, не мокнут) → 6 passed подтверждает полноту набора. - **layer_kind** `opportunity_*` в `ird_overlays.layer_kind` (TEXT, без enum в м.132) — новые значения валидны; analyze-фильтр `LIKE 'opportunity_%'`. - **Beat** `30 5 1 * *` (monthly) без коллизий (ird-harvest mon 05:00, прочие иные). celery_app.include корректен. - Self-contained, parcels.py не тронут, прод-прогон gated в B6. Минор (advisory): cross-module импорт приватных `_upsert_feature`/`_ekb_quarters` — приемлемо (DRY > дублирование движка), консистентно с #1090. Merge → deploy-verify.
bot-reviewer merged commit b1530467f7 into main 2026-06-06 19:32:46 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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#1100
No description provided.