- 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
156 lines
4.6 KiB
Python
156 lines
4.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
from sqlalchemy.orm import Session
|
|
from typing import List
|
|
from datetime import date, datetime
|
|
|
|
from app.core.database import get_db
|
|
from app.core.security import decode_access_token
|
|
from app.models.user import User
|
|
from app.models.habit import Habit, HabitCompletion
|
|
from app.schemas.habit import HabitCreate, HabitResponse, HabitCompletionCreate
|
|
|
|
router = APIRouter(prefix="/habits", tags=["habits"])
|
|
security = HTTPBearer()
|
|
|
|
def get_current_user_id(credentials: HTTPAuthorizationCredentials = Depends(security)) -> str:
|
|
"""Extrai user_id do token JWT"""
|
|
token = credentials.credentials
|
|
payload = decode_access_token(token)
|
|
if not payload:
|
|
raise HTTPException(status_code=401, detail="Token inválido")
|
|
return payload.get("sub")
|
|
|
|
@router.post("/", response_model=HabitResponse, status_code=status.HTTP_201_CREATED)
|
|
async def create_habit(
|
|
habit_data: HabitCreate,
|
|
user_id: str = Depends(get_current_user_id),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Criar novo hábito"""
|
|
new_habit = Habit(
|
|
user_id=user_id,
|
|
name=habit_data.name,
|
|
description=habit_data.description,
|
|
frequency_type=habit_data.frequency_type,
|
|
target_count=habit_data.target_count,
|
|
reminder_time=habit_data.reminder_time,
|
|
start_date=habit_data.start_date or date.today()
|
|
)
|
|
|
|
db.add(new_habit)
|
|
db.commit()
|
|
db.refresh(new_habit)
|
|
|
|
return new_habit
|
|
|
|
@router.get("/", response_model=List[HabitResponse])
|
|
async def list_habits(
|
|
user_id: str = Depends(get_current_user_id),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Listar todos os hábitos do usuário"""
|
|
habits = db.query(Habit).filter(
|
|
Habit.user_id == user_id,
|
|
Habit.is_active == True
|
|
).all()
|
|
|
|
return habits
|
|
|
|
@router.post("/{habit_id}/complete", status_code=status.HTTP_201_CREATED)
|
|
async def complete_habit(
|
|
habit_id: str,
|
|
user_id: str = Depends(get_current_user_id),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Marcar hábito como completo para hoje"""
|
|
# Verificar se já completou hoje
|
|
today = date.today()
|
|
existing = db.query(HabitCompletion).filter(
|
|
HabitCompletion.habit_id == habit_id,
|
|
HabitCompletion.user_id == user_id,
|
|
HabitCompletion.completion_date == today
|
|
).first()
|
|
|
|
if existing:
|
|
raise HTTPException(status_code=400, detail="Hábito já completado hoje")
|
|
|
|
# Criar completion
|
|
completion = HabitCompletion(
|
|
habit_id=habit_id,
|
|
user_id=user_id,
|
|
completion_date=today,
|
|
completion_time=datetime.now().time()
|
|
)
|
|
|
|
db.add(completion)
|
|
db.commit()
|
|
|
|
return {"message": "Hábito completado!", "date": today}
|
|
|
|
@router.delete("/{habit_id}/complete")
|
|
async def uncomplete_habit(
|
|
habit_id: str,
|
|
user_id: str = Depends(get_current_user_id),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Desmarcar hábito de hoje"""
|
|
today = date.today()
|
|
completion = db.query(HabitCompletion).filter(
|
|
HabitCompletion.habit_id == habit_id,
|
|
HabitCompletion.user_id == user_id,
|
|
HabitCompletion.completion_date == today
|
|
).first()
|
|
|
|
if not completion:
|
|
raise HTTPException(status_code=404, detail="Completion não encontrado")
|
|
|
|
db.delete(completion)
|
|
db.commit()
|
|
|
|
return {"message": "Completion removido"}
|
|
|
|
@router.get("/stats")
|
|
async def get_habit_stats(
|
|
user_id: str = Depends(get_current_user_id),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Estatísticas de hábitos"""
|
|
from sqlalchemy import func
|
|
|
|
total_habits = db.query(func.count(Habit.id)).filter(
|
|
Habit.user_id == user_id,
|
|
Habit.is_active == True
|
|
).scalar()
|
|
|
|
completed_today = db.query(func.count(HabitCompletion.id)).filter(
|
|
HabitCompletion.user_id == user_id,
|
|
HabitCompletion.completion_date == date.today()
|
|
).scalar()
|
|
|
|
return {
|
|
"total_habits": total_habits,
|
|
"completed_today": completed_today,
|
|
"pending_today": total_habits - completed_today
|
|
}
|
|
|
|
@router.delete("/{habit_id}")
|
|
async def delete_habit(
|
|
habit_id: str,
|
|
user_id: str = Depends(get_current_user_id),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Deletar hábito"""
|
|
habit = db.query(Habit).filter(
|
|
Habit.id == habit_id,
|
|
Habit.user_id == user_id
|
|
).first()
|
|
|
|
if not habit:
|
|
raise HTTPException(status_code=404, detail="Hábito não encontrado")
|
|
|
|
db.delete(habit)
|
|
db.commit()
|
|
|
|
return {"message": "Hábito deletado"}
|