klg-asutk-app/backend/app/services/risk_scheduler.py
Yuriy 0a19a03b6e fix: seed data, API 500 errors, security hardening
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-15 21:35:22 +03:00

151 lines
5.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.

"""
Scheduled risk scanner — runs periodically to detect new risks.
Uses APScheduler for lightweight background scheduling.
Production: migrate to Celery + Redis for distributed workers.
"""
import logging
from datetime import datetime, timezone
from contextlib import contextmanager
from app.db.session import SessionLocal
from app.services.risk_scanner import scan_risks
logger = logging.getLogger(__name__)
# Track last scan time
_last_scan: datetime | None = None
@contextmanager
def _get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
def run_scheduled_scan():
"""Run a full risk scan across all aircraft (scan_risks обрабатывает все ВС за один вызов)."""
global _last_scan
logger.info("Starting scheduled risk scan...")
with _get_db() as db:
try:
total_created = scan_risks(db)
db.commit()
except Exception as e:
logger.error("Risk scan error: %s", e)
raise
_last_scan = datetime.now(timezone.utc)
logger.info("Scheduled scan complete: %s new risks", total_created)
return total_created
def get_last_scan_time() -> datetime | None:
return _last_scan
def setup_scheduler(app=None):
"""Setup background scheduler. Без app — заглушка (logger.info). С app — запуск APScheduler."""
if app is None:
logger.info("Risk scheduler: stub — планировщик не сконфигурирован (вызовите setup_scheduler(app) при старте).")
return
try:
from apscheduler.schedulers.background import BackgroundScheduler
scheduler = BackgroundScheduler()
scheduler.add_job(run_scheduled_scan, 'interval', hours=6, id='risk_scan', next_run_time=None)
scheduler.start()
logger.info("Risk scanner scheduler started (interval: 6h)")
@app.on_event("shutdown")
def shutdown_scheduler():
scheduler.shutdown()
except ImportError:
logger.warning("APScheduler not installed — scheduled scans disabled. pip install apscheduler")
# ===================================================================
# ФГИС РЭВС: автоматическая синхронизация (каждые 24ч)
# ===================================================================
def scheduled_fgis_sync():
"""
Периодическая синхронизация с ФГИС РЭВС.
Выполняется каждые 24 часа (настраивается).
Порядок:
1. Pull реестра ВС → обновление локальной БД
2. Pull СЛГ → проверка сроков действия
3. Pull новых ДЛГ → создание записей + risk alerts
4. Log результатов
"""
import logging
logger = logging.getLogger(__name__)
try:
from app.services.fgis_revs import fgis_client
from app.api.routes.fgis_revs import _sync_state
if not _sync_state.get("auto_sync_enabled", True):
logger.info("ФГИС auto-sync disabled, skipping")
return
logger.info("=== ФГИС РЭВС auto-sync started ===")
# 1. Sync aircraft
r1 = fgis_client.sync_aircraft()
logger.info("Aircraft: %s (%d/%d)", r1.status, r1.records_synced, r1.records_total)
# 2. Sync certificates
r2 = fgis_client.sync_certificates()
logger.info("Certificates: %s (%d/%d)", r2.status, r2.records_synced, r2.records_total)
# 3. Sync directives (last 30 days)
r3 = fgis_client.sync_directives(since_days=30)
logger.info("Directives: %s (%d/%d)", r3.status, r3.records_synced, r3.records_total)
# 4. Check for new mandatory ADs → create risk alerts
if r3.records_synced > 0:
from app.api.routes.airworthiness_core import _directives
new_mandatory = [d for d in _directives.values()
if d.get("source") == "ФГИС РЭВС"
and d.get("compliance_type") == "mandatory"
and d.get("status") == "open"]
if new_mandatory:
logger.warning("⚠️ %d new mandatory ADs from ФГИС РЭВС!", len(new_mandatory))
# Create risk alerts
from app.api.routes.risk_alerts import _alerts
import uuid
from datetime import datetime, timezone
for ad in new_mandatory:
aid = str(uuid.uuid4())
_alerts[aid] = {
"id": aid,
"title": f"Новая обязательная ДЛГ из ФГИС: {ad['number']}",
"severity": "critical",
"category": "fgis_directive",
"status": "open",
"source": "ФГИС РЭВС auto-sync",
"entity_type": "directive",
"entity_id": ad["id"],
"created_at": datetime.now(timezone.utc).isoformat(),
}
# 5. Check expired certificates → alerts
from app.services.fgis_revs import fgis_client as fc
certs = fc.pull_certificates()
expired = [c for c in certs if c.status == "expired"]
if expired:
logger.warning("⚠️ %d expired certificates found in ФГИС!", len(expired))
from datetime import datetime, timezone
_sync_state["last_sync"] = datetime.now(timezone.utc).isoformat()
logger.info("=== ФГИС РЭВС auto-sync completed ===")
except Exception as e:
logger.error("ФГИС auto-sync error: %s", str(e))