from datetime import datetime, timedelta, timezone from sqlalchemy import select from sqlalchemy.orm import Session from app.config import get_settings from app.db.base import SessionLocal from app.db.models import ChatSession, FitnessReminder, Message from app.fitness.service import FitnessService KIND_LABELS = { "water": "Вода", "meal": "Еда", "workout": "Тренировка", "weigh_in": "Взвешивание", } def _post_fitness_notice(content: str) -> None: db = SessionLocal() try: session = db.scalar( select(ChatSession).order_by(ChatSession.updated_at.desc()).limit(1) ) if not session: session = ChatSession(title="Фитнес") db.add(session) db.commit() db.refresh(session) db.add(Message(session_id=session.id, role="notice", content=content)) db.commit() finally: db.close() def _build_notice(kind: str, summary: dict) -> str: label = KIND_LABELS.get(kind, kind) totals = summary.get("totals") or {} targets = summary.get("targets") or {} water_l = totals.get("water_ml", 0) / 1000 water_target = targets.get("water_ml", 2500) / 1000 cals = totals.get("calories", 0) cal_target = targets.get("calories", 2000) if kind == "water": return ( f"💪 **{label}** · выпито {water_l:.1f}/{water_target:.1f} л сегодня. " "Пора выпить стакан воды." ) if kind == "meal": return ( f"💪 **{label}** · {cals:.0f}/{cal_target:.0f} ккал за день. " "Не забудь залогировать приём пищи." ) if kind == "workout": workouts = summary.get("workouts") or [] if workouts: return f"💪 **{label}** · сегодня уже была тренировка. Отдыхай или лёгкая активность." return "💪 **Тренировка** · запланирована на сегодня. Время двигаться!" if kind == "weigh_in": return "💪 **Взвешивание** · пора записать вес (log_weight)." return f"💪 **{label}** · напоминание" def check_reminders(db: Session) -> list[str]: if not get_settings().fitness_reminders_enabled: return [] now = datetime.now(timezone.utc) service = FitnessService(db) summary = service.get_daily_summary() fired: list[str] = [] reminders = db.scalars( select(FitnessReminder).where(FitnessReminder.enabled.is_(True)) ).all() for rem in reminders: should_fire = False if rem.interval_hours: if rem.last_fired_at is None: should_fire = now.hour >= rem.hour else: delta = now - rem.last_fired_at.replace(tzinfo=timezone.utc) should_fire = delta >= timedelta(hours=rem.interval_hours) else: if rem.kind == "weigh_in": if rem.last_fired_at: delta = now - rem.last_fired_at.replace(tzinfo=timezone.utc) should_fire = delta >= timedelta(days=7) else: should_fire = now.hour == rem.hour and now.minute >= rem.minute else: if rem.last_fired_at: last = rem.last_fired_at.replace(tzinfo=timezone.utc) already_today = last.date() == now.date() if already_today: continue should_fire = now.hour == rem.hour and now.minute >= rem.minute if not should_fire: continue notice = _build_notice(rem.kind, summary) rem.last_fired_at = now fired.append(notice) if fired: db.commit() for notice in fired: _post_fitness_notice(notice) return fired