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:
3
backend/src/infrastructure/cache/__init__.py
vendored
Normal file
3
backend/src/infrastructure/cache/__init__.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
from .ranking_cache import RankingCache, ranking_cache
|
||||
|
||||
__all__ = ["RankingCache", "ranking_cache"]
|
||||
34
backend/src/infrastructure/cache/ranking_cache.py
vendored
Normal file
34
backend/src/infrastructure/cache/ranking_cache.py
vendored
Normal 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)
|
||||
@@ -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 []
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user