""" 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))