112 lines
3.9 KiB
Python
112 lines
3.9 KiB
Python
from datetime import datetime, timedelta, timezone
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.chat.notice_inbox import post_notice_to_latest_chat
|
|
from app.config import get_settings
|
|
from app.db.models import FitnessReminder, User
|
|
from app.fitness.service import FitnessService
|
|
|
|
KIND_LABELS = {
|
|
"water": "Вода",
|
|
"meal": "Еда",
|
|
"workout": "Тренировка",
|
|
"weigh_in": "Взвешивание",
|
|
}
|
|
|
|
|
|
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_user_reminders(db: Session, user_id: int) -> list[str]:
|
|
now = datetime.now(timezone.utc)
|
|
service = FitnessService(db, user_id)
|
|
summary = service.get_daily_summary()
|
|
fired: list[str] = []
|
|
|
|
reminders = db.scalars(
|
|
select(FitnessReminder).where(
|
|
FitnessReminder.user_id == user_id,
|
|
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:
|
|
for notice in fired:
|
|
post_notice_to_latest_chat(notice, user_id)
|
|
|
|
return fired
|
|
|
|
|
|
def check_reminders(db: Session) -> list[str]:
|
|
if not get_settings().fitness_reminders_enabled:
|
|
return []
|
|
|
|
users = db.scalars(select(User).where(User.is_active.is_(True))).all()
|
|
all_fired: list[str] = []
|
|
for user in users:
|
|
all_fired.extend(_check_user_reminders(db, user.id))
|
|
|
|
if all_fired:
|
|
db.commit()
|
|
|
|
return all_fired
|