fix: Correções no cálculo de pontuação e extração de dados

- Refatora calculador para agrupar coordenações por tipo com hierarquia
- Corrige contagem de eventos separando SAE de consultorias
- Melhora extração de área de avaliação usando dadosConsultoria
- Ajusta pontuação de premiações conforme regras documentadas
- Usa alias ES em vez de índice específico
This commit is contained in:
Frederico Castro
2025-12-09 19:57:35 -03:00
parent 59ae516ee0
commit e7b34e33a8
3 changed files with 98 additions and 36 deletions

View File

@@ -17,21 +17,40 @@ class CalculadorPontuacao:
if not coordenacoes: if not coordenacoes:
return ComponentePontuacao(base=0, tempo=0, extras=0, bonus=0, retorno=0) return ComponentePontuacao(base=0, tempo=0, extras=0, bonus=0, retorno=0)
coord_atual = next((c for c in coordenacoes if c.periodo.ativo), None)
if not coord_atual:
return ComponentePontuacao(base=0, tempo=0, extras=0, bonus=0, retorno=0)
base_map = {"CA": 200, "CAJ": 150, "CAJ-MP": 120, "CAM": 100} base_map = {"CA": 200, "CAJ": 150, "CAJ-MP": 120, "CAM": 100}
tempo_max_map = {"CA": 100, "CAJ": 80, "CAJ-MP": 60, "CAM": 50} tempo_max_map = {"CA": 100, "CAJ": 80, "CAJ-MP": 60, "CAM": 50}
bonus_atual_map = {"CA": 30, "CAJ": 20, "CAJ-MP": 15, "CAM": 10} bonus_atual_map = {"CA": 30, "CAJ": 20, "CAJ-MP": 15, "CAM": 10}
mult_tempo_map = {"CA": 10, "CAJ": 8, "CAJ-MP": 6, "CAM": 5}
base = base_map.get(coord_atual.tipo, 0) # Agrupa por tipo de coordenação e considera o melhor tipo (hierarquia)
anos = coord_atual.periodo.anos_decorridos tipos_ordenados = ["CA", "CAJ", "CAJ-MP", "CAM"]
tempo = min(int(anos * 10), tempo_max_map.get(coord_atual.tipo, 0)) coord_por_tipo = {t: [] for t in tipos_ordenados}
for c in coordenacoes:
coord_por_tipo.setdefault(c.tipo, []).append(c)
extras = min(len(coord_atual.areas_adicionais) * 20, 100) coord_escolhida_tipo = None
bonus = bonus_atual_map.get(coord_atual.tipo, 0) if coord_atual.periodo.ativo else 0 for t in tipos_ordenados:
retorno = 20 if coord_atual.ja_coordenou_antes else 0 if coord_por_tipo.get(t):
coord_escolhida_tipo = t
break
if not coord_escolhida_tipo:
return ComponentePontuacao(base=0, tempo=0, extras=0, bonus=0, retorno=0)
coord_do_tipo = coord_por_tipo.get(coord_escolhida_tipo, [])
anos_total = sum(c.periodo.anos_decorridos for c in coord_do_tipo)
ativo = any(c.periodo.ativo for c in coord_do_tipo)
base = base_map.get(coord_escolhida_tipo, 0)
tempo = min(int(anos_total * mult_tempo_map.get(coord_escolhida_tipo, 0)), tempo_max_map.get(coord_escolhida_tipo, 0))
extras = 0
areas_adicionais = [a for c in coord_do_tipo for a in c.areas_adicionais]
if areas_adicionais:
extras = min(len(set(areas_adicionais)) * 20, 100)
bonus = bonus_atual_map.get(coord_escolhida_tipo, 0) if ativo else 0
retorno = 20 if len(coord_do_tipo) > 1 else 0
return ComponentePontuacao(base=base, tempo=tempo, extras=extras, bonus=bonus, retorno=retorno) return ComponentePontuacao(base=base, tempo=tempo, extras=extras, bonus=bonus, retorno=retorno)

