- Mover logica de routes.py para RankingMapper na camada de aplicacao - Consolidar funcoes mesclar_periodos e anos_completos_periodos em periodo.py - Extrair RankingCache para modulo separado em infrastructure/cache - Substituir todos os print() por logging adequado - Corrigir exception handlers genericos para tipos especificos - Remover classe Atuacao e atributo atuacoes_raw nao utilizados - Documentar status dos scripts utilitarios
173 lines
6.3 KiB
Python
173 lines
6.3 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Query, BackgroundTasks
|
|
from typing import Optional, List
|
|
|
|
from ...application.use_cases.obter_ranking import ObterRankingUseCase
|
|
from ...application.use_cases.obter_consultor import ObterConsultorUseCase
|
|
from ...application.mappers import RankingMapper
|
|
from ...infrastructure.repositories.consultor_repository_impl import ConsultorRepositoryImpl
|
|
from ..schemas.consultor_schema import (
|
|
RankingResponseSchema,
|
|
RankingDetalhadoResponseSchema,
|
|
ConsultorDetalhadoSchema,
|
|
ConsultorResumoSchema,
|
|
)
|
|
from ..schemas.ranking_schema import (
|
|
RankingPaginadoResponseSchema,
|
|
ConsultorRankingResumoSchema,
|
|
EstatisticasRankingSchema,
|
|
JobStatusSchema,
|
|
ProcessarRankingRequestSchema,
|
|
ProcessarRankingResponseSchema,
|
|
ConsultaNomeSchema,
|
|
)
|
|
from .dependencies import get_repository, get_ranking_repository, get_processar_job
|
|
from ...application.jobs.job_status import job_status
|
|
|
|
router = APIRouter(prefix="/api/v1", tags=["ranking"])
|
|
|
|
|
|
@router.get("/ranking", response_model=RankingResponseSchema)
|
|
async def obter_ranking(
|
|
limite: int = Query(default=100, ge=1, le=1000, description="Limite de consultores"),
|
|
offset: int = Query(default=0, ge=0, description="Offset para paginação"),
|
|
componente: Optional[str] = Query(
|
|
default=None, description="Filtrar por bloco (a, c, d)"
|
|
),
|
|
repository: ConsultorRepositoryImpl = Depends(get_repository),
|
|
):
|
|
use_case = ObterRankingUseCase(repository=repository)
|
|
consultores_dto = await use_case.executar(limite=limite, componente=componente)
|
|
|
|
total = await repository.contar_total()
|
|
|
|
consultores_schema = [
|
|
ConsultorResumoSchema(**vars(dto)) for dto in consultores_dto
|
|
]
|
|
|
|
return RankingResponseSchema(
|
|
total=total, limite=limite, offset=offset, consultores=consultores_schema
|
|
)
|
|
|
|
|
|
@router.get("/ranking/detalhado", response_model=RankingDetalhadoResponseSchema)
|
|
async def obter_ranking_detalhado(
|
|
limite: int = Query(default=100, ge=1, le=1000, description="Limite de consultores"),
|
|
componente: Optional[str] = Query(
|
|
default=None, description="Filtrar por bloco (a, c, d)"
|
|
),
|
|
repository: ConsultorRepositoryImpl = Depends(get_repository),
|
|
):
|
|
use_case = ObterRankingUseCase(repository=repository)
|
|
consultores_dto = await use_case.executar_detalhado(limite=limite, componente=componente)
|
|
|
|
total = await repository.contar_total()
|
|
|
|
consultores_schema = [
|
|
ConsultorDetalhadoSchema(**dto.to_dict()) for dto in consultores_dto
|
|
]
|
|
|
|
return RankingDetalhadoResponseSchema(total=total, limite=limite, consultores=consultores_schema)
|
|
|
|
|
|
@router.get("/consultor/{id_pessoa}", response_model=ConsultorDetalhadoSchema)
|
|
async def obter_consultor(
|
|
id_pessoa: int,
|
|
repository: ConsultorRepositoryImpl = Depends(get_repository),
|
|
):
|
|
use_case = ObterConsultorUseCase(repository=repository)
|
|
consultor = await use_case.executar(id_pessoa=id_pessoa)
|
|
|
|
if not consultor:
|
|
raise HTTPException(status_code=404, detail=f"Consultor {id_pessoa} não encontrado")
|
|
|
|
return consultor
|
|
|
|
|
|
@router.get("/health")
|
|
async def health_check():
|
|
return {"status": "ok", "message": "API Ranking CAPES funcionando"}
|
|
|
|
|
|
@router.get("/ranking/paginado", response_model=RankingPaginadoResponseSchema)
|
|
async def ranking_paginado(
|
|
page: int = Query(default=1, ge=1, description="Número da página"),
|
|
size: int = Query(default=50, ge=1, le=1000, description="Tamanho da página (máx 1000)"),
|
|
ativo: Optional[bool] = Query(default=None, description="Filtrar por status ativo"),
|
|
ranking_repo = Depends(get_ranking_repository),
|
|
):
|
|
total = ranking_repo.contar_total(filtro_ativo=ativo)
|
|
consultores = ranking_repo.buscar_paginado(page=page, size=size, filtro_ativo=ativo)
|
|
|
|
total_pages = (total + size - 1) // size
|
|
|
|
consultores_schema = [RankingMapper.consultor_ranking_to_schema(c) for c in consultores]
|
|
|
|
return RankingPaginadoResponseSchema(
|
|
total=total,
|
|
page=page,
|
|
size=size,
|
|
total_pages=total_pages,
|
|
consultores=consultores_schema
|
|
)
|
|
|
|
|
|
@router.get("/ranking/busca", response_model=List[ConsultaNomeSchema])
|
|
async def buscar_por_nome(
|
|
nome: str = Query(..., min_length=3, description="Nome (ou parte) para buscar"),
|
|
limit: int = Query(default=5, ge=1, le=20, description="Limite de resultados"),
|
|
ranking_repo = Depends(get_ranking_repository),
|
|
):
|
|
resultados = ranking_repo.buscar_por_nome(nome=nome, limit=limit)
|
|
return [
|
|
ConsultaNomeSchema(
|
|
id_pessoa=r["ID_PESSOA"],
|
|
nome=r["NOME"],
|
|
posicao=r["POSICAO"],
|
|
pontuacao_total=float(r["PONTUACAO_TOTAL"]),
|
|
)
|
|
for r in resultados
|
|
]
|
|
|
|
|
|
@router.get("/ranking/estatisticas", response_model=EstatisticasRankingSchema)
|
|
async def ranking_estatisticas(
|
|
ranking_repo = Depends(get_ranking_repository),
|
|
):
|
|
estatisticas = ranking_repo.obter_estatisticas()
|
|
distribuicao = ranking_repo.obter_distribuicao()
|
|
|
|
return EstatisticasRankingSchema(
|
|
total_consultores=estatisticas.get("total_consultores", 0),
|
|
total_ativos=estatisticas.get("total_ativos", 0),
|
|
total_inativos=estatisticas.get("total_inativos", 0),
|
|
ultima_atualizacao=estatisticas.get("ultima_atualizacao"),
|
|
pontuacao_media=estatisticas.get("pontuacao_media", 0),
|
|
pontuacao_maxima=estatisticas.get("pontuacao_maxima", 0),
|
|
pontuacao_minima=estatisticas.get("pontuacao_minima", 0),
|
|
media_blocos=estatisticas.get("media_componentes", {}),
|
|
distribuicao=distribuicao
|
|
)
|
|
|
|
|
|
@router.get("/ranking/status", response_model=JobStatusSchema)
|
|
async def status_processamento():
|
|
return JobStatusSchema(**job_status.to_dict())
|
|
|
|
|
|
@router.post("/ranking/processar", response_model=ProcessarRankingResponseSchema)
|
|
async def processar_ranking(
|
|
background_tasks: BackgroundTasks,
|
|
request: ProcessarRankingRequestSchema = ProcessarRankingRequestSchema(),
|
|
job = Depends(get_processar_job),
|
|
):
|
|
if job_status.is_running:
|
|
raise HTTPException(status_code=409, detail="Job já está em execução")
|
|
|
|
background_tasks.add_task(job.executar, limpar_antes=request.limpar_antes)
|
|
|
|
return ProcessarRankingResponseSchema(
|
|
sucesso=True,
|
|
mensagem="Processamento do ranking iniciado em background",
|
|
job_id="ranking_job"
|
|
)
|