"""Самоочистка сервера от старых фото нарядов и заданий. Фото, которые нужно хранить дольше (у пункта чек-листа или задания отмечено «хранить более года»), с самого начала кладутся в отдельный каталог keep/ и очисткой не трогаются. Остальные удаляются, когда старше заданного числа дней (настройка photo_keep_days, 0 — не удалять). Проверка — раз в сутки из планировщика, вручную — из настроек сервера.""" import logging from datetime import datetime, timedelta from sqlalchemy import select from sqlalchemy.orm import Session from . import config, media, settings_store as ss from .models import Photo, TaskPhoto log = logging.getLogger("photo_cleanup") KEEP = "keep/" EVERY = timedelta(hours=24) def days(db: Session) -> int: try: return max(0, int(ss.get(db, "photo_keep_days") or 0)) except ValueError: return 0 def _old(db: Session, model, cut: datetime): return db.scalars(select(model).where(model.created_at < cut, model.path.not_like(KEEP + "%"))).all() def run(db: Session, dry: bool = False, now: datetime | None = None) -> dict: """Удалить старые фото (dry — только посчитать). Возвращает число фото и освобождённые байты.""" n = days(db) if n <= 0: return {"enabled": False, "days": 0, "photos": 0, "bytes": 0} cut = (now or config.now()) - timedelta(days=n) root = ss.photo_root(db) order_ph, task_ph = _old(db, Photo, cut), _old(db, TaskPhoto, cut) out = {"enabled": True, "days": n, "before": config.iso(cut), "photos": len(order_ph) + len(task_ph), "bytes": sum(p.size or 0 for p in order_ph + task_ph)} if dry: return out for p in order_ph: (root / p.path).unlink(missing_ok=True) db.delete(p) for p in task_ph: media.remove(db, "tasks", p.path) db.delete(p) ss.set_(db, "photo_cleanup_at", config.now().isoformat()) db.commit() if out["photos"]: log.warning("Удалено старых фото: %s (%.1f МБ), старше %s дней", out["photos"], out["bytes"] / 1048576, n) return out def due(db: Session) -> bool: if days(db) <= 0: return False at = ss.get(db, "photo_cleanup_at") try: return not at or config.now() - datetime.fromisoformat(at) >= EVERY except ValueError: return True