feat: Implementar funcionalidades de Tarefas e Saúde
- 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
This commit is contained in:
155
backend/app/api/habits.py
Normal file
155
backend/app/api/habits.py
Normal file
@@ -0,0 +1,155 @@
|
||||
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"}
|
||||
Reference in New Issue
Block a user