feat(web): стабилизация UI, Gemini-скроллинг, роутеры контекста/снапшотов и актуализация роадмапа
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
"""
|
||||
===============================================================================
|
||||
FILE: services/snapshots/retention.py
|
||||
ROLE: Политика ротации и очистки промежуточных почасовых срезов в SQLite.
|
||||
===============================================================================
|
||||
"""
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Tuple
|
||||
from core.connection import get_connection
|
||||
|
||||
logger = logging.getLogger("SNAPSHOT_RETENTION")
|
||||
|
||||
|
||||
def cleanup_old_intermediate_snapshots(days_to_keep_all: int = 2) -> int:
|
||||
"""
|
||||
Очищает промежуточные дневные срезы старше days_to_keep_all дней.
|
||||
|
||||
Правило сохранения для архивных дней:
|
||||
- Сохраняется срез Y (23:59:59).
|
||||
- Сохраняется полуденный срез (ближайший к 13:00).
|
||||
- Все остальные промежуточные снапшоты удаляются из scud_logs.
|
||||
|
||||
Возвращает количество удаленных записей.
|
||||
"""
|
||||
cutoff_date = datetime.now() - timedelta(days=days_to_keep_all)
|
||||
cutoff_str = cutoff_date.strftime("%Y-%m-%d")
|
||||
|
||||
deleted_rows_count = 0
|
||||
|
||||
with get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 1. Получаем список всех уникальных дат старше cutoff_date
|
||||
cursor.execute("""
|
||||
SELECT DISTINCT log_date
|
||||
FROM scud_logs
|
||||
WHERE snapshot_time IS NOT NULL
|
||||
""")
|
||||
all_dates = [row[0] for row in cursor.fetchall()]
|
||||
|
||||
for raw_date in all_dates:
|
||||
try:
|
||||
# Преобразуем формат ДД.ММ.ГГГГ в объект даты
|
||||
d_clean = raw_date.replace('_', '.')
|
||||
dt_obj = datetime.strptime(d_clean, "%d.%m.%Y")
|
||||
if dt_obj >= cutoff_date:
|
||||
continue # Свежие дни не трогаем — там хранятся все срезы
|
||||
|
||||
# 2. Для архивной даты ищем все снапшоты
|
||||
cursor.execute("""
|
||||
SELECT DISTINCT snapshot_id, snapshot_time
|
||||
FROM scud_logs
|
||||
WHERE log_date = ?
|
||||
""", (raw_date,))
|
||||
snapshots = cursor.fetchall()
|
||||
|
||||
if len(snapshots) <= 2:
|
||||
continue # Если и так 1-2 среза, ничего чистить не нужно
|
||||
|
||||
keep_ids = set()
|
||||
|
||||
# Ищем финальный срез Y / 23:59:59 / 22:00:00
|
||||
for s_id, s_time in snapshots:
|
||||
s_id_str = str(s_id)
|
||||
s_time_str = str(s_time)
|
||||
if s_id_str.startswith('Y') or '23:59:59' in s_time_str or '22:00:00' in s_time_str:
|
||||
keep_ids.add(s_id)
|
||||
break
|
||||
|
||||
# Ищем полуденный срез (ближайший к 13:00)
|
||||
noon_target = datetime.strptime(f"{d_clean} 13:00:00", "%d.%m.%Y %H:%M:%S")
|
||||
best_noon_id = None
|
||||
min_noon_diff = float('inf')
|
||||
|
||||
for s_id, s_time in snapshots:
|
||||
if s_id in keep_ids:
|
||||
continue
|
||||
try:
|
||||
raw_t = str(s_time).strip()
|
||||
if '.' in raw_t.split()[0]:
|
||||
dt_s = datetime.strptime(raw_t, "%d.%m.%Y %H:%M:%S")
|
||||
else:
|
||||
dt_s = datetime.strptime(raw_t, "%Y-%m-%d %H:%M:%S")
|
||||
|
||||
diff = abs((dt_s - noon_target).total_seconds())
|
||||
if diff < min_noon_diff:
|
||||
min_noon_diff = diff
|
||||
best_noon_id = s_id
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if best_noon_id:
|
||||
keep_ids.add(best_noon_id)
|
||||
|
||||
# 3. Все снапшоты, не попавшие в keep_ids, удаляем
|
||||
to_delete_ids = [s_id for s_id, _ in snapshots if s_id not in keep_ids]
|
||||
|
||||
for del_id in to_delete_ids:
|
||||
cursor.execute("DELETE FROM scud_logs WHERE snapshot_id = ?", (del_id,))
|
||||
deleted_rows_count += cursor.rowcount
|
||||
logger.info(f"Удален промежуточный архивный срез {del_id} за {raw_date}")
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Ошибка при обработке ротации за дату {raw_date}: {e}")
|
||||
|
||||
conn.commit()
|
||||
|
||||
if deleted_rows_count > 0:
|
||||
logger.info(f"Ротация завершена: удалено {deleted_rows_count} строк промежуточных срезов.")
|
||||
return deleted_rows_count
|
||||
Reference in New Issue
Block a user