Some checks failed
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Failing after 1m56s
CI / backend-tests (pull_request) Successful in 14m37s
Ранний сигнал future-supply: разрешения на строительство появляются в ГИСОГД до проектных деклараций ЕИСЖС. Старый minstroy-путь мёртв (WAF), портал gisogd66.midural.ru отдаёт открытый JSON-API без авторизации. Миграция 187_gisogd_permits (RS+RV, UNIQUE(doc_group,doc_num), GIST geom 4326, GIN cad_nums) + инкрементальный httpx-лоадер (схемы agate_ekbgo + agate_sverdregion, сверка по (source_key,date_reg), карточки только для новых/изменённых, SAVEPOINT per-row, WHERE-гейт предпочтения позднего date_reg) + Celery weekly (вт 06:30 МСК, limit 3600/3300) + on-demand admin-триггер + freshness-реестр (data-table, 14/45). Refs #2367
69 lines
3.1 KiB
Python
69 lines
3.1 KiB
Python
"""Celery task: инкрементальная синхронизация реестра РНС/РВЭ ГИСОГД-СО (#2367).
|
||
|
||
Источник: открытый JSON-API gisogd66.midural.ru (раздел 13), БЕЗ авторизации и БЕЗ
|
||
гео-блока — задача может выполняться и локально, и на проде. Держим weekly (обновляется
|
||
ежедневно, но лоадер инкрементален и дёшев при отсутствии изменений). Persistent
|
||
breadcrumbs в nspd_geo_log (как connection_capacity_sync). Import-light: тяжёлый
|
||
загрузчик импортируется лениво внутри таска.
|
||
"""
|
||
|
||
import logging
|
||
|
||
from sqlalchemy import text
|
||
|
||
from app.workers.celery_app import celery_app
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
def _log_breadcrumb(stage: str, level: str, message: str) -> None:
|
||
"""Записать строку в nspd_geo_log (job_id=NULL) — диагностика без SSH."""
|
||
try:
|
||
from app.core.db import SessionLocal
|
||
|
||
db = SessionLocal()
|
||
try:
|
||
db.execute(
|
||
text(
|
||
"INSERT INTO nspd_geo_log (job_id, level, stage, message) "
|
||
"VALUES (NULL, :lvl, :stage, :msg)"
|
||
),
|
||
{"lvl": level, "stage": stage, "msg": message[:500]},
|
||
)
|
||
db.commit()
|
||
finally:
|
||
db.close()
|
||
except Exception as e:
|
||
logger.warning("gisogd_permits breadcrumb failed: %s", e)
|
||
|
||
|
||
@celery_app.task(
|
||
name="tasks.gisogd_permits_sync.sync_gisogd_permits",
|
||
queue="celery",
|
||
# Холодный прогон ~6k карточек (2 схемы × RS+RV) при politeness-sleep укладывается
|
||
# в ~25 мин; щедрый лимит по конвенции full_report/forecast, чтобы beat-ран не убили
|
||
# mid-upsert. Инкрементальные прогоны — минуты.
|
||
time_limit=3600,
|
||
soft_time_limit=3300,
|
||
)
|
||
def sync_gisogd_permits() -> dict:
|
||
"""Инкрементально загрузить РНС/РВЭ ГИСОГД-СО (обе схемы, обе группы) → gisogd_permits.
|
||
|
||
Список группы → сверка с БД по (source_key, date_reg) → карточки только для
|
||
новых/изменившихся → UPSERT по (doc_group, doc_num) с предпочтением позднего date_reg.
|
||
|
||
Returns: счётчики {listed, fetched_details, inserted, updated, skipped_unchanged,
|
||
no_cad, no_geom}.
|
||
"""
|
||
_log_breadcrumb("gisogd_permits_sync", "info", "task started")
|
||
try:
|
||
from app.services.scrapers.gisogd66 import load_gisogd_permits
|
||
|
||
result = load_gisogd_permits()
|
||
logger.info("sync_gisogd_permits done: %s", result)
|
||
_log_breadcrumb("gisogd_permits_sync", "info", f"done: {result}")
|
||
return result
|
||
except Exception as e:
|
||
logger.exception("sync_gisogd_permits failed: %s", e)
|
||
_log_breadcrumb("gisogd_permits_sync", "error", f"{type(e).__name__}: {e}")
|
||
raise
|