from datetime import datetime from uuid import uuid4 from fastapi import APIRouter, Body, HTTPException, Query, status from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import selectinload from pydantic import UUID4 from fastapi_pagination import Page, paginate from workout_api.atleta.schemas import AtletaIn, AtletaOut, AtletaUpdate, AtletaCustomOut from workout_api.atleta.models import AtletaModel from workout_api.categorias.models import CategoriaModel from workout_api.centro_treinamento.models import CentroTreinamentoModel from workout_api.contrib.dependencies import DatabaseDependency from sqlalchemy.future import select router = APIRouter() @router.post( '/', summary='Criar um novo atleta', status_code=status.HTTP_201_CREATED, response_model=AtletaOut ) async def post( db_session: DatabaseDependency, atleta_in: AtletaIn = Body(...) ): categoria_nome = atleta_in.categoria.nome centro_treinamento_nome = atleta_in.centro_treinamento.nome categoria = (await db_session.execute( select(CategoriaModel).filter_by(nome=categoria_nome)) ).scalars().first() if not categoria: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f'A categoria {categoria_nome} não foi encontrada.' ) centro_treinamento = (await db_session.execute( select(CentroTreinamentoModel).filter_by(nome=centro_treinamento_nome)) ).scalars().first() if not centro_treinamento: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f'O centro de treinamento {centro_treinamento_nome} não foi encontrado.' ) try: atleta_model = AtletaModel( **atleta_in.model_dump(exclude={'categoria', 'centro_treinamento'}), categoria_id=categoria.pk_id, centro_treinamento_id=centro_treinamento.pk_id, created_at=datetime.utcnow() ) db_session.add(atleta_model) await db_session.commit() await db_session.refresh(atleta_model) except IntegrityError: await db_session.rollback() raise HTTPException( status_code=status.HTTP_303_SEE_OTHER, detail=f"Já existe um atleta cadastrado com o cpf: {atleta_in.cpf}" ) except Exception as e: await db_session.rollback() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f'Ocorreu um erro ao inserir os dados no banco: {e}' ) return AtletaOut.model_validate(atleta_model) @router.get( '/', summary='Consultar Atletas com filtros e paginação', status_code=status.HTTP_200_OK, response_model=Page[AtletaCustomOut], ) async def query( db_session: DatabaseDependency, nome: str | None = Query(None, description="Filtrar atleta por nome"), cpf: str | None = Query(None, description="Filtrar atleta por CPF") ) -> Page[AtletaCustomOut]: stmt = select(AtletaModel).options( selectinload(AtletaModel.categoria), selectinload(AtletaModel.centro_treinamento) ) if nome: stmt = stmt.where(AtletaModel.nome.ilike(f"%{nome}%")) if cpf: stmt = stmt.where(AtletaModel.cpf == cpf) atletas: list[AtletaModel] = (await db_session.execute(stmt)).scalars().all() atletas_custom = [ AtletaCustomOut( nome=atleta.nome, categoria=atleta.categoria.nome, centro_treinamento=atleta.centro_treinamento.nome ) for atleta in atletas ] return paginate(atletas_custom) @router.get( '/{id}', summary='Consulta um Atleta pelo id', status_code=status.HTTP_200_OK, response_model=AtletaOut, ) async def get(id: UUID4, db_session: DatabaseDependency) -> AtletaOut: atleta_model: AtletaModel | None = ( await db_session.execute(select(AtletaModel).filter_by(id=id)) ).scalars().first() if not atleta_model: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f'Atleta não encontrado no id: {id}' ) return AtletaOut.model_validate(atleta_model) @router.patch( '/{id}', summary='Editar um Atleta pelo id', status_code=status.HTTP_200_OK, response_model=AtletaOut, ) async def patch(id: UUID4, db_session: DatabaseDependency, atleta_up: AtletaUpdate = Body(...)) -> AtletaOut: atleta_model: AtletaModel | None = ( await db_session.execute(select(AtletaModel).filter_by(id=id)) ).scalars().first() if not atleta_model: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f'Atleta não encontrado no id: {id}' ) atleta_update = atleta_up.model_dump(exclude_unset=True) for key, value in atleta_update.items(): setattr(atleta_model, key, value) await db_session.commit() await db_session.refresh(atleta_model) return AtletaOut.model_validate(atleta_model) @router.delete( '/{id}', summary='Deletar um Atleta pelo id', status_code=status.HTTP_204_NO_CONTENT ) async def delete(id: UUID4, db_session: DatabaseDependency) -> None: atleta_model: AtletaModel | None = ( await db_session.execute(select(AtletaModel).filter_by(id=id)) ).scalars().first() if not atleta_model: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f'Atleta não encontrado no id: {id}' ) await db_session.delete(atleta_model) await db_session.commit()