225 lines
7.8 KiB
Python
225 lines
7.8 KiB
Python
from datetime import datetime
|
|
from typing import List, Dict
|
|
from collections import defaultdict
|
|
|
|
from ..entities.consultor import (
|
|
Consultor,
|
|
CoordenacaoCapes,
|
|
Consultoria,
|
|
Inscricao,
|
|
AvaliacaoComissao,
|
|
Premiacao,
|
|
BolsaCNPQ,
|
|
Participacao,
|
|
Orientacao,
|
|
MembroBanca,
|
|
)
|
|
from ..value_objects.pontuacao import PontuacaoAtuacao, PontuacaoBloco, PontuacaoCompleta
|
|
from ..value_objects.criterios_pontuacao import CRITERIOS, get_criterio, Bloco
|
|
from ..value_objects.periodo import Periodo, mesclar_periodos, anos_completos_periodos
|
|
|
|
|
|
class CalculadorPontuacao:
|
|
|
|
@staticmethod
|
|
def calcular_bloco_a(coordenacoes: List[CoordenacaoCapes]) -> PontuacaoBloco:
|
|
if not coordenacoes:
|
|
return PontuacaoBloco(bloco="A", atuacoes=[])
|
|
|
|
tipos_ordenados = ["CA", "CAJ", "CAJ_MP", "CAM"]
|
|
coord_por_tipo: Dict[str, List[CoordenacaoCapes]] = defaultdict(list)
|
|
for c in coordenacoes:
|
|
codigo = c.codigo.replace("-", "_")
|
|
coord_por_tipo[codigo].append(c)
|
|
|
|
atuacoes = []
|
|
for tipo in tipos_ordenados:
|
|
if tipo not in coord_por_tipo:
|
|
continue
|
|
|
|
criterio = get_criterio(tipo)
|
|
if not criterio:
|
|
continue
|
|
|
|
coords = coord_por_tipo[tipo]
|
|
periodos = [c.periodo for c in coords]
|
|
mesclados = mesclar_periodos(periodos)
|
|
|
|
anos_total = anos_completos_periodos(mesclados)
|
|
ativo = any(c.periodo.ativo for c in coords)
|
|
tem_retorno = len(mesclados) > 1
|
|
|
|
base = criterio.base
|
|
tempo = min(anos_total * criterio.multiplicador_tempo, criterio.teto_tempo)
|
|
bonus_atualidade = criterio.bonus_atualidade if ativo else 0
|
|
bonus_retorno = criterio.bonus_retorno if tem_retorno else 0
|
|
bonus = bonus_atualidade + bonus_retorno
|
|
|
|
total_bruto = base + tempo + bonus
|
|
total = min(total_bruto, criterio.teto)
|
|
|
|
atuacoes.append(PontuacaoAtuacao(
|
|
codigo=tipo,
|
|
base=base,
|
|
tempo=tempo,
|
|
bonus=bonus,
|
|
total=total,
|
|
quantidade=len(coords),
|
|
))
|
|
|
|
return PontuacaoBloco(bloco="A", atuacoes=atuacoes)
|
|
|
|
@staticmethod
|
|
def calcular_bloco_c(consultoria: Consultoria) -> PontuacaoBloco:
|
|
if not consultoria:
|
|
return PontuacaoBloco(bloco="C", atuacoes=[])
|
|
|
|
codigo = consultoria.codigo
|
|
criterio = get_criterio(codigo)
|
|
if not criterio:
|
|
return PontuacaoBloco(bloco="C", atuacoes=[])
|
|
|
|
base = criterio.base
|
|
|
|
tempo = 0
|
|
if criterio.pontua_tempo:
|
|
periodos = consultoria.periodos if consultoria.periodos else [consultoria.periodo]
|
|
mesclados = mesclar_periodos(periodos)
|
|
anos_total = anos_completos_periodos(mesclados)
|
|
tempo = min(anos_total * criterio.multiplicador_tempo, criterio.teto_tempo)
|
|
|
|
bonus = 0
|
|
|
|
# Bônus de continuidade (escalonado, não cumulativo) - apenas CONS_ATIVO
|
|
if codigo == "CONS_ATIVO":
|
|
if consultoria.anos_consecutivos >= 8:
|
|
bonus += 15
|
|
elif consultoria.anos_consecutivos >= 5:
|
|
bonus += 10
|
|
elif consultoria.anos_consecutivos >= 3:
|
|
bonus += 5
|
|
|
|
# Bônus de retorno (uma vez) - apenas CONS_ATIVO
|
|
if consultoria.retornos > 0 and criterio.bonus_retorno:
|
|
bonus += criterio.bonus_retorno
|
|
|
|
total_bruto = base + tempo + bonus
|
|
total = min(total_bruto, criterio.teto) if criterio.teto > 0 else total_bruto
|
|
|
|
atuacoes = [PontuacaoAtuacao(
|
|
codigo=codigo,
|
|
base=base,
|
|
tempo=tempo,
|
|
bonus=bonus,
|
|
total=total,
|
|
quantidade=1,
|
|
)]
|
|
|
|
return PontuacaoBloco(bloco="C", atuacoes=atuacoes)
|
|
|
|
@staticmethod
|
|
def calcular_bloco_d(
|
|
inscricoes: List[Inscricao],
|
|
avaliacoes: List[AvaliacaoComissao],
|
|
premiacoes: List[Premiacao],
|
|
bolsas: List[BolsaCNPQ],
|
|
participacoes: List[Participacao],
|
|
orientacoes: List[Orientacao],
|
|
membros_banca: List[MembroBanca],
|
|
) -> PontuacaoBloco:
|
|
atuacoes = []
|
|
totais_por_codigo: Dict[str, Dict] = defaultdict(lambda: {"base": 0, "qtd": 0, "anos": set()})
|
|
|
|
for insc in inscricoes:
|
|
criterio = get_criterio(insc.codigo)
|
|
if criterio:
|
|
totais_por_codigo[insc.codigo]["base"] += criterio.base
|
|
totais_por_codigo[insc.codigo]["qtd"] += 1
|
|
|
|
for aval in avaliacoes:
|
|
criterio = get_criterio(aval.codigo)
|
|
if criterio:
|
|
totais_por_codigo[aval.codigo]["base"] += criterio.base
|
|
totais_por_codigo[aval.codigo]["qtd"] += 1
|
|
if hasattr(aval, 'ano') and aval.ano:
|
|
totais_por_codigo[aval.codigo]["anos"].add(aval.ano)
|
|
|
|
for prem in premiacoes:
|
|
criterio = get_criterio(prem.codigo)
|
|
if criterio:
|
|
totais_por_codigo[prem.codigo]["base"] += criterio.base
|
|
totais_por_codigo[prem.codigo]["qtd"] += 1
|
|
|
|
for bolsa in bolsas:
|
|
criterio = get_criterio(bolsa.codigo)
|
|
if criterio:
|
|
totais_por_codigo[bolsa.codigo]["base"] += criterio.base
|
|
totais_por_codigo[bolsa.codigo]["qtd"] += 1
|
|
|
|
for part in participacoes:
|
|
criterio = get_criterio(part.codigo)
|
|
if criterio:
|
|
totais_por_codigo[part.codigo]["base"] += criterio.base
|
|
totais_por_codigo[part.codigo]["qtd"] += 1
|
|
|
|
for orient in orientacoes:
|
|
criterio = get_criterio(orient.codigo)
|
|
if criterio:
|
|
totais_por_codigo[orient.codigo]["base"] += criterio.base
|
|
totais_por_codigo[orient.codigo]["qtd"] += 1
|
|
|
|
for mb in membros_banca:
|
|
criterio = get_criterio(mb.codigo)
|
|
if criterio:
|
|
totais_por_codigo[mb.codigo]["base"] += criterio.base
|
|
totais_por_codigo[mb.codigo]["qtd"] += 1
|
|
|
|
for codigo, dados in totais_por_codigo.items():
|
|
criterio = get_criterio(codigo)
|
|
if not criterio:
|
|
continue
|
|
|
|
base_total = dados["base"]
|
|
bonus = 0
|
|
if criterio.bonus_recorrencia_anual > 0:
|
|
anos_distintos = len(dados["anos"])
|
|
bonus_recorrencia = anos_distintos * criterio.bonus_recorrencia_anual
|
|
bonus = min(bonus_recorrencia, criterio.teto_recorrencia)
|
|
|
|
total_bruto = base_total + bonus
|
|
if criterio.teto > 0:
|
|
total = min(total_bruto, criterio.teto)
|
|
else:
|
|
total = total_bruto
|
|
|
|
atuacoes.append(PontuacaoAtuacao(
|
|
codigo=codigo,
|
|
base=base_total,
|
|
tempo=0,
|
|
bonus=bonus,
|
|
total=total,
|
|
quantidade=dados["qtd"],
|
|
))
|
|
|
|
return PontuacaoBloco(bloco="D", atuacoes=atuacoes)
|
|
|
|
@classmethod
|
|
def calcular_pontuacao_completa(cls, consultor: Consultor) -> PontuacaoCompleta:
|
|
bloco_a = cls.calcular_bloco_a(consultor.coordenacoes_capes)
|
|
bloco_c = cls.calcular_bloco_c(consultor.consultoria)
|
|
bloco_d = cls.calcular_bloco_d(
|
|
inscricoes=consultor.inscricoes,
|
|
avaliacoes=consultor.avaliacoes_comissao,
|
|
premiacoes=consultor.premiacoes,
|
|
bolsas=consultor.bolsas_cnpq,
|
|
participacoes=consultor.participacoes,
|
|
orientacoes=consultor.orientacoes,
|
|
membros_banca=consultor.membros_banca,
|
|
)
|
|
|
|
return PontuacaoCompleta(
|
|
bloco_a=bloco_a,
|
|
bloco_c=bloco_c,
|
|
bloco_d=bloco_d,
|
|
)
|