170 lines
7.2 KiB
Python
170 lines
7.2 KiB
Python
"""Celery tasks: еженедельная синхронизация точек подключения со свободной
|
||
мощностью — электро (Россети WFS + xlsx-резервы) и вода (Водоканал docx). #2119.
|
||
|
||
Источники ГЕО-БЛОКИРУЮТ non-RU IP → таски выполняются НА ПРОДЕ (Celery worker
|
||
на VPS). Persistent breadcrumbs в nspd_geo_log (как utility/poi/noise 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("connection_capacity breadcrumb failed: %s", e)
|
||
|
||
|
||
@celery_app.task(
|
||
name="tasks.connection_capacity_sync.sync_power_supply_centers",
|
||
queue="celery",
|
||
)
|
||
def sync_power_supply_centers() -> dict:
|
||
"""WFS центры питания + xlsx-резервы (ЦП + ТП/РП) → power_supply_centers / ТП-РП.
|
||
|
||
RUN-ON-PROD (гео-блок источников). Сначала WFS (координаты + напряжение +
|
||
индекс загрузки), затем резервы из xlsx-раскрытия. Каждый шаг graceful —
|
||
сбой резервов не откатывает загруженные WFS-точки.
|
||
"""
|
||
_log_breadcrumb("power_capacity_sync", "info", "task started")
|
||
result: dict = {}
|
||
try:
|
||
from app.services.site_finder.rosseti_wfs_loader import load_power_supply_centers
|
||
|
||
result["wfs"] = load_power_supply_centers()
|
||
except Exception as e:
|
||
logger.exception("sync_power_supply_centers: WFS failed: %s", e)
|
||
_log_breadcrumb("power_capacity_sync", "error", f"wfs {type(e).__name__}: {e}")
|
||
result["wfs"] = {"error": str(e)}
|
||
|
||
try:
|
||
from app.services.site_finder.rosseti_reserve_loader import load_all_reserves
|
||
|
||
result["reserves"] = load_all_reserves()
|
||
except Exception as e:
|
||
logger.exception("sync_power_supply_centers: reserves failed: %s", e)
|
||
_log_breadcrumb("power_capacity_sync", "error", f"reserves {type(e).__name__}: {e}")
|
||
result["reserves"] = {"error": str(e)}
|
||
|
||
logger.info("sync_power_supply_centers done: %s", result)
|
||
_log_breadcrumb("power_capacity_sync", "info", f"done: {result}")
|
||
return result
|
||
|
||
|
||
@celery_app.task(
|
||
name="tasks.connection_capacity_sync.sync_water_reserves",
|
||
queue="celery",
|
||
)
|
||
def sync_water_reserves() -> dict:
|
||
"""Резервы ЦСВ/ЦСК Водоканала ЕКБ (docx) → water_supply_reserves.
|
||
|
||
RUN-ON-PROD (гео-блок). Обе страницы (водоснабжение/водоотведение) graceful.
|
||
"""
|
||
_log_breadcrumb("water_capacity_sync", "info", "task started")
|
||
try:
|
||
from app.services.site_finder.vodokanal_reserve_loader import load_water_reserves
|
||
|
||
result = load_water_reserves()
|
||
logger.info("sync_water_reserves done: %s", result)
|
||
_log_breadcrumb("water_capacity_sync", "info", f"done: {result}")
|
||
return result
|
||
except Exception as e:
|
||
logger.exception("sync_water_reserves failed: %s", e)
|
||
_log_breadcrumb("water_capacity_sync", "error", f"{type(e).__name__}: {e}")
|
||
raise
|
||
|
||
|
||
@celery_app.task(
|
||
name="tasks.connection_capacity_sync.sync_gas_grs_capacity",
|
||
queue="celery",
|
||
)
|
||
def sync_gas_grs_capacity() -> dict:
|
||
"""Свободная мощность ГРС «Газпром трансгаз Екатеринбург» (HTML) → gas_grs_capacity.
|
||
|
||
RUN-ON-PROD (гео-блок источника). Инлайн HTML-таблица раскрытия ГТЕ → UPSERT
|
||
свободной мощности ГРС (по выходам). #2119 Фаза B1.
|
||
"""
|
||
_log_breadcrumb("gas_grs_sync", "info", "task started")
|
||
try:
|
||
from app.services.site_finder.gazprom_grs_loader import load_grs_capacity
|
||
|
||
result = load_grs_capacity()
|
||
logger.info("sync_gas_grs_capacity done: %s", result)
|
||
_log_breadcrumb("gas_grs_sync", "info", f"done: {result}")
|
||
return result
|
||
except Exception as e:
|
||
logger.exception("sync_gas_grs_capacity failed: %s", e)
|
||
_log_breadcrumb("gas_grs_sync", "error", f"{type(e).__name__}: {e}")
|
||
raise
|
||
|
||
|
||
@celery_app.task(
|
||
name="tasks.connection_capacity_sync.sync_eesk_reserves",
|
||
queue="celery",
|
||
)
|
||
def sync_eesk_reserves() -> dict:
|
||
"""Резервы АО «ЕЭСК» (xlsx-раскрытие) → power_supply_centers (ПС 35-220, МВт) +
|
||
power_tp_rp_reserves (ТП/РП «ниже 35»). #2119 Фаза B2.
|
||
|
||
RUN-ON-PROD рядом с rosseti/gas/vodokanal (единый weekly-кластер). discovery
|
||
свежайшего xlsx + загрузка ПС и ТП/РП — каждый файл graceful (сбой одного не
|
||
валит другой; лоадер сам изолирует per-row SAVEPOINT-ами).
|
||
"""
|
||
_log_breadcrumb("eesk_reserves_sync", "info", "task started")
|
||
try:
|
||
from app.services.site_finder.eesk_reserve_loader import load_eesk_reserves
|
||
|
||
result = load_eesk_reserves()
|
||
logger.info("sync_eesk_reserves done: %s", result)
|
||
_log_breadcrumb("eesk_reserves_sync", "info", f"done: {result}")
|
||
return result
|
||
except Exception as e:
|
||
logger.exception("sync_eesk_reserves failed: %s", e)
|
||
_log_breadcrumb("eesk_reserves_sync", "error", f"{type(e).__name__}: {e}")
|
||
raise
|
||
|
||
|
||
@celery_app.task(
|
||
name="tasks.connection_capacity_sync.sync_heat_reserves",
|
||
queue="celery",
|
||
)
|
||
def sync_heat_reserves() -> dict:
|
||
"""Резервы тепловой мощности ФАС (ri.eias.ru) → heat_system_reserves. #2119 B2.
|
||
|
||
RUN-ON-PROD (гео-блок non-RU + российский УЦ). По всем организациям-держателям
|
||
(ЕТК, Екатеринбургэнерго): карточка ФАС → публикации свежайшего квартала →
|
||
выгрузки xlsx → UPSERT. Каждая организация graceful (падение одной не валит вторую).
|
||
"""
|
||
_log_breadcrumb("heat_reserves_sync", "info", "task started")
|
||
try:
|
||
from app.services.site_finder.eias_heat_loader import load_heat_reserves
|
||
|
||
result = load_heat_reserves()
|
||
logger.info("sync_heat_reserves done: %s", result)
|
||
_log_breadcrumb("heat_reserves_sync", "info", f"done: {result}")
|
||
return result
|
||
except Exception as e:
|
||
logger.exception("sync_heat_reserves failed: %s", e)
|
||
_log_breadcrumb("heat_reserves_sync", "error", f"{type(e).__name__}: {e}")
|
||
raise
|