gendesign/backend/app/workers/tasks/backfill_zone_regulations.py
Light1YT 2ff34b1500
Some checks failed
CI / changes (pull_request) Successful in 7s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Failing after 1m48s
CI / backend-tests (pull_request) Successful in 9m56s
feat(site-finder): backfill ЕКБ зон-регламента — разблокировать видимость финмодели (#1881)
financial_estimate (эпик #1881) фаерит только при разрезолвленном НСПД-регламенте
(max_far), а он резолвится ЛЕНИВО при анализе участка → zone_regulation_cache всего
33 зоны → у большинства участков financial_estimate=None («—» в кокпите). Backfill
проактивно кэширует ВСЕ террзоны ЕКБ (~100-120) → ЛЮБОй участок в известной зоне
сразу получает регламент и финмодель.

- backfill_ekb_zone_regulations (zone_regulation.py): WFS-перечисление террзон ЕКБ
  (features_in_bbox 'territorial_zone') → dedup по urban_index (предпочёт фичу с
  геометрией) → representative_point (внутри полигона, не centroid) → zone_regulation_at
  → upsert. ИДЕМПОТЕНТНО (cached skip перед fetch), per-zone try/except (один сбой не
  рушит batch), rate_delay вежливость к геопорталу, limit для частичного прогона.
- Celery task backfill_zone_regulations + регистрация в celery_app include +
  admin POST /api/v1/admin/scrape/zone-regulations/backfill (паттерн objective sync-our).
- Ленивый refresh_zone_regulations / get_or_fetch / upsert НЕ тронуты (additive путь).
- +9 тестов (dedup, idempotency cached-skip, per-zone error isolation, centroid-inside,
  limit). mypy/ruff clean.

Прод-прогон backfill — отдельный шаг после deploy (как Objective sync), не на deploy.
Code-review поймал незарегистрированный task (был бы NotRegistered) — исправлено.

Refs #1881
2026-06-24 02:11:41 +05:00

61 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Celery task: backfill кэша ПЗЗ-регламента ВСЕХ терзон ЕКБ (эпик #1881).
В отличие от ``zone_regulation_refresh.refresh_zone_regulations`` (ленивый прогрев, beat
~ежемесячно) эта задача — явный одноразовый/ad-hoc backfill, чтобы ``financial_estimate`` (#1881)
фаерил для большинства участков: ``max_far`` резолвится из ``zone_regulation_cache`` без
live-urbanCard в hot-пути. На момент задачи в кэше всего ~33 зоны → задача докэширует остальные.
Идемпотентно: ``backfill_ekb_zone_regulations`` пропускает уже закэшированные зоны (cached skip),
так что повторный запуск дешёв (только WFS-перечисление). Per-zone try/except в сервисе —
сбой одной зоны не валит прогон. ``rate_delay_s`` — вежливость к геопорталу.
Mirror conventions (zone_regulation_refresh.py / backend.md): SessionLocal()+try/finally,
sync (не async), commit per-upsert внутри сервиса.
"""
from __future__ import annotations
import logging
from app.core.db import SessionLocal
from app.services.scrapers.ekb_geoportal_client import EKBGeoportalClient
from app.services.site_finder.zone_regulation import (
_EKB_CITY_BBOX,
backfill_ekb_zone_regulations,
)
from app.workers.celery_app import celery_app
logger = logging.getLogger(__name__)
@celery_app.task(name="tasks.backfill_zone_regulations.backfill_zone_regulations")
def backfill_zone_regulations(
bbox: tuple[float, float, float, float] | None = None,
limit: int | None = None,
rate_delay_s: float = 1.0,
) -> dict[str, int]:
"""Backfill ``zone_regulation_cache`` по ВСЕМ терзонам ЕКБ (эпик #1881).
Args:
bbox: (minlon, minlat, maxlon, maxlat) EPSG:4326. None → дефолтный bbox ЕКБ.
limit: максимум уникальных незакэшированных зон к фетчу (None = все). Для smoke-теста.
rate_delay_s: пауза (сек) между live-fetch'ами зон — вежливость к геопорталу.
Returns:
{'enumerated_features', 'unique_zones', 'cached_before', 'fetched', 'upserted', 'failed'}.
"""
target_bbox = bbox if bbox is not None else _EKB_CITY_BBOX
client = EKBGeoportalClient()
db = SessionLocal()
try:
result = backfill_ekb_zone_regulations(
db,
client=client,
bbox=target_bbox,
limit=limit,
rate_delay_s=rate_delay_s,
)
logger.info("backfill_zone_regulations done: %s", result)
return result
finally:
db.close()