View File

@@ -63,33 +63,51 @@ class ConsultorRepositoryImpl(ConsultorRepository):
if not consultorias: if not consultorias:
return None return None
datas_inicio = [ datas_inicio_consultoria = [
self._parse_date(c.get("inicio")) self._parse_date(c.get("inicio"))
for c in consultorias for c in consultorias
] ]
datas_inicio = [d for d in datas_inicio if d] datas_inicio_consultoria = [d for d in datas_inicio_consultoria if d]
datas_fim = [ if not datas_inicio_consultoria:
self._parse_date(c.get("fim"))
for c in consultorias
]
datas_fim = [d for d in datas_fim if d]
if not datas_inicio:
return None return None
limite_recente = datetime.now() - timedelta(days=730) eventos_sae = [
eventos_recentes = sum(1 for d in datas_fim if d >= limite_recente) a for a in atuacoes if a.get("tipo") == "Evento"
]
areas = list({c.get("areaAvaliacao", "N/A") for c in consultorias if c.get("areaAvaliacao")}) total_eventos = len(eventos_sae)
# considerar últimos 24 meses como janela de atividade
limite_recente = datetime.now() - timedelta(days=730)
eventos_recentes = 0
for ev in eventos_sae:
data_fim = self._parse_date(ev.get("fim")) or self._parse_date(ev.get("inicio"))
if data_fim and data_fim >= limite_recente:
eventos_recentes += 1
dados_consultoria = consultorias[0].get("dadosConsultoria", {}) or {}
areas = []
for c in consultorias:
dc = c.get("dadosConsultoria", {}) or {}
area = dc.get("areaAvaliacao") or c.get("areaAvaliacao")
if area:
areas.append(area)
areas = list(set(areas)) if areas else ["N/A"]
vezes_responsavel = sum(1 for c in consultorias if c.get("responsavel", False)) vezes_responsavel = sum(1 for c in consultorias if c.get("responsavel", False))
datas_fim_consultoria = [
self._parse_date(c.get("fim"))
for c in consultorias
]
datas_fim_consultoria = [d for d in datas_fim_consultoria if d]
return Consultoria( return Consultoria(
total_eventos=len(consultorias), total_eventos=total_eventos,
eventos_recentes=eventos_recentes, eventos_recentes=eventos_recentes,
primeiro_evento=min(datas_inicio), primeiro_evento=min(datas_inicio_consultoria),
ultimo_evento=max(datas_fim) if datas_fim else datetime.now(), ultimo_evento=max(datas_fim_consultoria) if datas_fim_consultoria else datetime.now(),
vezes_responsavel=vezes_responsavel, vezes_responsavel=vezes_responsavel,
areas=areas, areas=areas,
) )
@@ -116,25 +134,46 @@ class ConsultorRepositoryImpl(ConsultorRepository):
tipo = self._inferir_tipo_coordenacao(coord) tipo = self._inferir_tipo_coordenacao(coord)
fim = self._parse_date(coord.get("fim")) fim = self._parse_date(coord.get("fim"))
dados_coord = coord.get("dadosCoordenacaoArea", {}) or {}
area_avaliacao_obj = dados_coord.get("areaAvaliacao", {}) or {}
area_avaliacao = area_avaliacao_obj.get("nome") if isinstance(area_avaliacao_obj, dict) else coord.get("areaAvaliacao", "N/A")
if not area_avaliacao:
area_avaliacao = coord.get("descricao", "N/A").split(" - ")[0] if coord.get("descricao") else "N/A"
resultado.append( resultado.append(
CoordenacaoCapes( CoordenacaoCapes(
tipo=tipo, tipo=tipo,
area_avaliacao=coord.get("areaAvaliacao", "N/A"), area_avaliacao=area_avaliacao,
periodo=Periodo(inicio=inicio, fim=fim), periodo=Periodo(inicio=inicio, fim=fim),
areas_adicionais=[], areas_adicionais=[],
ja_coordenou_antes=False, ja_coordenou_antes=len(resultado) > 0,
) )
) )
return resultado return resultado
def _inferir_tipo_coordenacao(self, coord: Dict[str, Any]) -> str: def _inferir_tipo_coordenacao(self, coord: Dict[str, Any]) -> str:
nome = coord.get("nome", "").lower() dados_coord = coord.get("dadosCoordenacaoArea", {}) or {}
if "câmara" in nome or "camara" in nome: tipo_coord = dados_coord.get("tipo", "").lower()
if "câmara" in tipo_coord or "camara" in tipo_coord:
return "CAM" return "CAM"
elif "mestrado profissional" in nome: elif "adjunt" in tipo_coord:
if "profissional" in tipo_coord or "mestrado" in tipo_coord:
return "CAJ-MP" return "CAJ-MP"
elif "adjunta" in nome: return "CAJ"
elif "coordenador de área" in tipo_coord:
return "CA"
descricao = coord.get("descricao", "").lower()
nome = coord.get("nome", "").lower()
texto = f"{descricao} {nome}"
if "câmara" in texto or "camara" in texto:
return "CAM"
elif "mestrado profissional" in texto:
return "CAJ-MP"
elif "adjunt" in texto:
return "CAJ" return "CAJ"
else: else:
return "CA" return "CA"
@@ -148,6 +187,7 @@ class ConsultorRepositoryImpl(ConsultorRepository):
"Premiação Prêmio", "Premiação Prêmio",
"Avaliação Prêmio", "Avaliação Prêmio",
"Inscrição Prêmio", "Inscrição Prêmio",
"Premiação",
] ]
] ]
@@ -169,10 +209,12 @@ class ConsultorRepositoryImpl(ConsultorRepository):
return premiacoes return premiacoes
def _calcular_pontos_premiacao(self, tipo: str) -> int: def _calcular_pontos_premiacao(self, tipo: str) -> int:
# Aproximação das regras (D) seguindo .claude/rules/ranking-consultores-capes.md
mapa = { mapa = {
"Premiação Prêmio": 60, "Premiação Prêmio": 150,
"Premiação": 150,
"Avaliação Prêmio": 40, "Avaliação Prêmio": 40,
"Inscrição Prêmio": 20, "Inscrição Prêmio": 10,
} }
return mapa.get(tipo, 0) return mapa.get(tipo, 0)
@@ -186,9 +228,9 @@ class ConsultorRepositoryImpl(ConsultorRepository):
premiacoes = self._extrair_premiacoes(atuacoes) premiacoes = self._extrair_premiacoes(atuacoes)
coordenacoes_programas_raw = [] coordenacoes_programas_raw = []
if self.oracle_client.is_connected: if self.oracle_client and self.oracle_client.is_connected:
try: try:
coordenacoes_programas_raw = self.oracle_client.buscar_coordenacoes_programa(id_pessoa) coordenacoes_programas_raw = self.oracle_client.buscar_coordenacoes_programa(int(id_pessoa))
except Exception as e: except Exception as e:
print(f"AVISO Oracle: erro ao buscar coordenacoes do programa para {id_pessoa}: {e}") print(f"AVISO Oracle: erro ao buscar coordenacoes do programa para {id_pessoa}: {e}")
coordenacoes_programas = [ coordenacoes_programas = [

View File

@@ -5,8 +5,9 @@ from typing import List
class Settings(BaseSettings): class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8") model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
# Preferir o alias apontado para o índice vigente do Atuacapes
ES_URL: str = "http://localhost:9200" ES_URL: str = "http://localhost:9200"
ES_INDEX: str = "atuacapes__1763197236" ES_INDEX: str = "atuacapes"
ES_USER: str = "" ES_USER: str = ""
ES_PASSWORD: str = "" ES_PASSWORD: str = ""