""" =============================================================================== FILE: services/scud_etl/svodka_generator.py ROLE: Генератор Ежедневной Сводки (оперативный контроль, текущий срез). =============================================================================== """ import os import logging from typing import Dict, Any, Optional import pandas as pd from config import DATE_TODAY from core.database import load_scud_from_db_by_snapshot from services.scud_etl.pipeline import load_1c_files_for_date from services.scud_etl.merger import merge_scud_and_1c, calculate_summary_metrics from services.scud_etl.anomaly_detector import detect_registry_anomalies from services.snapshots.finder import find_or_create_snapshot_for_time from services.excel_exporter import generate_summary_excel, get_dated_reports_dir, format_date_ru logger = logging.getLogger("SVODKA_GENERATOR") def generate_svodka_service( target_date: Optional[str] = None, target_time: Optional[str] = None, snapshot_id: Optional[str] = None ) -> Dict[str, Any]: """ Формирует оперативную сводку на указанную дату / время: - target_date: дата сводки (по умолчанию сегодня). - target_time: время среза (например '14:30'). - snapshot_id: точный ID среза. """ date_clean = str(target_date or DATE_TODAY).replace('_', '.') applied_note = "" # Если передано время, но не указан конкретный snapshot_id — ищем ближайший или запрашиваем экспорт if target_time and not snapshot_id: found_id, note = find_or_create_snapshot_for_time(date_clean, target_time, allow_ondemand_export=True) snapshot_id = found_id applied_note = note if note: logger.info(note) df_scud = load_scud_from_db_by_snapshot(date_clean, snapshot_param=snapshot_id) if df_scud is None or df_scud.empty: return { "status": "error", "message": f"Срез СКУД за {date_clean} ({applied_note or snapshot_id or 'последний доступный'}) не найден в базе." } df_staff, df_abs = load_1c_files_for_date(date_clean) df_merged = merge_scud_and_1c(df_scud, df_staff, df_abs) metrics = calculate_summary_metrics(df_merged) anomalies = detect_registry_anomalies(df_merged, df_raw_scud=df_scud) # Добавляем суффикс времени в имя файла, если сводка строилась на точный срез time_suffix = f" на {target_time.replace(':', '-')}" if target_time else "" filename = f"{format_date_ru(date_clean)} сводка{time_suffix}.xlsx" generate_summary_excel(df_merged, date_str=date_clean, filename=filename) target_dir = get_dated_reports_dir(date_clean) full_filepath = os.path.join(target_dir, filename) return { "status": "success", "report_type": "SVODKA", "date": date_clean, "target_time": target_time, "snapshot_id": snapshot_id or "AUTO_LATEST", "filename": filename, "filepath": full_filepath, "download_url": f"/api/v1/files/download/reports/{os.path.basename(full_filepath)}", "metrics": metrics, "anomalies_count": len(anomalies), "note": applied_note, "message": f"Ежедневная сводка на {date_clean} {target_time or ''} успешно сформирована." }