fix(selos): corrigir geração de selos e adicionar ícones visuais

- Corrigir extração de orientações (tipo "Orientação de Discentes")
- Selos de premiação agora usam campo papel (Autor/Orientador/Coorientador)
- Adicionar ícones visuais aos selos (emojis Unicode)
- Adicionar estilos CSS para novos tipos de selos
- Melhorias no Oracle client e ranking repository
This commit is contained in:
Frederico Castro
2025-12-15 05:01:52 -03:00
parent 97322e5ad7
commit d639b82087
14 changed files with 320 additions and 111 deletions

View File

@@ -1,4 +1,6 @@
import logging
import json
import asyncio
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
@@ -8,14 +10,102 @@ from .routes import router
logger = logging.getLogger(__name__)
from .config import settings
from .dependencies import es_client, get_processar_job
from .dependencies import es_client, oracle_client, ranking_oracle_repo, get_processar_job
from ...application.jobs.scheduler import RankingScheduler
from ...infrastructure.ranking_store import ranking_store, RankingEntry
async def carregar_ranking_do_oracle() -> int:
if not ranking_oracle_repo or not oracle_client:
logger.warning("Oracle não configurado - ranking será carregado do Elasticsearch quando solicitado")
return 0
try:
if not oracle_client.is_connected:
oracle_client.connect()
if not oracle_client.is_connected:
logger.warning("Não foi possível conectar ao Oracle")
return 0
def _sync_load():
total = ranking_oracle_repo.contar_total()
if total == 0:
return []
consultores = ranking_oracle_repo.buscar_paginado(page=1, size=total)
return consultores
consultores = await asyncio.wait_for(
asyncio.get_event_loop().run_in_executor(None, _sync_load),
timeout=30.0
)
if not consultores:
logger.info("Nenhum dado encontrado no Oracle - ranking vazio")
return 0
entries = []
for c in consultores:
try:
detalhes = json.loads(c.json_detalhes) if isinstance(c.json_detalhes, str) else c.json_detalhes or {}
except:
detalhes = {}
entries.append(
RankingEntry(
id_pessoa=c.id_pessoa,
nome=c.nome,
posicao=c.posicao or 0,
pontuacao_total=int(c.pontuacao_total),
bloco_a=int(c.componente_a),
bloco_b=int(c.componente_b),
bloco_c=int(c.componente_c),
bloco_d=int(c.componente_d),
ativo=c.ativo,
anos_atuacao=float(c.anos_atuacao or 0),
detalhes=detalhes,
)
)
await ranking_store.set_entries(entries)
return len(entries)
except asyncio.TimeoutError:
logger.warning("Timeout ao carregar ranking do Oracle")
return 0
except Exception as e:
logger.warning(f"Erro ao carregar ranking do Oracle: {e}")
return 0
@asynccontextmanager
async def lifespan(app: FastAPI):
await es_client.connect()
try:
if oracle_client:
def _connect_oracle():
oracle_client.connect()
return oracle_client.is_connected
connected = await asyncio.wait_for(
asyncio.get_event_loop().run_in_executor(None, _connect_oracle),
timeout=10.0
)
if connected:
logger.info("Conectado ao Oracle")
total = await carregar_ranking_do_oracle()
if total > 0:
logger.info(f"Ranking carregado do Oracle: {total} consultores")
else:
logger.info("Ranking vazio no Oracle - aguardando processamento")
else:
logger.warning("Não foi possível conectar ao Oracle - ranking será carregado do ES")
except asyncio.TimeoutError:
logger.warning("Timeout ao conectar ao Oracle - ranking será carregado do ES")
except Exception as e:
logger.warning(f"Erro ao inicializar Oracle: {e}")
scheduler = None
try:
if settings.SCHEDULER_ENABLED:
@@ -33,6 +123,12 @@ async def lifespan(app: FastAPI):
except:
pass
if oracle_client:
try:
oracle_client.close()
except:
pass
await es_client.close()

View File

@@ -5,13 +5,16 @@ from typing import List
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
# Preferir o alias apontado para o índice vigente do Atuacapes
ES_URL: str = "http://localhost:9200"
ES_INDEX: str = "atuacapes"
ES_USER: str = ""
ES_PASSWORD: str = ""
ES_VERIFY_SSL: bool = True
ORACLE_LOCAL_USER: str = ""
ORACLE_LOCAL_PASSWORD: str = ""
ORACLE_LOCAL_DSN: str = ""
API_HOST: str = "0.0.0.0"
API_PORT: int = 8000
API_RELOAD: bool = True

View File

@@ -1,5 +1,7 @@
from ...infrastructure.elasticsearch.client import ElasticsearchClient
from ...infrastructure.repositories.consultor_repository_impl import ConsultorRepositoryImpl
from ...infrastructure.oracle.client import OracleClient
from ...infrastructure.oracle.ranking_repository import RankingOracleRepository
from ...application.jobs.processar_ranking import ProcessarRankingJob
from ...infrastructure.ranking_store import ranking_store, RankingStore
from .config import settings
@@ -13,6 +15,14 @@ es_client = ElasticsearchClient(
verify_ssl=settings.ES_VERIFY_SSL,
)
oracle_client = OracleClient(
user=settings.ORACLE_LOCAL_USER,
password=settings.ORACLE_LOCAL_PASSWORD,
dsn=settings.ORACLE_LOCAL_DSN,
) if settings.ORACLE_LOCAL_USER and settings.ORACLE_LOCAL_DSN else None
ranking_oracle_repo = RankingOracleRepository(oracle_client) if oracle_client else None
_repository: ConsultorRepositoryImpl = None
_processar_job: ProcessarRankingJob = None
@@ -20,7 +30,7 @@ _processar_job: ProcessarRankingJob = None
def get_repository() -> ConsultorRepositoryImpl:
global _repository
if _repository is None:
_repository = ConsultorRepositoryImpl(es_client=es_client, oracle_client=None)
_repository = ConsultorRepositoryImpl(es_client=es_client, oracle_client=oracle_client)
return _repository
@@ -28,11 +38,20 @@ def get_ranking_store() -> RankingStore:
return ranking_store
def get_oracle_client() -> OracleClient:
return oracle_client
def get_ranking_oracle_repo() -> RankingOracleRepository:
return ranking_oracle_repo
def get_processar_job() -> ProcessarRankingJob:
global _processar_job
if _processar_job is None:
_processar_job = ProcessarRankingJob(
es_client=es_client,
ranking_store=ranking_store,
ranking_oracle_repo=ranking_oracle_repo,
)
return _processar_job