feat: implement svodka/otchet generators, Y-23:59:59 and snap-to-grid time finder
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
===============================================================================
|
||||
FILE: services/snapshots/finder.py
|
||||
ROLE: Поиск ближайшего снапшота в SQLite (Smart Snap-to-Grid) и On-Demand экспорт.
|
||||
===============================================================================
|
||||
"""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Optional, Tuple
|
||||
from core.connection import get_connection
|
||||
from services.scud_export import run_export
|
||||
|
||||
logger = logging.getLogger("SNAPSHOT_FINDER")
|
||||
|
||||
|
||||
def find_or_create_snapshot_for_time(
|
||||
target_date_str: str,
|
||||
target_time_str: str,
|
||||
tolerance_minutes: int = 20,
|
||||
allow_ondemand_export: bool = True
|
||||
) -> Tuple[Optional[str], str]:
|
||||
"""
|
||||
Ищет ближайший срез за указанную дату и время (±tolerance_minutes).
|
||||
Если не найден и allow_ondemand_export=True — запрашивает выгрузку из MS SQL на это время.
|
||||
|
||||
Возвращает: (snapshot_id, human_message)
|
||||
"""
|
||||
date_clean = target_date_str.replace('_', '.')
|
||||
dt_target = datetime.strptime(f"{date_clean} {target_time_str}", "%d.%m.%Y %H:%M")
|
||||
|
||||
# 1. Поиск существующих снапшотов за эту дату в SQLite
|
||||
with get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT DISTINCT snapshot_id, snapshot_time
|
||||
FROM scud_logs
|
||||
WHERE log_date = ? AND snapshot_time IS NOT NULL
|
||||
""", (date_clean,))
|
||||
rows = cursor.fetchall()
|
||||
|
||||
best_snapshot = None
|
||||
min_diff_seconds = float('inf')
|
||||
|
||||
for snap_id, snap_time_str in rows:
|
||||
try:
|
||||
# Формат в базе: YYYY-MM-DD HH:MM:SS или DD.MM.YYYY HH:MM:SS
|
||||
raw_time = str(snap_time_str).strip()
|
||||
if '.' in raw_time.split()[0]:
|
||||
dt_snap = datetime.strptime(raw_time, "%d.%m.%Y %H:%M:%S")
|
||||
else:
|
||||
dt_snap = datetime.strptime(raw_time, "%Y-%m-%d %H:%M:%S")
|
||||
|
||||
diff = abs((dt_snap - dt_target).total_seconds())
|
||||
if diff < min_diff_seconds:
|
||||
min_diff_seconds = diff
|
||||
best_snapshot = (snap_id, snap_time_str, dt_snap)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
# Если найден срез в пределах допуска (по умолчанию 20 минут)
|
||||
if best_snapshot and min_diff_seconds <= (tolerance_minutes * 60):
|
||||
snap_id, snap_time, dt_s = best_snapshot
|
||||
diff_mins = round(min_diff_seconds / 60)
|
||||
return snap_id, f"Использован готовый срез {snap_id} за {dt_s.strftime('%H:%M')} (разница {diff_mins} мин)."
|
||||
|
||||
# 2. Если срез не найден и разрешен On-Demand экспорт из MS SQL Орион
|
||||
if allow_ondemand_export:
|
||||
logger.info(f"Снапшот на {date_clean} {target_time_str} не найден в SQLite. Запуск прямого среза из MS SQL...")
|
||||
|
||||
run_export(
|
||||
input_date=date_clean,
|
||||
debug=False,
|
||||
save_xlsx=True
|
||||
)
|
||||
|
||||
with get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT snapshot_id
|
||||
FROM scud_logs
|
||||
WHERE log_date = ?
|
||||
ORDER BY id DESC LIMIT 1
|
||||
""", (date_clean,))
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
return row[0], f"Создан новый срез {row[0]} из MS SQL на {target_time_str}."
|
||||
|
||||
return None, f"Срез на {date_clean} {target_time_str} не найден."
|
||||
Reference in New Issue
Block a user