Corrige bug onde consultores ativos com vínculos sobrepostos tinham anos_consecutivos zerados incorretamente. Problema: Ao mesclar períodos sobrepostos (ex: UP encerrado + UTFPR ativo), o período mesclado era marcado como encerrado, resultando em anos_consecutivos = 0 mesmo para consultores ativos. Solução: Período mesclado é ativo se QUALQUER um dos períodos originais for ativo (lógica OR em vez de considerar apenas o primeiro período). Impacto: - Consultores ativos com múltiplos vínculos agora recebem corretamente bônus de continuidade (até +20 pontos) - Exemplo: Valdir Fernandes (#1) tinha 0 anos consecutivos, agora tem 10 Otimizações adicionais: - Aumenta batch_size de 5.000 para 10.000 consultores - Reduz tempo de processamento de ~60min para ~25min (58% mais rápido) - Reduz requisições ao Elasticsearch pela metade Arquivos alterados: - backend/src/domain/value_objects/periodo.py: lógica de mesclagem corrigida - backend/src/application/jobs/processar_ranking.py: batch_size otimizado Testado com 350.222 consultores em 25min 35s
64 lines
2.0 KiB
Python
64 lines
2.0 KiB
Python
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Periodo:
|
|
inicio: datetime
|
|
fim: Optional[datetime] = None
|
|
|
|
@property
|
|
def ativo(self) -> bool:
|
|
return self.fim is None
|
|
|
|
@property
|
|
def anos_decorridos(self) -> float:
|
|
fim = self.fim if self.fim else datetime.now()
|
|
dias = (fim - self.inicio).days
|
|
return round(dias / 365.25, 1)
|
|
|
|
def anos_completos(self, data_referencia: Optional[datetime] = None) -> int:
|
|
"""
|
|
Retorna apenas anos completos entre início e fim (ou data de referência).
|
|
Usado para pontuação que desconsidera frações de ano.
|
|
"""
|
|
fim = self.fim or data_referencia or datetime.now()
|
|
if fim < self.inicio:
|
|
return 0
|
|
return int((fim - self.inicio).days // 365)
|
|
|
|
def __post_init__(self) -> None:
|
|
if self.fim and self.fim < self.inicio:
|
|
object.__setattr__(self, "fim", None)
|
|
|
|
|
|
def mesclar_periodos(periodos: List[Periodo]) -> List[Periodo]:
|
|
if not periodos:
|
|
return []
|
|
periodos_ordenados = sorted(
|
|
periodos, key=lambda p: p.inicio if p.inicio else datetime.min
|
|
)
|
|
mesclados: List[Periodo] = []
|
|
for p in periodos_ordenados:
|
|
if not mesclados:
|
|
mesclados.append(p)
|
|
continue
|
|
ultimo = mesclados[-1]
|
|
ultimo_fim = ultimo.fim or datetime.now()
|
|
atual_fim = p.fim or datetime.now()
|
|
if p.inicio and p.inicio <= ultimo_fim:
|
|
novo_fim = max(ultimo_fim, atual_fim)
|
|
mesclados[-1] = Periodo(
|
|
inicio=ultimo.inicio,
|
|
fim=None if (ultimo.ativo or p.ativo) else novo_fim
|
|
)
|
|
else:
|
|
mesclados.append(p)
|
|
return mesclados
|
|
|
|
|
|
def anos_completos_periodos(periodos: List[Periodo], data_ref: Optional[datetime] = None) -> int:
|
|
ref = data_ref or datetime.now()
|
|
return sum(p.anos_completos(ref) for p in periodos)
|