Files
scud_ai/services/scud_etl/svodka_generator.py
T

89 lines
4.0 KiB
Python

"""
===============================================================================
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_best_snapshot_for_date, 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]:
"""
Формирует оперативную сводку:
- По умолчанию берет ПОСЛЕДНИЙ готовый снапшот из базы SQLite (без долгого опроса MS SQL).
"""
date_clean = str(target_date or DATE_TODAY).replace('_', '.')
applied_note = ""
# 1. Загрузка среза СКУД строго из готовых в базе
if snapshot_id:
df_scud = load_scud_from_db_by_snapshot(date_clean, snapshot_param=snapshot_id)
elif target_time:
found_id, note = find_or_create_snapshot_for_time(date_clean, target_time, allow_ondemand_export=False)
applied_note = note
df_scud = load_scud_from_db_by_snapshot(date_clean, snapshot_param=found_id)
else:
# По умолчанию: берем самый свежий существующий срез за дату
df_scud = load_best_snapshot_for_date(date_clean, prefer_final_y=False)
if df_scud is None or df_scud.empty:
return {
"status": "error",
"message": f"Срез СКУД за {date_clean} не найден в базе данных."
}
# Извлекаем время фактического среза для имени файла
actual_snap_time = ""
if 'snapshot_time' in df_scud.columns and not df_scud.empty:
raw_st = str(df_scud['snapshot_time'].iloc[0]).strip()
if " " in raw_st:
actual_snap_time = raw_st.split()[1][:5].replace(':', '-')
# 2. Загружаем актуальные кадровые данные 1С (штат + отсутствия + реестры)
df_staff, df_abs = load_1c_files_for_date(date_clean)
# 3. Слияние и расчет
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)
# 4. Формирование книги Excel
time_suffix = f" на {actual_snap_time}" if actual_snap_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": actual_snap_time,
"snapshot_id": snapshot_id or "LATEST_READY",
"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} успешно сформирована."
}