- Adicionar idiomas e formacoes ao _source das queries ES (client.py) - Corrigir type mismatch int/str no endpoint paginado (routes.py) - Adicionar campo evento nas inscricoes para nome do premio - Implementar extracao de idiomas do ES no repository - Ajustar frontend para exibir selo multilingue corretamente
323 lines
13 KiB
Python
323 lines
13 KiB
Python
import logging
|
|
from typing import Dict, Any, List, Optional
|
|
|
|
from ...infrastructure.elasticsearch.client import ElasticsearchClient
|
|
from ...infrastructure.ranking_store import RankingEntry, RankingStore
|
|
from ...infrastructure.oracle.ranking_repository import RankingOracleRepository
|
|
|
|
logger = logging.getLogger(__name__)
|
|
from ...infrastructure.repositories.consultor_repository_impl import ConsultorRepositoryImpl
|
|
from .job_status import job_status
|
|
|
|
|
|
class ProcessarRankingJob:
|
|
def __init__(
|
|
self,
|
|
es_client: ElasticsearchClient,
|
|
ranking_store: RankingStore,
|
|
ranking_oracle_repo: Optional[RankingOracleRepository] = None,
|
|
):
|
|
self.es_client = es_client
|
|
self.ranking_store = ranking_store
|
|
self.ranking_oracle_repo = ranking_oracle_repo
|
|
self.consultor_repo = ConsultorRepositoryImpl(es_client, oracle_client=None)
|
|
self._consultores: List[dict] = []
|
|
|
|
async def executar(self, limpar_antes: bool = True) -> Dict[str, Any]:
|
|
if job_status.is_running:
|
|
raise RuntimeError("Job já está em execução")
|
|
|
|
try:
|
|
total = await self.es_client.contar_com_atuacoes()
|
|
job_status.iniciar(total_esperado=total)
|
|
|
|
self._consultores = []
|
|
job_status.mensagem = "Iniciando processamento do ranking via Scroll API (Elasticsearch)..."
|
|
|
|
resultado = await self.es_client.buscar_todos_consultores(
|
|
callback=self._processar_batch,
|
|
batch_size=10000
|
|
)
|
|
|
|
job_status.mensagem = "Ordenando e gerando posições..."
|
|
entries = self._gerar_entries_ordenadas(self._consultores)
|
|
await self.ranking_store.set_entries(entries)
|
|
|
|
if self.ranking_oracle_repo:
|
|
job_status.mensagem = "Persistindo no Oracle..."
|
|
await self._persistir_oracle(self._consultores, limpar_antes)
|
|
|
|
estatisticas = self._obter_estatisticas(entries)
|
|
|
|
job_status.finalizar(sucesso=True)
|
|
|
|
resultado_final = {
|
|
"sucesso": True,
|
|
"total_processados": resultado.get("processados", len(entries)),
|
|
"total_batches": resultado.get("batches", 0),
|
|
"tempo_decorrido": job_status.tempo_decorrido,
|
|
"estatisticas": estatisticas
|
|
}
|
|
|
|
self._consultores = []
|
|
|
|
return resultado_final
|
|
|
|
except Exception as e:
|
|
self._consultores = []
|
|
job_status.finalizar(sucesso=False, erro=str(e))
|
|
logger.error(f"Erro ao processar ranking: {e}", exc_info=True)
|
|
raise RuntimeError(f"Erro ao processar ranking: {e}") from e
|
|
|
|
async def _processar_batch(self, docs: list, progress: dict) -> None:
|
|
for doc in docs:
|
|
try:
|
|
consultor = await self.consultor_repo._construir_consultor(doc)
|
|
self._consultores.append(self._gerar_json_detalhes(consultor))
|
|
except Exception as e:
|
|
import traceback
|
|
logger.warning(f"Erro ao processar consultor {doc.get('id')}: {e}")
|
|
logger.debug(f"Traceback: {traceback.format_exc()}")
|
|
continue
|
|
|
|
job_status.atualizar_progresso(
|
|
processados=progress["processados"],
|
|
batch_atual=progress["batch_atual"],
|
|
mensagem=f"Processando batch {progress['batch_atual']} ({progress['percentual']}%)"
|
|
)
|
|
|
|
def _gerar_json_detalhes(self, consultor) -> dict:
|
|
pontuacao = consultor.pontuacao.to_dict() if consultor.pontuacao else None
|
|
|
|
return {
|
|
"id_pessoa": consultor.id_pessoa,
|
|
"nome": consultor.nome,
|
|
"posicao": None,
|
|
"pontuacao_total": consultor.pontuacao_total,
|
|
"bloco_a": consultor.pontuacao_bloco_a,
|
|
"bloco_b": consultor.pontuacao_bloco_b,
|
|
"bloco_c": consultor.pontuacao_bloco_c,
|
|
"bloco_d": consultor.pontuacao_bloco_d,
|
|
"bloco_e": consultor.pontuacao_bloco_e,
|
|
"ativo": consultor.ativo,
|
|
"anos_atuacao": consultor.anos_atuacao,
|
|
"coordenador_ppg": consultor.coordenador_ppg,
|
|
"coordenacoes_capes": [
|
|
{
|
|
"codigo": c.codigo,
|
|
"tipo": c.tipo,
|
|
"area_avaliacao": c.area_avaliacao,
|
|
"inicio": c.periodo.inicio.isoformat() if c.periodo.inicio else None,
|
|
"fim": c.periodo.fim.isoformat() if c.periodo.fim else None,
|
|
"ativo": c.periodo.ativo,
|
|
"presidente": c.presidente,
|
|
}
|
|
for c in consultor.coordenacoes_capes
|
|
],
|
|
"consultoria": {
|
|
"codigo": consultor.consultoria.codigo,
|
|
"situacao": consultor.consultoria.situacao,
|
|
"inicio": consultor.consultoria.periodo.inicio.isoformat() if consultor.consultoria.periodo.inicio else None,
|
|
"fim": consultor.consultoria.periodo.fim.isoformat() if consultor.consultoria.periodo.fim else None,
|
|
"areas": consultor.consultoria.areas,
|
|
"anos_consecutivos": consultor.consultoria.anos_consecutivos,
|
|
"retornos": consultor.consultoria.retornos,
|
|
"vinculos": [
|
|
{
|
|
"inicio": v.periodo.inicio.isoformat() if v.periodo.inicio else None,
|
|
"fim": v.periodo.fim.isoformat() if v.periodo.fim else None,
|
|
"ativo": v.periodo.ativo,
|
|
"situacao": v.situacao,
|
|
"ies": {
|
|
"id": v.ies.id,
|
|
"nome": v.ies.nome,
|
|
"sigla": v.ies.sigla,
|
|
} if v.ies else None,
|
|
}
|
|
for v in consultor.consultoria.vinculos
|
|
],
|
|
} if consultor.consultoria else None,
|
|
"inscricoes": [
|
|
{
|
|
"codigo": i.codigo,
|
|
"tipo": i.tipo,
|
|
"premio": i.premio,
|
|
"ano": i.ano,
|
|
"situacao": i.situacao,
|
|
"evento": i.evento
|
|
}
|
|
for i in consultor.inscricoes
|
|
],
|
|
"avaliacoes_comissao": [
|
|
{
|
|
"codigo": a.codigo,
|
|
"tipo": a.tipo,
|
|
"premio": a.premio,
|
|
"ano": a.ano,
|
|
"comissao_tipo": a.comissao_tipo,
|
|
"nome_comissao": a.nome_comissao,
|
|
}
|
|
for a in consultor.avaliacoes_comissao
|
|
],
|
|
"premiacoes": [
|
|
{
|
|
"codigo": p.codigo,
|
|
"tipo": p.tipo,
|
|
"nome_premio": p.nome_premio,
|
|
"ano": p.ano,
|
|
"papel": p.papel,
|
|
}
|
|
for p in consultor.premiacoes
|
|
],
|
|
"bolsas_cnpq": [
|
|
{
|
|
"codigo": b.codigo,
|
|
"nivel": b.nivel,
|
|
"area": b.area
|
|
}
|
|
for b in consultor.bolsas_cnpq
|
|
],
|
|
"participacoes": [
|
|
{
|
|
"codigo": p.codigo,
|
|
"tipo": p.tipo,
|
|
"descricao": p.descricao,
|
|
"ano": p.ano
|
|
}
|
|
for p in consultor.participacoes
|
|
],
|
|
"orientacoes": [
|
|
{
|
|
"codigo": o.codigo,
|
|
"tipo": o.tipo,
|
|
"nivel": o.nivel,
|
|
"ano": o.ano,
|
|
"coorientacao": o.coorientacao,
|
|
"premiada": o.premiada,
|
|
"premiacao_tipo": o.premiacao_tipo,
|
|
}
|
|
for o in consultor.orientacoes
|
|
],
|
|
"membros_banca": [
|
|
{
|
|
"codigo": m.codigo,
|
|
"tipo": m.tipo,
|
|
"nivel": m.nivel,
|
|
"ano": m.ano
|
|
}
|
|
for m in consultor.membros_banca
|
|
],
|
|
"docencias": [
|
|
{
|
|
"programa": d.programa,
|
|
"codigo_programa": d.codigo_programa,
|
|
"ies_sigla": d.ies_sigla,
|
|
"ies_nome": d.ies_nome,
|
|
"categoria": d.categoria,
|
|
"area_avaliacao": d.area_avaliacao,
|
|
"modalidade": d.modalidade,
|
|
"inicio": d.periodo.inicio.isoformat() if d.periodo.inicio else None,
|
|
"fim": d.periodo.fim.isoformat() if d.periodo.fim else None,
|
|
"ativo": d.periodo.ativo,
|
|
"carga_horaria": d.carga_horaria,
|
|
"linhas_pesquisa": d.linhas_pesquisa,
|
|
}
|
|
for d in consultor.docencias
|
|
],
|
|
"idiomas": [
|
|
{
|
|
"idioma": i.idioma,
|
|
"nivel_leitura": i.nivel_leitura,
|
|
"nivel_escrita": i.nivel_escrita,
|
|
"nivel_fala": i.nivel_fala,
|
|
"nivel_compreensao": i.nivel_compreensao,
|
|
}
|
|
for i in consultor.idiomas
|
|
],
|
|
"titulacao": consultor.titulacao,
|
|
"pontuacao": pontuacao,
|
|
}
|
|
|
|
@staticmethod
|
|
def _gerar_entries_ordenadas(consultores: List[dict]) -> List[RankingEntry]:
|
|
consultores_ordenados = sorted(
|
|
consultores,
|
|
key=lambda c: (int(c.get("pontuacao_total", 0)), -int(c.get("id_pessoa", 0))),
|
|
reverse=True,
|
|
)
|
|
entries: List[RankingEntry] = []
|
|
for idx, c in enumerate(consultores_ordenados, start=1):
|
|
c["posicao"] = idx
|
|
entries.append(
|
|
RankingEntry(
|
|
id_pessoa=int(c["id_pessoa"]),
|
|
nome=str(c.get("nome", "")),
|
|
posicao=idx,
|
|
pontuacao_total=int(c.get("pontuacao_total", 0)),
|
|
bloco_a=int(c.get("bloco_a", 0)),
|
|
bloco_b=int(c.get("bloco_b", 0)),
|
|
bloco_c=int(c.get("bloco_c", 0)),
|
|
bloco_d=int(c.get("bloco_d", 0)),
|
|
bloco_e=int(c.get("bloco_e", 0)),
|
|
ativo=bool(c.get("ativo", False)),
|
|
anos_atuacao=float(c.get("anos_atuacao", 0) or 0),
|
|
detalhes=c,
|
|
)
|
|
)
|
|
return entries
|
|
|
|
@staticmethod
|
|
def _obter_estatisticas(entries: List[RankingEntry]) -> Dict[str, Any]:
|
|
if not entries:
|
|
return {
|
|
"total_consultores": 0,
|
|
"total_ativos": 0,
|
|
"total_inativos": 0,
|
|
"ultima_atualizacao": None,
|
|
"pontuacao_media": 0,
|
|
"pontuacao_maxima": 0,
|
|
"pontuacao_minima": 0,
|
|
"media_componentes": {"a": 0, "b": 0, "c": 0, "d": 0, "e": 0},
|
|
}
|
|
|
|
total = len(entries)
|
|
ativos = sum(1 for e in entries if e.ativo)
|
|
inativos = total - ativos
|
|
totais = [e.pontuacao_total for e in entries]
|
|
media_total = sum(totais) / total if total else 0
|
|
|
|
return {
|
|
"total_consultores": total,
|
|
"total_ativos": ativos,
|
|
"total_inativos": inativos,
|
|
"ultima_atualizacao": None,
|
|
"pontuacao_media": float(round(media_total, 2)),
|
|
"pontuacao_maxima": float(max(totais) if totais else 0),
|
|
"pontuacao_minima": float(min(totais) if totais else 0),
|
|
"media_componentes": {
|
|
"a": float(round(sum(e.bloco_a for e in entries) / total, 2)),
|
|
"b": float(round(sum(e.bloco_b for e in entries) / total, 2)),
|
|
"c": float(round(sum(e.bloco_c for e in entries) / total, 2)),
|
|
"d": float(round(sum(e.bloco_d for e in entries) / total, 2)),
|
|
"e": float(round(sum(e.bloco_e for e in entries) / total, 2)),
|
|
},
|
|
}
|
|
|
|
async def _persistir_oracle(self, consultores: List[dict], limpar_antes: bool) -> None:
|
|
import asyncio
|
|
if not self.ranking_oracle_repo:
|
|
return
|
|
|
|
def _sync_persist():
|
|
if limpar_antes:
|
|
self.ranking_oracle_repo.limpar_tabela()
|
|
|
|
batch_size = 2000
|
|
for i in range(0, len(consultores), batch_size):
|
|
batch = consultores[i:i + batch_size]
|
|
self.ranking_oracle_repo.inserir_batch(batch)
|
|
|
|
self.ranking_oracle_repo.atualizar_posicoes()
|
|
|
|
await asyncio.get_event_loop().run_in_executor(None, _sync_persist)
|