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 class CalculadorPontuacao: @staticmethod 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=novo_fim if not ultimo.ativo else None) else: mesclados.append(p) return mesclados @staticmethod def _anos_completos_periodos(periodos: List[Periodo]) -> int: ref = datetime.now() return sum(p.anos_completos(ref) for p in periodos) @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 = CalculadorPontuacao._mesclar_periodos(periodos) anos_total = CalculadorPontuacao._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 = CalculadorPontuacao._mesclar_periodos(periodos) anos_total = CalculadorPontuacao._anos_completos_periodos(mesclados) tempo = min(anos_total * criterio.multiplicador_tempo, criterio.teto_tempo) bonus = 0 if codigo == "CONS_ATIVO": if consultoria.anos_consecutivos >= 8: bonus += criterio.bonus_continuidade_8anos elif consultoria.anos_consecutivos >= 5: bonus += criterio.bonus_continuidade_5anos elif consultoria.anos_consecutivos >= 3: bonus += criterio.bonus_continuidade_3anos if consultoria.retornos > 0: bonus += criterio.bonus_retorno total_bruto = base + tempo + bonus total = min(total_bruto, criterio.teto) 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}) 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 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 total = min(dados["base"], criterio.teto) atuacoes.append(PontuacaoAtuacao( codigo=codigo, base=dados["base"], tempo=0, bonus=0, 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, )