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
61 lines
2.8 KiB
Python
61 lines
2.8 KiB
Python
"""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()
|