- Criadas APIs para Health (métricas de saúde) * Registrar peso, altura, % gordura, medidas * Histórico completo de medições * Estatísticas e resumo - Criadas APIs para Tasks (tarefas) * Criar, editar e deletar tarefas * Filtros por status e data * Estatísticas detalhadas * Prioridades (baixa, média, alta) - Frontend implementado: * Página Health.tsx - registro de métricas * Página Tasks.tsx - gerenciamento de tarefas * Página Progress.tsx - visualização de progresso * Dashboard integrado com estatísticas reais - Schemas e modelos atualizados - Todas as funcionalidades testadas e operacionais
291 lines
11 KiB
Python
291 lines
11 KiB
Python
from datetime import datetime, timedelta
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import func, and_
|
|
from typing import Optional, Dict
|
|
import random
|
|
|
|
from app.models.message import MotivationalMessage, UserMessageLog
|
|
from app.models.habit import Habit, HabitCompletion
|
|
from app.models.task import Task
|
|
from app.models.health import HealthMetric
|
|
from app.models.streak import Streak
|
|
|
|
|
|
class MessageService:
|
|
"""Serviço para gerar mensagens contextuais inteligentes"""
|
|
|
|
def __init__(self, db: Session):
|
|
self.db = db
|
|
|
|
def get_message_of_the_day(self, user_id: str) -> Dict:
|
|
"""Retorna a mensagem mais relevante para o usuário"""
|
|
|
|
# 1. Verificar streak (prioridade alta)
|
|
streak_message = self._check_streak_achievements(user_id)
|
|
if streak_message:
|
|
self._log_message(user_id, streak_message)
|
|
return streak_message
|
|
|
|
# 2. Verificar inatividade (recuperação)
|
|
inactive_message = self._check_inactivity(user_id)
|
|
if inactive_message:
|
|
self._log_message(user_id, inactive_message)
|
|
return inactive_message
|
|
|
|
# 3. Verificar lembretes importantes
|
|
reminder_message = self._check_reminders(user_id)
|
|
if reminder_message:
|
|
self._log_message(user_id, reminder_message)
|
|
return reminder_message
|
|
|
|
# 4. Verificar progresso e milestones
|
|
progress_message = self._check_progress(user_id)
|
|
if progress_message:
|
|
self._log_message(user_id, progress_message)
|
|
return progress_message
|
|
|
|
# 5. Mensagem baseada em hora do dia
|
|
time_message = self._get_time_based_message()
|
|
if time_message:
|
|
self._log_message(user_id, time_message)
|
|
return time_message
|
|
|
|
# 6. Mensagem motivacional padrão
|
|
default_message = self._get_daily_motivation()
|
|
self._log_message(user_id, default_message)
|
|
return default_message
|
|
|
|
def _check_streak_achievements(self, user_id: str) -> Optional[Dict]:
|
|
"""Verifica conquistas de streak"""
|
|
# Buscar maior streak do usuário
|
|
max_streak = self.db.query(func.max(Streak.current_streak)).filter(
|
|
Streak.user_id == user_id
|
|
).scalar() or 0
|
|
|
|
# Definir condições de streak
|
|
conditions = [
|
|
(100, 'streak_100'),
|
|
(60, 'streak_60'),
|
|
(30, 'streak_30'),
|
|
(14, 'streak_14'),
|
|
(7, 'streak_7')
|
|
]
|
|
|
|
for days, condition in conditions:
|
|
if max_streak >= days:
|
|
# Verificar se já mostrou essa mensagem hoje
|
|
already_shown = self.db.query(UserMessageLog).filter(
|
|
and_(
|
|
UserMessageLog.user_id == user_id,
|
|
UserMessageLog.message_type == 'streak_achievement',
|
|
func.date(UserMessageLog.shown_at) == datetime.now().date()
|
|
)
|
|
).first()
|
|
|
|
if not already_shown:
|
|
message = self.db.query(MotivationalMessage).filter(
|
|
and_(
|
|
MotivationalMessage.message_type == 'streak_achievement',
|
|
MotivationalMessage.trigger_condition == condition,
|
|
MotivationalMessage.is_active == True
|
|
)
|
|
).first()
|
|
|
|
if message:
|
|
return self._message_to_dict(message)
|
|
|
|
return None
|
|
|
|
def _check_inactivity(self, user_id: str) -> Optional[Dict]:
|
|
"""Verifica se o usuário está inativo"""
|
|
last_log = self.db.query(UserMessageLog).filter(
|
|
UserMessageLog.user_id == user_id
|
|
).order_by(UserMessageLog.shown_at.desc()).first()
|
|
|
|
if not last_log:
|
|
return None
|
|
|
|
days_inactive = (datetime.now() - last_log.shown_at).days
|
|
|
|
if days_inactive >= 14:
|
|
condition = 'inactive_14days'
|
|
elif days_inactive >= 7:
|
|
condition = 'inactive_7days'
|
|
else:
|
|
return None
|
|
|
|
message = self.db.query(MotivationalMessage).filter(
|
|
and_(
|
|
MotivationalMessage.message_type == 'recovery',
|
|
MotivationalMessage.trigger_condition == condition,
|
|
MotivationalMessage.is_active == True
|
|
)
|
|
).first()
|
|
|
|
return self._message_to_dict(message) if message else None
|
|
|
|
def _check_reminders(self, user_id: str) -> Optional[Dict]:
|
|
"""Verifica lembretes importantes"""
|
|
# Verificar última medição de saúde
|
|
last_metric = self.db.query(HealthMetric).filter(
|
|
HealthMetric.user_id == user_id
|
|
).order_by(HealthMetric.measurement_date.desc()).first()
|
|
|
|
if last_metric:
|
|
days_since_last = (datetime.now().date() - last_metric.measurement_date).days
|
|
|
|
if days_since_last >= 7:
|
|
condition = 'no_weight_7days'
|
|
elif days_since_last >= 3:
|
|
condition = 'no_weight_3days'
|
|
else:
|
|
condition = None
|
|
|
|
if condition:
|
|
message = self.db.query(MotivationalMessage).filter(
|
|
and_(
|
|
MotivationalMessage.message_type == 'reminder',
|
|
MotivationalMessage.trigger_condition == condition,
|
|
MotivationalMessage.is_active == True
|
|
)
|
|
).first()
|
|
|
|
if message:
|
|
return self._message_to_dict(message)
|
|
|
|
# Verificar hábitos pendentes
|
|
pending_habits = self.db.query(Habit).filter(
|
|
and_(
|
|
Habit.user_id == user_id,
|
|
Habit.is_active == True,
|
|
~Habit.id.in_(
|
|
self.db.query(HabitCompletion.habit_id).filter(
|
|
and_(
|
|
HabitCompletion.user_id == user_id,
|
|
HabitCompletion.completion_date == datetime.now().date()
|
|
)
|
|
)
|
|
)
|
|
)
|
|
).count()
|
|
|
|
if pending_habits > 0:
|
|
message = self.db.query(MotivationalMessage).filter(
|
|
and_(
|
|
MotivationalMessage.message_type == 'reminder',
|
|
MotivationalMessage.trigger_condition == 'habits_pending',
|
|
MotivationalMessage.is_active == True
|
|
)
|
|
).first()
|
|
|
|
if message:
|
|
msg_dict = self._message_to_dict(message)
|
|
msg_dict['message_text'] = f"🎯 Você tem {pending_habits} hábitos esperando por você hoje!"
|
|
return msg_dict
|
|
|
|
return None
|
|
|
|
def _check_progress(self, user_id: str) -> Optional[Dict]:
|
|
"""Verifica progresso e milestones"""
|
|
# Calcular consistência do mês
|
|
first_day_month = datetime.now().replace(day=1).date()
|
|
|
|
total_habits_month = self.db.query(func.count(Habit.id)).filter(
|
|
and_(
|
|
Habit.user_id == user_id,
|
|
Habit.start_date <= datetime.now().date()
|
|
)
|
|
).scalar()
|
|
|
|
completed_habits_month = self.db.query(func.count(HabitCompletion.id)).filter(
|
|
and_(
|
|
HabitCompletion.user_id == user_id,
|
|
HabitCompletion.completion_date >= first_day_month
|
|
)
|
|
).scalar()
|
|
|
|
if total_habits_month > 0:
|
|
consistency = (completed_habits_month / total_habits_month) * 100
|
|
|
|
if consistency >= 80:
|
|
message = self.db.query(MotivationalMessage).filter(
|
|
and_(
|
|
MotivationalMessage.message_type == 'progress_milestone',
|
|
MotivationalMessage.trigger_condition == 'consistency_80',
|
|
MotivationalMessage.is_active == True
|
|
)
|
|
).first()
|
|
|
|
if message:
|
|
msg_dict = self._message_to_dict(message)
|
|
msg_dict['message_text'] = f"💎 {int(consistency)}% de consistência esse mês! Top 10% dos usuários!"
|
|
return msg_dict
|
|
|
|
return None
|
|
|
|
def _get_time_based_message(self) -> Optional[Dict]:
|
|
"""Retorna mensagem baseada na hora do dia"""
|
|
hour = datetime.now().hour
|
|
|
|
if 5 <= hour < 12:
|
|
condition = 'morning'
|
|
elif 12 <= hour < 18:
|
|
condition = 'afternoon'
|
|
elif 18 <= hour < 22:
|
|
condition = 'evening'
|
|
else:
|
|
return None
|
|
|
|
message = self.db.query(MotivationalMessage).filter(
|
|
and_(
|
|
MotivationalMessage.message_type == 'time_based',
|
|
MotivationalMessage.trigger_condition == condition,
|
|
MotivationalMessage.is_active == True
|
|
)
|
|
).first()
|
|
|
|
return self._message_to_dict(message) if message else None
|
|
|
|
def _get_daily_motivation(self) -> Dict:
|
|
"""Retorna uma mensagem motivacional aleatória"""
|
|
messages = self.db.query(MotivationalMessage).filter(
|
|
and_(
|
|
MotivationalMessage.message_type == 'daily_motivation',
|
|
MotivationalMessage.is_active == True
|
|
)
|
|
).all()
|
|
|
|
if messages:
|
|
message = random.choice(messages)
|
|
return self._message_to_dict(message)
|
|
|
|
# Fallback
|
|
return {
|
|
'id': None,
|
|
'message_text': '💪 A transformação acontece um dia de cada vez. Esse dia é hoje!',
|
|
'icon': '💪',
|
|
'message_type': 'daily_motivation',
|
|
'priority': 1
|
|
}
|
|
|
|
def _message_to_dict(self, message: MotivationalMessage) -> Dict:
|
|
"""Converte mensagem para dicionário"""
|
|
return {
|
|
'id': str(message.id) if message.id else None,
|
|
'message_text': message.message_text,
|
|
'icon': message.icon,
|
|
'message_type': message.message_type,
|
|
'priority': message.priority
|
|
}
|
|
|
|
def _log_message(self, user_id: str, message: Dict):
|
|
"""Registra mensagem exibida no histórico"""
|
|
log = UserMessageLog(
|
|
user_id=user_id,
|
|
message_id=message.get('id'),
|
|
message_text=message['message_text'],
|
|
message_type=message['message_type']
|
|
)
|
|
self.db.add(log)
|
|
self.db.commit()
|