added fitness

This commit is contained in:
2026-06-10 09:12:50 +03:00
parent 0b39692300
commit d0bdd1e95c
25 changed files with 2082 additions and 7 deletions
+114
View File
@@ -0,0 +1,114 @@
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