diff --git a/.gitignore b/.gitignore index b6b2bac..1b7f05c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .env +docs/ __pycache__/ *.py[cod] *$py.class diff --git a/TIPOS_ATUACAO_ELASTICSEARCH.md b/TIPOS_ATUACAO_ELASTICSEARCH.md deleted file mode 100644 index dbd450c..0000000 --- a/TIPOS_ATUACAO_ELASTICSEARCH.md +++ /dev/null @@ -1,407 +0,0 @@ -# Tipos de Atuação Disponíveis no Elasticsearch (AtuaCAPES) - -> Mapeamento completo dos tipos de atuação encontrados no índice `atuacapes` do Elasticsearch CAPES - -## Visão Geral - -Este documento lista todos os tipos de atuação (`atuacoes.tipo`) disponíveis no índice Elasticsearch do sistema AtuaCAPES, identificados através da análise do código implementado e da documentação do projeto. - ---- - -## Tipos de Atuação Implementados - -### Componente A - Coordenação CAPES - -| Tipo | Descrição | Boost | Pontuação Base | -|------|-----------|-------|----------------| -| `Coordenação de Área de Avaliação` | Coordenadores ativos (CA, CAJ, CAJ-MP, CAM) | 10 | 200-100 pts | -| `Histórico de Coordenação de Área de Avaliação` | Coordenadores históricos (mandatos encerrados) | 5 | 200-100 pts | - -**Subtipos identificados em `dadosCoordenacaoArea.tipo`:** -- `Coordenador de Área` (CA) - Base: 200 pts -- `Coordenador Adjunto` (CAJ) - Base: 150 pts -- `Coordenador Adjunto de Mestrado Profissionalizante` (CAJ-MP) - Base: 120 pts -- `Coordenador de Câmara Temática` (CAM) - Base: 100 pts - -### Componente C - Consultoria - -| Tipo | Descrição | Boost | Pontuação Base | -|------|-----------|-------|----------------| -| `Consultor` | Registro de consultoria ativa ou histórica | 2 | 150 pts (ativo) | -| `Histórico de Consultoria` | Consultorias encerradas | 1 | 100 pts (histórico) | - -**Valores de `situacaoConsultoria`:** -- `Atividade Contínua` - Consultor Ativo -- `Ativo` - Consultor Ativo -- Outros valores ou com `inativacaoSituacao` preenchido = Histórico - -### Componente D - Premiações - -| Tipo | Descrição | Boost | Pontuação | -|------|-----------|-------|-----------| -| `Premiação Prêmio` | Prêmio recebido | 3 | 60-180 pts | -| `Avaliação Prêmio` | Participação em avaliação/comissão | 2 | 2-115 pts | -| `Inscrição Prêmio` | Inscrição em prêmio | 1 | 1-2 pts | - -**Níveis de Premiação:** - -#### Nível 1 - Grande Prêmio CAPES -- `dadosPremiacaoPremio.premiacao`: "Grande Prêmio" -- Pontuação: 150 + 50 = 200 pts (limitado a 180) - -#### Nível 1 - Prêmio CAPES de Tese (PCT) -- `dadosPremiacaoPremio.premio`: "PCT" -- `dadosPremiacaoPremio.premiacao`: "Prêmio" (+25) ou "Menção Honrosa" (+15) -- Pontuação base: 100 pts (máx 150 pts) - -#### Nível 2 - Institucionais -- Interfarma, Vale-CAPES, etc. -- Base: 30 pts -- Prêmio: +20 pts / Menção: +10 pts -- Máximo: 60 pts - -#### Nível 3 - Autoinscrição -- CDTDN, Vínculos Familiares, etc. -- Base: 10 pts -- Prêmio: +5 pts / Menção: +3 pts -- Máximo: 20 pts - -### Outros Tipos Identificados - -| Tipo | Descrição | Uso no Ranking | -|------|-----------|----------------| -| `Evento` | Participação em eventos SAE | Sim - bônus consultoria | - -**Campos relacionados a eventos:** -- `dadosEvento.consultorResponsavel`: "Sim" ou "Não" -- Eventos recentes (últimos 2 anos): bonus para consultoria ativa -- Responsável por evento: +5 pts por vez (máx 25) - ---- - -## Estrutura de Dados por Componente - -### Coordenação CAPES - -```json -{ - "tipo": "Coordenação de Área de Avaliação", - "inicio": "01/01/2020", - "fim": null, - "dadosCoordenacaoArea": { - "tipo": "Coordenador de Área", - "areaAvaliacao": { - "id": 123, - "nome": "CIÊNCIAS AMBIENTAIS" - }, - "inicioVinculacao": "01/01/2020", - "fimVinculacao": null, - "colegio": [ - {"nome": "Colégio de Ciências Exatas, Tecnológicas e Multidisciplinar"} - ], - "portaria": "123/2020", - "ies": { - "nome": "Universidade Federal do Brasil", - "sigla": "UFB" - } - } -} -``` - -### Consultoria - -```json -{ - "tipo": "Consultor", - "inicio": "15/03/2018", - "fim": null, - "dadosConsultoria": { - "situacaoConsultoria": "Atividade Contínua", - "inicioSituacao": "15/03/2018", - "inativacaoSituacao": null, - "ies": { - "nome": "Universidade Federal do Brasil", - "sigla": "UFB" - }, - "areaConhecimentoPos": [ - { - "areaAvaliacao": { - "nome": "CIÊNCIAS AMBIENTAIS" - } - } - ], - "areaPesquisa": [ - {"descricao": "Mudanças Climáticas"} - ] - } -} -``` - -### Premiações - -```json -{ - "tipo": "Premiação Prêmio", - "dadosPremiacaoPremio": { - "nomePremio": "Prêmio CAPES de Tese", - "premio": "PCT", - "premiacao": "Prêmio", - "ano": 2022, - "areaConhecimento": { - "areaAvaliacao": { - "nome": "CIÊNCIAS AMBIENTAIS" - } - }, - "papelPessoa": "Autor" - } -} -``` - -### Participação em Prêmios - -```json -{ - "tipo": "Avaliação Prêmio", - "dadosParticipacaoPremio": { - "tipo": "Membro de Comissão", - "premio": "PCT", - "ano": 2023, - "comissao": { - "nome": "Comissão Julgadora", - "tipo": "Avaliação" - }, - "areaAvaliacao": { - "nome": "CIÊNCIAS AMBIENTAIS" - } - } -} -``` - -### Eventos - -```json -{ - "tipo": "Evento", - "inicio": "10/05/2023", - "fim": "15/05/2023", - "dadosEvento": { - "consultorResponsavel": "Sim", - "nome": "Seminário de Avaliação", - "tipo": "Presencial" - } -} -``` - ---- - -## Query de Agregação - Descobrir Todos os Tipos - -Para descobrir todos os tipos de atuação disponíveis no índice: - -```json -{ - "size": 0, - "aggs": { - "tipos": { - "nested": {"path": "atuacoes"}, - "aggs": { - "tipos_unicos": { - "terms": { - "field": "atuacoes.tipo.keyword", - "size": 100 - } - } - } - } - } -} -``` - ---- - -## Mapeamento de Tipos vs. Componentes de Pontuação - -| Componente | Tipos Relacionados | Pontuação Máxima | -|------------|-------------------|------------------| -| **A - Coordenação CAPES** | `Coordenação de Área de Avaliação`
`Histórico de Coordenação de Área de Avaliação` | 450 pts (CA) | -| **B - Coordenação PPG** | *Não existe no ES - usar Oracle* | 180 pts | -| **C - Consultoria** | `Consultor`
`Histórico de Consultoria`
`Evento` (bônus) | 230 pts | -| **D - Premiações** | `Premiação Prêmio`
`Avaliação Prêmio`
`Inscrição Prêmio` | 180 pts | - ---- - -## Campos Faltantes / Limitações - -### Não Disponíveis no Elasticsearch - -1. **Coordenação de Programa (PPG)** - - Não existe tipo `atuacoes.tipo` para coordenação de programa - - Fonte alternativa: Oracle `SUCUPIRA_PAINEL.VM_COORDENADOR` - -2. **Nota do PPG** - - Não disponível em `dadosGestaoPrograma` - - Fonte alternativa: Oracle `VM_PROGRAMA_SUCUPIRA.DS_CONCEITO` - -3. **Dados detalhados de Câmara Temática** - - Inferido pelo campo `dadosCoordenacaoArea.tipo` ou `nome` - - Pode haver imprecisão na classificação - -4. **Situação "Falecido"** - - Existe `dadosPessoais.anoObito` mas não `situacaoConsultoria="Falecido"` - ---- - -## Implementação no Código - -### Arquivo: `backend/src/infrastructure/elasticsearch/client.py` - -Query principal com boost por tipo: - -```python -async def buscar_candidatos_ranking(self, size: int = 1000) -> List[Dict[str, Any]]: - query = { - "size": size, - "query": { - "bool": { - "should": [ - # Coordenação CAPES - { - "nested": { - "path": "atuacoes", - "query": { - "bool": { - "should": [ - {"term": {"atuacoes.tipo": {"value": "Coordenação de Área de Avaliação", "boost": 10}}}, - {"term": {"atuacoes.tipo": {"value": "Histórico de Coordenação de Área de Avaliação", "boost": 5}}} - ] - } - }, - "score_mode": "sum" - } - }, - # Consultoria - { - "nested": { - "path": "atuacoes", - "query": { - "bool": { - "should": [ - {"term": {"atuacoes.tipo": {"value": "Consultor", "boost": 2}}}, - {"term": {"atuacoes.tipo": {"value": "Histórico de Consultoria", "boost": 1}}} - ] - } - }, - "score_mode": "sum" - } - }, - # Premiações - { - "nested": { - "path": "atuacoes", - "query": { - "bool": { - "should": [ - {"term": {"atuacoes.tipo": {"value": "Premiação Prêmio", "boost": 3}}}, - {"term": {"atuacoes.tipo": {"value": "Avaliação Prêmio", "boost": 2}}}, - {"term": {"atuacoes.tipo": {"value": "Inscrição Prêmio", "boost": 1}}} - ] - } - }, - "score_mode": "sum" - } - } - ], - "minimum_should_match": 1 - } - }, - "_source": ["id", "dadosPessoais", "atuacoes"], - "sort": [{"_score": "desc"}] - } -``` - -### Arquivo: `backend/src/infrastructure/repositories/consultor_repository_impl.py` - -Extração de tipos: - -```python -# Consultoria -consultorias = [ - a for a in atuacoes - if a.get("tipo") in ["Consultor", "Histórico de Consultoria"] -] - -# Coordenação CAPES -coordenacoes = [ - a for a in atuacoes - if a.get("tipo") in [ - "Coordenação de Área de Avaliação", - "Histórico de Coordenação de Área de Avaliação", - ] -] - -# Eventos (bônus consultoria) -eventos_sae = [ - a for a in atuacoes - if a.get("tipo") == "Evento" -] - -# Premiações (todos os tipos são processados genericamente) -premiacoes = [ - a for a in atuacoes - if a.get("dadosPremiacaoPremio") or - a.get("dadosParticipacaoPremio") or - a.get("dadosParticipacaoInscricaoPremio") -] -``` - ---- - -## Estatísticas - -### Estimativas de Volume (baseado em documentação) - -| Tipo | Quantidade Estimada | -|------|---------------------| -| Coordenadores de área (ativos) | ~200 | -| Coordenadores históricos | ~322 | -| Consultores | ~52.551 | -| Premiações | ~63.799 | -| **Total de registros com atuações** | **~90.482** | - -### Boost Aplicado - -O boost é usado para priorizar candidatos no score inicial do Elasticsearch: - -| Tipo | Boost | Justificativa | -|------|-------|---------------| -| Coordenação de Área (ativa) | 10 | Máxima prioridade (200-450 pts) | -| Coordenação de Área (histórica) | 5 | Alta prioridade histórica | -| Premiação Prêmio | 3 | Mérito significativo | -| Consultor | 2 | Base importante | -| Avaliação Prêmio | 2 | Participação ativa | -| Histórico de Consultoria | 1 | Relevância histórica | -| Inscrição Prêmio | 1 | Participação básica | - ---- - -## Notas de Implementação - -1. **Paginação**: Query principal retorna top 1000 candidatos ordenados por score ES -2. **Recálculo**: Python recalcula pontuação completa e reordena por pontuação real -3. **Cache**: Resultados cacheados por 5 minutos (TTL=300s) -4. **Performance**: Primeira requisição ~1m34s, cacheadas ~0.27s -5. **Timeout**: 120s para queries ES - ---- - -## Referências - -- **Documentação**: `/home/fred/projetos/ranking/.claude/rules/ranking-queries-elasticsearch.md` -- **Critérios**: `/home/fred/projetos/ranking/.claude/rules/ranking-consultores-capes.md` -- **Implementação ES**: `/home/fred/projetos/ranking/backend/src/infrastructure/elasticsearch/client.py` -- **Extração**: `/home/fred/projetos/ranking/backend/src/infrastructure/repositories/consultor_repository_impl.py` - ---- - -**Última atualização**: 2025-12-13 -**Índice Elasticsearch**: `atuacapes` (servidor: `elastic-atuacapes.hom.capes.gov.br:9200`) diff --git a/TOP_10_RANKING_CAPES.md b/TOP_10_RANKING_CAPES.md deleted file mode 100644 index 1836915..0000000 --- a/TOP_10_RANKING_CAPES.md +++ /dev/null @@ -1,291 +0,0 @@ -# TOP 10 CONSULTORES - RANKING CAPES - -**Data da Consulta:** 13/12/2025 -**Base de Dados:** Elasticsearch AtuaCAPES (índice: atuacapes) -**Total de Candidatos Analisados:** 100 -**Critérios:** Sistema de Ranking Integrado CAPES v1.0 - ---- - -## Resumo Executivo - -O ranking foi calculado com base em 4 componentes principais: - -- **Componente A:** Coordenação CAPES (máx 450 pts) - CA, CAJ, CAJ-MP, CAM -- **Componente B:** Coordenação de Programa PPG (máx 180 pts) - *Não disponível nesta consulta* -- **Componente C:** Consultoria (máx 230 pts) -- **Componente D:** Premiações (máx 180 pts) - -**Pontuação Máxima Teórica:** 1.040 pontos -**Pontuação Máxima Observada:** 685 pontos (65,9% do máximo) - ---- - -## Top 10 Consultores - -### 1º LUGAR - EDSON APARECIDO MITISHITA -**ID:** 519524 -**Pontuação Total:** 685 pontos - -**Breakdown de Pontuação:** -- **Componente A (Coordenação CAPES):** 340 pts - - Base: 200 (Coordenador de Área) - - Tempo: 90 (9 anos completos) - - Áreas Adicionais: 0 - - Bônus Atualidade: 30 (coordenação ativa) - - Retorno: 20 (já coordenou antes) - -- **Componente B (Coordenação PPG):** 0 pts - -- **Componente C (Consultoria):** 165 pts - - Base: 100 (histórico) - - Tempo: 50 (10+ anos) - - Total eventos: 2 - - Eventos recentes: 0 (último há mais de 2 anos) - - Bônus continuidade: 15 (8+ anos consecutivos) - -- **Componente D (Premiações):** 180 pts - - Total de premiações: 7 - -**Perfil:** -- Anos de atuação: 11,3 anos -- Status: Inativo (sem eventos recentes) -- Veterano: Sim -- Área principal: GEOCIÊNCIAS -- Coordenações: 3 (incluindo 1 ativa) - -**Destaque:** Possui a maior pontuação no Componente A devido à combinação de coordenação ativa (bônus de 30 pts) + retorno (20 pts) + 9 anos completos de experiência. - ---- - -### 2º LUGAR - ANDRE MOREIRA CUNHA -**ID:** 45997 -**Pontuação Total:** 675 pontos - -**Breakdown de Pontuação:** -- **Componente A:** 330 pts - - Base: 200 - - Tempo: 100 (10 anos - máximo permitido) - - Bônus Atualidade: 30 - - Retorno: 0 (sem retorno) - -- **Componente C:** 165 pts -- **Componente D:** 180 pts (7 premiações) - -**Perfil:** -- Anos de atuação: 14,2 anos -- Status: Inativo -- Área principal: ECONOMIA -- Coordenações: 2 (1 ativa) - -**Destaque:** Atingiu o teto máximo de pontuação por tempo (100 pts) no Componente A. - ---- - -### 3º LUGAR - FLAVIO AUGUSTO SENRA RIBEIRO -**ID:** 7794 -**Pontuação Total:** 665 pontos - -**Breakdown de Pontuação:** -- **Componente A:** 320 pts - - Base: 200 - - Tempo: 100 - - Áreas Adicionais: 20 (coordenou 2 áreas diferentes) - - Bônus Atualidade: 0 (coordenação encerrada) - - Retorno: 0 - -- **Componente C:** 165 pts -- **Componente D:** 180 pts (11 premiações) - -**Perfil:** -- Anos de atuação: 14,5 anos -- Status: Inativo -- Áreas: FILOSOFIA, CIÊNCIAS DA RELIGIÃO E TEOLOGIA -- Coordenações: 3 (todas encerradas) - -**Destaque:** Único no top 10 com pontuação por áreas adicionais (coordenou múltiplas áreas). Maior número de premiações entre os 10 primeiros (11 premiações). - ---- - -### 4º ao 10º LUGAR (Empate em 655 pontos) - -Os consultores da 4ª à 10ª posição compartilham a mesma pontuação total de **655 pontos**, com o seguinte padrão: - -**Componente A:** 310 pts -- Base: 200 -- Tempo: 60 (6 anos) -- Bônus Atualidade: 30 -- Retorno: 20 - -**Componente C:** 165 pts -**Componente D:** 180 pts - ---- - -#### 4º - CARLOS FREDERICO MARTINS MENCK -**ID:** 20912 -**Área:** CIÊNCIAS BIOLÓGICAS I -**Anos:** 14,5 | **Status:** Inativo -**Premiações:** 25 (maior número absoluto) -**Coordenações:** 2 (1 ativa) - ---- - -#### 5º - MARCELO ALBANO MORET SIMOES GONCALVES -**ID:** 5888 -**Área:** INTERDISCIPLINAR -**Anos:** 11,0 | **Status:** Ativo ✓ -**Premiações:** 9 -**Eventos recentes:** 2 -**Coordenações:** 2 (1 ativa) - ---- - -#### 6º - CARLOS PELLESCHI TABORDA -**ID:** 15100 -**Área:** CIÊNCIAS BIOLÓGICAS III -**Anos:** 12,6 | **Status:** Inativo -**Premiações:** 15 -**Coordenações:** 2 (1 ativa) - ---- - -#### 7º - CLÁUDIA LUCIA DE MORAES FORJAZ -**ID:** 14569 -**Área:** EDUCAÇÃO FÍSICA, FISIOTERAPIA, FONOAUDIOLOGIA E TERAPIA OCUPACIONAL -**Anos:** 14,5 | **Status:** Inativo -**Premiações:** 9 -**Coordenações:** 2 (1 ativa) - ---- - -#### 8º - ALTAIR ANTONINHA DEL BEL CURY -**ID:** 8639 -**Área:** ODONTOLOGIA -**Anos:** 12,1 | **Status:** Ativo ✓ -**Premiações:** 11 -**Eventos recentes:** 2 -**Coordenações:** 2 (1 ativa) - ---- - -#### 9º - DEBORA FOGUEL -**ID:** 12271 -**Área:** CIÊNCIAS BIOLÓGICAS II -**Anos:** 14,5 | **Status:** Inativo -**Premiações:** 7 -**Coordenações:** 2 (1 ativa) - ---- - -#### 10º - MARCELO TÁVORA MIRA -**ID:** 509665 -**Área:** MEDICINA I -**Anos:** 11,3 | **Status:** Ativo ✓ -**Premiações:** 7 -**Eventos recentes:** 1 -**Coordenações:** 2 (1 ativa) - ---- - -## Análise Estatística - -### Distribuição de Pontuação - -| Posição | Pontuação | Diferença para 1º | -|---------|-----------|-------------------| -| 1º | 685 | - | -| 2º | 675 | -10 | -| 3º | 665 | -20 | -| 4º-10º | 655 | -30 | - -**Média (top 10):** 661 pontos -**Desvio padrão:** ~10 pontos - -### Componentes - Análise - -**Componente A (Coordenação CAPES):** -- Média: 319 pts (71% do máximo) -- Variação: 310-340 pts -- Padrão dominante: CA (Coordenador de Área) com coordenação ativa - -**Componente B (Coordenação PPG):** -- Todos: 0 pts (dados não disponíveis no Elasticsearch) - -**Componente C (Consultoria):** -- Todos: 165 pts -- Padrão: Base 100 + Tempo 50 + Bônus continuidade 15 -- Uniformidade total no top 10 - -**Componente D (Premiações):** -- Todos: 180 pts (máximo permitido) -- Média de premiações: 10,3 por consultor -- Variação: 7-25 premiações - -### Perfil dos Líderes - -**Anos de Atuação:** -- Média: 12,7 anos -- Variação: 11,0 - 14,5 anos -- Todos são veteranos (10+ anos) - -**Status de Atividade:** -- Ativos: 3 (30%) -- Inativos: 7 (70%) - -**Áreas de Conhecimento:** -- Ciências Biológicas: 3 consultores -- Áreas diversas: 7 consultores - -**Padrão de Coordenação:** -- Todos possuem coordenação ATIVA no momento -- Todos exceto 1 tiveram retorno à coordenação -- Média de 2,3 coordenações por consultor - ---- - -## Observações Técnicas - -### Limitações da Análise - -1. **Componente B = 0:** O Elasticsearch não contém dados de coordenação de PPG. Esses dados estão no Oracle (SUCUPIRA_PAINEL). Se incluídos, o ranking poderia mudar significativamente. - -2. **Amostra de 100 candidatos:** A query com boost buscou apenas 100 candidatos pré-ordenados pelo Elasticsearch. É possível que candidatos com alta pontuação real estejam fora dessa amostra. - -3. **Dados de Consultoria incompletos:** O campo "áreas" da consultoria retornou vazio para todos os consultores, sugerindo possível problema na extração de dados do Elasticsearch. - -### Critérios de Desempate - -Quando há empate na pontuação total (casos 4º-10º), a ordenação segue a ordem de retorno do Elasticsearch, que não necessariamente reflete um critério definido. - -**Sugestão:** Implementar critério de desempate explícito: -1. Componente A (maior) -2. Anos de atuação (maior) -3. Status ativo (preferencial) -4. ID menor (mais antigo no sistema) - -### Validação dos Cálculos - -Os cálculos seguem rigorosamente os critérios definidos em: -- `/home/fred/projetos/ranking/.claude/rules/ranking-consultores-capes.md` -- `/home/fred/projetos/ranking/.claude/rules/ranking-queries-implementadas.md` - -**Implementação:** `CalculadorPontuacao` em `/home/fred/projetos/ranking/backend/src/domain/services/calculador_pontuacao.py` - ---- - -## Próximos Passos Recomendados - -1. **Integrar dados do Oracle** para calcular Componente B (Coordenação PPG) -2. **Expandir amostra** para 1.000+ candidatos para garantir cobertura completa -3. **Corrigir extração de áreas** na consultoria -4. **Implementar critério de desempate** explícito -5. **Validar dados de premiações** (verificar se todas as 7-25 premiações são válidas) - ---- - -**Script de Geração:** -`/home/fred/projetos/ranking/backend/scripts/top10_ranking.py` - -**Método de Consulta:** -Query Elasticsearch com boost por tipo de atuação (conforme especificação técnica) diff --git a/docs/ranking-queries-implementadas.md b/docs/ranking-queries-implementadas.md deleted file mode 100644 index b0425b1..0000000 --- a/docs/ranking-queries-implementadas.md +++ /dev/null @@ -1,441 +0,0 @@ -# Queries Implementadas - Sistema de Ranking de Consultores CAPES - -> Documentação das queries efetivamente implementadas no sistema, com regras de pontuação aplicadas. - -## Visão Geral da Arquitetura - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ FLUXO DE DADOS │ -├─────────────────────────────────────────────────────────────────────┤ -│ │ -│ 1. Elasticsearch (AtuaCAPES) │ -│ └─> Query com boost por tipo de atuação │ -│ └─> Retorna top 1000 candidatos pré-ordenados │ -│ │ -│ 2. Oracle - Opcional │ -│ └─> Busca coordenações de PPG por ID_PESSOA │ -│ │ -│ 3. Python (Backend) │ -│ └─> Calcula pontuação completa (A + B + C + D) │ -│ └─> Reordena por pontuação real │ -│ └─> Cache por 5 minutos │ -│ │ -└─────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## Query Principal - Elasticsearch - -### Objetivo -Buscar candidatos relevantes para o ranking, priorizando por tipo de atuação usando boost. - -### Query Implementada - -```json -{ - "size": 1000, - "query": { - "bool": { - "should": [ - { - "nested": { - "path": "atuacoes", - "query": { - "bool": { - "should": [ - {"term": {"atuacoes.tipo": {"value": "Coordenação de Área de Avaliação", "boost": 10}}}, - {"term": {"atuacoes.tipo": {"value": "Histórico de Coordenação de Área de Avaliação", "boost": 5}}} - ] - } - }, - "score_mode": "sum" - } - }, - { - "nested": { - "path": "atuacoes", - "query": { - "bool": { - "should": [ - {"term": {"atuacoes.tipo": {"value": "Consultor", "boost": 2}}}, - {"term": {"atuacoes.tipo": {"value": "Histórico de Consultoria", "boost": 1}}} - ] - } - }, - "score_mode": "sum" - } - }, - { - "nested": { - "path": "atuacoes", - "query": { - "bool": { - "should": [ - {"term": {"atuacoes.tipo": {"value": "Premiação Prêmio", "boost": 3}}}, - {"term": {"atuacoes.tipo": {"value": "Avaliação Prêmio", "boost": 2}}}, - {"term": {"atuacoes.tipo": {"value": "Inscrição Prêmio", "boost": 1}}} - ] - } - }, - "score_mode": "sum" - } - } - ], - "minimum_should_match": 1 - } - }, - "_source": ["id", "dadosPessoais", "atuacoes"], - "sort": [{"_score": "desc"}] -} -``` - -### Explicação dos Boosts - -| Tipo de Atuação | Boost | Justificativa | -|-----------------|-------|---------------| -| Coordenação de Área de Avaliação | 10 | Máxima pontuação (base 200 pts) | -| Histórico de Coordenação de Área | 5 | Alta pontuação histórica | -| Premiação Prêmio | 3 | 60 pts por premiação | -| Consultor | 2 | Base 150 pts se ativo | -| Avaliação Prêmio | 2 | 40 pts por avaliação | -| Histórico de Consultoria | 1 | Base 100 pts | -| Inscrição Prêmio | 1 | 20 pts por inscrição | - -### Estatísticas de Candidatos - -| Tipo | Quantidade no ES | -|------|------------------| -| Coordenadores de área | 522 | -| Consultores | 52.551 | -| Premiações | 63.799 | -| **Total com atuações relevantes** | **90.482** | - ---- - -## Query Oracle - Coordenação de Programa (PPG) - -### Objetivo -Buscar coordenações de programa por ID_PESSOA para calcular o Componente B. - -### Query Implementada - -```sql -SELECT - c.ID_PESSOA, - c.ID_PROGRAMA_SNPG, - p.NM_PROGRAMA, - p.CD_PROGRAMA_PPG, - p.DS_CONCEITO AS NOTA_PPG, - p.NM_PROGRAMA_MODALIDADE, - aa.NM_AREA_AVALIACAO, - c.DT_INICIO_VIGENCIA, - c.DT_FIM_VIGENCIA -FROM SUCUPIRA_PAINEL.VM_COORDENADOR c -INNER JOIN SUCUPIRA_PAINEL.VM_PROGRAMA_SUCUPIRA p - ON c.ID_PROGRAMA_SNPG = p.ID_PROGRAMA -LEFT JOIN SUCUPIRA_PAINEL.VM_AREA_CONHECIMENTO ac - ON p.ID_AREA_CONHECIMENTO_ATUAL = ac.ID_AREA_CONHECIMENTO -LEFT JOIN SUCUPIRA_PAINEL.VM_AREA_AVALIACAO aa - ON ac.ID_AREA_AVALIACAO = aa.ID_AREA_AVALIACAO -WHERE c.ID_PESSOA = :id_pessoa -ORDER BY c.DT_INICIO_VIGENCIA -``` - -### Campos Retornados - -| Campo | Tipo | Uso | -|-------|------|-----| -| ID_PROGRAMA_SNPG | NUMBER | Identificador único do programa | -| NM_PROGRAMA | VARCHAR2 | Nome do programa | -| CD_PROGRAMA_PPG | VARCHAR2 | Código do PPG | -| NOTA_PPG | VARCHAR2 | Nota CAPES (3-7, A) | -| NM_PROGRAMA_MODALIDADE | VARCHAR2 | Acadêmico/Profissional | -| NM_AREA_AVALIACAO | VARCHAR2 | Área de avaliação | -| DT_INICIO_VIGENCIA | DATE | Início da coordenação | -| DT_FIM_VIGENCIA | DATE | Fim (null = ativo) | - ---- - -## Regras de Pontuação Implementadas - -### Componente A - Coordenação CAPES (máx 450 pts) - -**Arquivo**: `backend/src/domain/services/calculador_pontuacao.py` - -```python -# Apenas coordenações ATIVAS pontuam -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) - -# Valores por tipo -base_map = {"CA": 200, "CAJ": 150, "CAJ-MP": 120, "CAM": 100} -tempo_max_map = {"CA": 100, "CAJ": 80, "CAJ-MP": 60, "CAM": 50} -bonus_atual_map = {"CA": 30, "CAJ": 20, "CAJ-MP": 15, "CAM": 10} - -# Cálculo -base = base_map.get(coord_atual.tipo, 0) -anos = coord_atual.periodo.anos_decorridos -tempo = min(int(anos * 10), tempo_max_map.get(coord_atual.tipo, 0)) -extras = min(len(coord_atual.areas_adicionais) * 20, 100) -bonus = bonus_atual_map.get(coord_atual.tipo, 0) if coord_atual.periodo.ativo else 0 -retorno = 20 if coord_atual.ja_coordenou_antes else 0 -``` - -**Tabela de Pontuação**: - -| Tipo | Base | Tempo (máx) | Bônus Ativo | Áreas (máx) | Retorno | -|------|------|-------------|-------------|-------------|---------| -| CA | 200 | 100 (10 pts/ano) | 30 | 100 | 20 | -| CAJ | 150 | 80 (10 pts/ano) | 20 | 100 | 20 | -| CAJ-MP | 120 | 60 (10 pts/ano) | 15 | 100 | 20 | -| CAM | 100 | 50 (10 pts/ano) | 10 | 100 | 20 | - -### Componente B - Coordenação PPG (máx 180 pts) - -```python -if not coordenacoes: - return ComponentePontuacao(base=0, tempo=0, extras=0, bonus=0) - -base = 70 -anos_totais = sum(c.periodo.anos_decorridos for c in coordenacoes) -tempo = min(int(anos_totais * 5), 50) - -programas_distintos = len({c.id_programa for c in coordenacoes}) -extras = min((programas_distintos - 1) * 20, 40) - -coord_ativa = any(c.periodo.ativo for c in coordenacoes) -bonus = 20 if coord_ativa else 0 -``` - -**Tabela de Pontuação**: - -| Critério | Pontos | Máximo | -|----------|--------|--------| -| Base (ser coordenador) | 70 | 70 | -| Tempo | 5 pts/ano | 50 | -| Programas extras | 20 pts/programa | 40 | -| Bônus ativo | 20 | 20 | -| **TOTAL** | - | **180** | - -### Componente C - Consultoria (máx 230 pts) - -```python -if not consultoria: - return ComponentePontuacao(base=0, tempo=0, extras=0, bonus=0) - -# Base depende de ter eventos recentes (últimos 2 anos) -base = 150 if consultoria.eventos_recentes > 0 else 100 - -# Tempo desde primeiro evento -anos = (datetime.now() - consultoria.primeiro_evento).days / 365.25 -tempo = min(int(anos * 5), 50) - -# Extras -extras_eventos = min(consultoria.total_eventos * 2, 20) -extras_responsavel = min(consultoria.vezes_responsavel * 5, 25) -extras_areas = min((len(consultoria.areas) - 1) * 10, 30) if len(consultoria.areas) > 1 else 0 -extras = extras_eventos + extras_responsavel + extras_areas -``` - -**Tabela de Pontuação**: - -| Critério | Pontos | Máximo | -|----------|--------|--------| -| Base (ativo/recente) | 150 | 150 | -| Base (histórico) | 100 | 100 | -| Tempo | 5 pts/ano | 50 | -| Eventos | 2 pts/evento | 20 | -| Responsável | 5 pts/vez | 25 | -| Áreas extras | 10 pts/área | 30 | -| **TOTAL** | - | **230** | - -### Componente D - Premiações (máx 180 pts) - -```python -if not premiacoes: - return ComponentePontuacao(base=0, tempo=0, extras=0, bonus=0) - -# Pontos por tipo de premiação -mapa = { - "Premiação Prêmio": 60, - "Avaliação Prêmio": 40, - "Inscrição Prêmio": 20, -} - -total_pontos = sum(mapa.get(p.tipo, 0) for p in premiacoes) -total_pontos = min(total_pontos, 180) # Teto - -return ComponentePontuacao(base=total_pontos, tempo=0, extras=0, bonus=0) -``` - -**Tabela de Pontuação**: - -| Tipo | Pontos | Descrição | -|------|--------|-----------| -| Premiação Prêmio | 60 | Recebeu prêmio | -| Avaliação Prêmio | 40 | Participou de banca/comissão | -| Inscrição Prêmio | 20 | Inscreveu trabalho | -| **Máximo total** | **180** | - | - ---- - -## Extração de Dados do Elasticsearch - -### Coordenações CAPES - -```python -def _extrair_coordenacoes_capes(self, atuacoes: List[Dict[str, Any]]) -> List[CoordenacaoCapes]: - coordenacoes = [ - a for a in atuacoes - if a.get("tipo") in [ - "Coordenação de Área de Avaliação", - "Histórico de Coordenação de Área de Avaliação", - ] - ] - - # Inferir tipo pelo campo 'nome' - def _inferir_tipo_coordenacao(coord): - nome = coord.get("nome", "").lower() - if "câmara" in nome or "camara" in nome: - return "CAM" - elif "mestrado profissional" in nome: - return "CAJ-MP" - elif "adjunta" in nome: - return "CAJ" - else: - return "CA" -``` - -### Consultoria - -```python -def _extrair_consultoria(self, atuacoes: List[Dict[str, Any]]) -> Optional[Consultoria]: - consultorias = [ - a for a in atuacoes - if a.get("tipo") in ["Consultor", "Histórico de Consultoria"] - ] - - # Calcula eventos recentes (últimos 2 anos) - limite_recente = datetime.now() - timedelta(days=730) - eventos_recentes = sum(1 for d in datas_fim if d >= limite_recente) - - # Extrai áreas únicas - areas = list({c.get("areaAvaliacao", "N/A") for c in consultorias if c.get("areaAvaliacao")}) - - # Conta vezes como responsável - vezes_responsavel = sum(1 for c in consultorias if c.get("responsavel", False)) -``` - -### Premiações - -```python -def _extrair_premiacoes(self, atuacoes: List[Dict[str, Any]]) -> List[Premiacao]: - premiacoes_data = [ - a for a in atuacoes - if a.get("tipo") in [ - "Premiação Prêmio", - "Avaliação Prêmio", - "Inscrição Prêmio", - ] - ] - - for prem in premiacoes_data: - pontos = self._calcular_pontos_premiacao(prem.get("tipo", "")) - # tipo vem de atuacoes.tipo - # nome_premio vem de atuacoes.descricao - # ano vem de atuacoes.inicio (parse para extrair ano) -``` - ---- - -## Cache de Ranking - -### Implementação - -```python -class RankingCache: - def __init__(self, ttl_seconds: int = 300): # 5 minutos - 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 -``` - -### Performance - -| Métrica | Valor | -|---------|-------| -| Primeira requisição (cold) | ~1m34s | -| Requisições cacheadas | ~0.27s | -| TTL do cache | 5 minutos | -| Candidatos processados | 1000 | - ---- - -## Fluxo Completo de Ranking - -```python -async def buscar_ranking(self, limite: int = 100) -> List[Consultor]: - # 1. Verificar cache - if _ranking_cache.is_valid(): - return _ranking_cache.get()[:limite] - - async with _ranking_cache._lock: - # 2. Double-check cache - if _ranking_cache.is_valid(): - return _ranking_cache.get()[:limite] - - # 3. Buscar candidatos do ES (ordenados por score ES) - tamanho_busca = max(limite * 3, 1000) - docs = await self.es_client.buscar_candidatos_ranking(size=tamanho_busca) - - # 4. Construir consultores (calcula pontuação completa) - consultores = [] - for doc in docs: - consultor = await self._construir_consultor(doc) - # _construir_consultor busca dados do Oracle se disponível - consultores.append(consultor) - - # 5. Reordenar por pontuação real - consultores_ordenados = sorted( - consultores, key=lambda c: c.pontuacao_total, reverse=True - ) - - # 6. Cachear resultado - _ranking_cache.set(consultores_ordenados) - - return consultores_ordenados[:limite] -``` - ---- - -## Limitações Conhecidas - -1. **Cobertura de candidatos**: Buscamos 1000 candidatos do ES. Se alguém com alta pontuação estiver fora do top 1000 do score ES, não aparecerá. - -2. **Oracle opcional**: Se Oracle não estiver disponível (TNS error no Docker), Componente B = 0 para todos. - -3. **Tipos de coordenação**: Inferência pelo campo `nome` pode não ser 100% precisa. - -4. **Datas inconsistentes**: ES usa formatos `dd/MM/yyyy` e `yyyy-MM-dd` misturados. - ---- - -## Arquivos de Implementação - -| Arquivo | Responsabilidade | -|---------|------------------| -| `backend/src/infrastructure/elasticsearch/client.py` | Query ES com boost | -| `backend/src/infrastructure/oracle/client.py` | Query Oracle PPG | -| `backend/src/infrastructure/repositories/consultor_repository_impl.py` | Cache e orquestração | -| `backend/src/domain/services/calculador_pontuacao.py` | Regras de pontuação | -| `backend/src/domain/entities/consultor.py` | Entidades de domínio |