All checks were successful
CI / changes (pull_request) Successful in 8s
CI Trade-In / 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) Successful in 1m15s
CI / openapi-codegen-check (pull_request) Successful in 2m7s
CI / backend-tests (pull_request) Successful in 14m57s
Оркестратор build_full_report собирает §1-§7 + карты из analyze/forecast
ранов (реальная schema форсайта = "1.0"), best-effort дотягивает
connection-capacity (§3-резервы + сети рядом) и server-side концепцию
(поток POST /concepts: ConceptInput comfort → generate), рендерит
WeasyPrint'ом (локальный import — web-процесс native не тянет) на volume
/app/reports с метадата-раном report-pdf-1.0. Кэш по (analyze_run_id,
forecast_run_id) с проверкой существования файла (сирота → ре-рендер).
Запись атомарная (tmp+os.replace), имя файла из whitelist-санитизации
кад.номера. Celery task on-demand (time_limit 600), endpoint'ы POST
/{cad}/report (202/200) + status + download (дата из generated_at).
Deploy: bind-mount ./reports:/app/reports (backend+worker) + mkdir/chown
uid1000 в deploy.yml (docker создал бы root:root → PermissionError →
вечный building). reports/ в .gitignore. api-types регенерён (+219).
Refs #2259
102 lines
4.1 KiB
Python
102 lines
4.1 KiB
Python
"""Celery app — single source of truth для Celery configuration.
|
|
|
|
Beat schedule build → app/workers/beat_schedule.py.
|
|
Worker lifecycle hooks (process_init, worker_ready) → app/workers/lifecycle.py.
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
|
|
import sentry_sdk
|
|
from celery import Celery
|
|
from sentry_sdk.integrations.celery import CeleryIntegration
|
|
from sentry_sdk.integrations.httpx import HttpxIntegration
|
|
from sentry_sdk.integrations.logging import LoggingIntegration
|
|
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
|
|
|
|
from app.core.config import settings
|
|
from app.observability.sentry_scrub import scrub_sensitive_query
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# SDK инициализируется в обоих процессах (FastAPI-сервер и Celery-воркер),
|
|
# чтобы события из тасков попадали в GlitchTip. SDK безопасен для двойного
|
|
# вызова — повторный sentry_sdk.init() в одном процессе заменяет клиента.
|
|
if settings.glitchtip_dsn:
|
|
sentry_sdk.init(
|
|
dsn=settings.glitchtip_dsn,
|
|
environment=settings.environment,
|
|
release=os.getenv("GIT_SHA") or os.getenv("SENTRY_RELEASE") or "unknown",
|
|
traces_sample_rate=settings.glitchtip_traces_sample_rate,
|
|
profiles_sample_rate=0.0,
|
|
send_default_pii=False,
|
|
before_send_transaction=scrub_sensitive_query,
|
|
integrations=[
|
|
CeleryIntegration(monitor_beat_tasks=True),
|
|
SqlalchemyIntegration(),
|
|
HttpxIntegration(),
|
|
LoggingIntegration(level=logging.INFO, event_level=logging.ERROR),
|
|
],
|
|
)
|
|
logger.info(
|
|
"GlitchTip SDK initialised in Celery worker (env=%s)",
|
|
settings.environment,
|
|
)
|
|
|
|
celery_app = Celery(
|
|
"gendesign",
|
|
broker=settings.redis_url,
|
|
backend=settings.redis_url,
|
|
include=[
|
|
"app.workers.tasks.scrape_kn",
|
|
"app.workers.tasks.scrape_kn_catalog_objects",
|
|
"app.workers.tasks.refresh_analytics",
|
|
"app.workers.tasks.scrape_objective",
|
|
"app.workers.tasks.objective_etl",
|
|
"app.workers.tasks.nspd_geo",
|
|
"app.workers.tasks.nspd_sync",
|
|
"app.workers.tasks.poi_sync",
|
|
"app.workers.tasks.noise_sync",
|
|
"app.workers.tasks.utility_infrastructure_sync",
|
|
"app.workers.tasks.pzz_sync",
|
|
"app.workers.tasks.scrape_cadastre",
|
|
"app.workers.tasks.ekburg_permits_sync",
|
|
"app.workers.tasks.cbr_macro_sync",
|
|
"app.workers.tasks.rosstat_macro_sync",
|
|
"app.workers.tasks.refresh_quarter_price_index",
|
|
"app.workers.tasks.etl_newbuilding_crossload",
|
|
"app.workers.tasks.supply_layers_refresh",
|
|
"app.workers.tasks.location_refresh",
|
|
"app.workers.tasks.forecast",
|
|
"app.workers.tasks.full_report",
|
|
"app.workers.tasks.ird_harvest",
|
|
"app.workers.tasks.ekb_krt_sync",
|
|
"app.workers.tasks.gknspecial_harvest",
|
|
"app.workers.tasks.opportunity_harvest",
|
|
"app.workers.tasks.planning_harvest",
|
|
"app.workers.tasks.zone_regulation_refresh",
|
|
"app.workers.tasks.backfill_zone_regulations",
|
|
"app.workers.tasks.reservation_ingest",
|
|
"app.workers.tasks.genplan_zones_sync",
|
|
"app.workers.tasks.ekb_ppt_tep_sync",
|
|
"app.workers.tasks.krt_geometry_sync",
|
|
"app.workers.tasks.okn_objects_sync",
|
|
"app.workers.tasks.pat_subzones_load",
|
|
"app.workers.tasks.izyatie_ocr_ingest",
|
|
"app.workers.tasks.developer_registry_refresh",
|
|
"app.workers.tasks.refresh_layout_velocity",
|
|
"app.workers.tasks.riasurt_sverdl_harvest",
|
|
"app.workers.tasks.mv_sales_tracker_refresh",
|
|
"app.workers.tasks.scrape_freshness_check",
|
|
"app.workers.tasks.connection_capacity_sync",
|
|
],
|
|
)
|
|
celery_app.conf.timezone = "Europe/Moscow"
|
|
|
|
# Apply beat schedule (DB → fallback → hardcoded entries)
|
|
from app.workers.beat_schedule import build_beat_schedule # noqa: E402
|
|
|
|
celery_app.conf.beat_schedule = build_beat_schedule()
|
|
|
|
# Register lifecycle hooks (import for side-effect — signal decorator registration)
|
|
from app.workers import lifecycle # noqa: E402, F401
|