Refatoracao de qualidade de codigo

- 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
This commit is contained in:
Frederico Castro
2025-12-14 21:47:00 -03:00
parent 4a98e8b38c
commit f91651056a
15 changed files with 284 additions and 218 deletions

View File

@@ -0,0 +1,3 @@
from .ranking_cache import RankingCache, ranking_cache
__all__ = ["RankingCache", "ranking_cache"]

View File

@@ -0,0 +1,34 @@
import asyncio
from datetime import datetime
from typing import List, Optional, TYPE_CHECKING
if TYPE_CHECKING:
from ...domain.entities.consultor import Consultor
class RankingCache:
def __init__(self, ttl_seconds: int = 300):
self.ttl = ttl_seconds
self._cache: List["Consultor"] = []
self._last_update: Optional[datetime] = None
self._loading = False
self._lock = asyncio.Lock()
def is_valid(self) -> bool:
if not self._cache or not self._last_update:
return False
return (datetime.now() - self._last_update).total_seconds() < self.ttl
def get(self) -> List["Consultor"]:
return self._cache
def set(self, consultores: List["Consultor"]) -> None:
self._cache = consultores
self._last_update = datetime.now()
def invalidate(self) -> None:
self._cache = []
self._last_update = None
ranking_cache = RankingCache(ttl_seconds=300)

View File

@@ -1,7 +1,11 @@
import logging
import cx_Oracle
from typing import List, Dict, Any, Optional
from contextlib import contextmanager
logger = logging.getLogger(__name__)
class OracleClient:
def __init__(self, user: str, password: str, dsn: str):
@@ -24,14 +28,14 @@ class OracleClient:
)
self._connected = True
except Exception as e:
print(f"AVISO Oracle: {e}")
logger.warning(f"Oracle: {e}")
self._connected = False
def close(self) -> None:
if self._pool:
try:
self._pool.close()
except:
except cx_Oracle.Error:
pass
@property
@@ -71,7 +75,7 @@ class OracleClient:
cursor.close()
return results
except Exception as e:
print(f"AVISO Oracle: falha ao executar query: {e}")
logger.warning(f"Oracle: falha ao executar query: {e}")
self._connected = False
return []

View File

@@ -1,7 +1,8 @@
import asyncio
import logging
from typing import List, Optional, Dict, Any
from datetime import datetime, timedelta
from dateutil import parser as date_parser
import asyncio
from ...domain.entities.consultor import (
Consultor,
@@ -17,33 +18,12 @@ from ...domain.entities.consultor import (
)
from ...domain.repositories.consultor_repository import ConsultorRepository
from ...domain.services.calculador_pontuacao import CalculadorPontuacao
from ...domain.value_objects.periodo import Periodo
from ...domain.value_objects.periodo import Periodo, mesclar_periodos
from ..cache import ranking_cache
from ..elasticsearch.client import ElasticsearchClient
from ..oracle.client import OracleClient
class RankingCache:
def __init__(self, ttl_seconds: int = 300):
self.ttl = ttl_seconds
self._cache: List[Consultor] = []
self._last_update: Optional[datetime] = None
self._loading = False
self._lock = asyncio.Lock()
def is_valid(self) -> bool:
if not self._cache or not self._last_update:
return False
return (datetime.now() - self._last_update).total_seconds() < self.ttl
def get(self) -> List[Consultor]:
return self._cache
def set(self, consultores: List[Consultor]) -> None:
self._cache = consultores
self._last_update = datetime.now()
_ranking_cache = RankingCache(ttl_seconds=300)
logger = logging.getLogger(__name__)
class ConsultorRepositoryImpl(ConsultorRepository):
@@ -58,28 +38,9 @@ class ConsultorRepositoryImpl(ConsultorRepository):
return None
try:
return date_parser.parse(date_str, dayfirst=True)
except:
except (ValueError, TypeError):
return None
def _mesclar_periodos(self, periodos: List[Periodo]) -> List[Periodo]:
if not periodos:
return []
periodos = sorted(periodos, key=lambda p: p.inicio if p.inicio else datetime.min)
mesclados: List[Periodo] = []
for p in periodos:
if not mesclados:
mesclados.append(p)
continue
ultimo = mesclados[-1]
fim_ultimo = ultimo.fim or datetime.now()
fim_atual = p.fim or datetime.now()
if p.inicio and p.inicio <= fim_ultimo:
novo_fim = max(fim_ultimo, fim_atual)
mesclados[-1] = Periodo(inicio=ultimo.inicio, fim=novo_fim if not ultimo.ativo else None)
else:
mesclados.append(p)
return mesclados
def _inferir_tipo_coordenacao(self, coord: Dict[str, Any]) -> str:
dados_coord = coord.get("dadosCoordenacaoArea", {}) or {}
tipo_coord = dados_coord.get("tipo", "").lower()
@@ -182,7 +143,7 @@ class ConsultorRepositoryImpl(ConsultorRepository):
if not periodos:
return None
mesclados = self._mesclar_periodos(periodos)
mesclados = mesclar_periodos(periodos)
periodo_ativo = next((p for p in mesclados if p.ativo), None)
anos_consecutivos = periodo_ativo.anos_completos(datetime.now()) if periodo_ativo else 0
retornos = max(0, len(mesclados) - 1)
@@ -495,7 +456,7 @@ class ConsultorRepositoryImpl(ConsultorRepository):
try:
doc = await self.es_client.buscar_por_id(id_pessoa)
except Exception as e:
print(f"AVISO Elasticsearch: falha ao buscar consultor {id_pessoa}: {e}")
logger.warning(f"Elasticsearch: falha ao buscar consultor {id_pessoa}: {e}")
return None
if not doc:
return None
@@ -511,7 +472,7 @@ class ConsultorRepositoryImpl(ConsultorRepository):
docs = await self.es_client.buscar_com_atuacoes(size=limite, from_=offset)
self.es_disponivel = True
except Exception as e:
print(f"AVISO Elasticsearch: falha ao buscar consultores: {e}")
logger.warning(f"Elasticsearch: falha ao buscar consultores: {e}")
self.es_disponivel = False
return []
consultores = [await self._construir_consultor(doc) for doc in docs]
@@ -524,15 +485,13 @@ class ConsultorRepositoryImpl(ConsultorRepository):
async def buscar_ranking(
self, limite: int = 100, componente: Optional[str] = None
) -> List[Consultor]:
global _ranking_cache
if _ranking_cache.is_valid():
consultores_ordenados = _ranking_cache.get()
if ranking_cache.is_valid():
consultores_ordenados = ranking_cache.get()
return consultores_ordenados[:limite]
async with _ranking_cache._lock:
if _ranking_cache.is_valid():
return _ranking_cache.get()[:limite]
async with ranking_cache._lock:
if ranking_cache.is_valid():
return ranking_cache.get()[:limite]
tamanho_busca = max(limite * 3, 1000)
docs = await self.es_client.buscar_candidatos_ranking(size=tamanho_busca)
@@ -545,7 +504,7 @@ class ConsultorRepositoryImpl(ConsultorRepository):
consultores_ordenados = sorted(
consultores, key=lambda c: c.pontuacao_total, reverse=True
)
_ranking_cache.set(consultores_ordenados)
ranking_cache.set(consultores_ordenados)
return consultores_ordenados[:limite]
@@ -558,6 +517,6 @@ class ConsultorRepositoryImpl(ConsultorRepository):
try:
return await self.es_client.contar_com_atuacoes()
except Exception as e:
print(f"AVISO Elasticsearch: falha ao contar consultores: {e}")
logger.warning(f"Elasticsearch: falha ao contar consultores: {e}")
self.es_disponivel = False
return 0