From c78f48c988c9895158e360d6e725abd3e0319001 Mon Sep 17 00:00:00 2001 From: Frederico Castro Date: Sat, 27 Dec 2025 01:06:43 -0300 Subject: [PATCH] fix(lattes): correcoes finais na integracao Lattes --- backend/src/interface/api/routes.py | 142 ++++++- frontend/src/components/ConsultorCard.css | 31 ++ frontend/src/components/ConsultorCard.jsx | 484 +++++++++++++++++++++- 3 files changed, 632 insertions(+), 25 deletions(-) diff --git a/backend/src/interface/api/routes.py b/backend/src/interface/api/routes.py index 37bfa01..4f4488c 100644 --- a/backend/src/interface/api/routes.py +++ b/backend/src/interface/api/routes.py @@ -236,7 +236,17 @@ async def ranking_paginado( if not d.get("idiomas"): faltando_idiomas.append((c.id_pessoa, d)) - faltando_lattes = [(c.id_pessoa, d) for c, d in consultores_dados if not d.get("lattes")] + def lattes_incompleto(d): + lattes = d.get("lattes") + if not lattes: + return True + titulacoes = lattes.get("titulacoes", []) + if not titulacoes: + return False + primeira = titulacoes[0] if titulacoes else {} + return "programa" not in primeira or "area_avaliacao" not in primeira + + faltando_lattes = [(c.id_pessoa, d) for c, d in consultores_dados if lattes_incompleto(d)] ids_buscar = list(set([item[0] for item in faltando_idiomas] + [item[0] for item in faltando_lattes])) if ids_buscar: @@ -279,14 +289,26 @@ async def ranking_paginado( for t in titulacoes_raw: grau_obj = t.get("grauAcademico", {}) ies_obj = t.get("ies", {}) + area_obj = t.get("areaConhecimento", {}) + programa_obj = t.get("programa", {}) titulacoes_formatadas.append({ "grau": grau_obj.get("nome", ""), + "hierarquia": grau_obj.get("hierarquia"), "ano": t.get("ano"), + "inicio": t.get("inicio"), + "fim": t.get("fim"), "ies_nome": ies_obj.get("nome"), "ies_sigla": ies_obj.get("sigla"), - "area": t.get("areaConhecimento", {}).get("nome"), - "pais": "Brasil", + "ies_status": ies_obj.get("statusJuridico"), + "area": area_obj.get("nome"), + "area_avaliacao": area_obj.get("areaAvaliacao", {}).get("nome") if area_obj.get("areaAvaliacao") else None, + "programa": programa_obj.get("nome") if programa_obj else None, + "codigo_programa": programa_obj.get("codigo") if programa_obj else None, + "programa_modalidade": programa_obj.get("modalidade") if programa_obj else None, + "programa_situacao": programa_obj.get("situacao") if programa_obj else None, + "pais": t.get("pais") or "Brasil", }) + titulacoes_formatadas.sort(key=lambda x: (x.get("hierarquia") or 99, -(x.get("ano") or 0))) detalhes["lattes"] = { "id_lattes": id_lattes, "url": f"http://lattes.cnpq.br/{id_lattes}", @@ -514,6 +536,9 @@ async def obter_lattes( "area_avaliacao": area_obj.get("areaAvaliacao", {}).get("nome") if area_obj.get("areaAvaliacao") else None, "programa": programa_obj.get("nome") if programa_obj else None, "codigo_programa": programa_obj.get("codigo") if programa_obj else None, + "programa_modalidade": programa_obj.get("modalidade") if programa_obj else None, + "programa_situacao": programa_obj.get("situacao") if programa_obj else None, + "pais": t.get("pais") or "Brasil", }) titulacoes.sort(key=lambda x: (x.get("hierarquia") or 99, -(x.get("ano") or 0))) @@ -562,6 +587,111 @@ async def obter_lattes( "ies": dados.get("ies", {}).get("sigla") if dados.get("ies") else None, }) + docencias = [] + for a in atuacoes_raw: + if a.get("tipo") == "Docência": + dados = a.get("dadosDocencia", {}) + if dados: + programa_obj = dados.get("programa", {}) + ies_obj = dados.get("ies", {}) + area_obj = dados.get("areaConhecimento", {}) + linhas = dados.get("linhaPesquisa", []) + linhas_ativas = [l.get("nome") for l in linhas if l.get("fim") is None][:5] + docencias.append({ + "programa": programa_obj.get("nome") if programa_obj else None, + "codigo_programa": programa_obj.get("codigo") if programa_obj else None, + "modalidade": programa_obj.get("modalidade") if programa_obj else None, + "situacao_programa": programa_obj.get("situacao") if programa_obj else None, + "ies_nome": ies_obj.get("nome") if ies_obj else None, + "ies_sigla": ies_obj.get("sigla") if ies_obj else None, + "area": area_obj.get("nome") if area_obj else None, + "area_avaliacao": area_obj.get("areaAvaliacao", {}).get("nome") if area_obj.get("areaAvaliacao") else None, + "categoria": dados.get("categoria"), + "tipo_vinculo": dados.get("tipoVinculo"), + "regime_trabalho": dados.get("regimeTrabalho"), + "carga_horaria": dados.get("cargaHoraria"), + "linhas_pesquisa_ativas": linhas_ativas, + "total_linhas_pesquisa": len(linhas), + }) + + empregos = [] + for a in atuacoes_raw: + if a.get("tipo") == "Emprego": + dados = a.get("dadosEmprego", {}) + if dados: + historico = dados.get("historico", []) + periodos = [] + for h in historico: + periodos.append({ + "inicio": h.get("inicioRelacionamento"), + "fim": h.get("fimRelacionamento"), + }) + empregos.append({ + "empregador": dados.get("nomeEmpregador"), + "cnpj": dados.get("cnpjEmpregador"), + "tipo_emprego": dados.get("emprego"), + "atividade": dados.get("atividade"), + "vinculo": dados.get("vinculo"), + "profissao": dados.get("profissao"), + "periodos": periodos, + }) + + projetos = [] + for a in atuacoes_raw: + if a.get("tipo") == "Projeto": + dados = a.get("dadosProjeto", {}) + if dados and dados.get("nome"): + programa_obj = dados.get("programa", {}) + ies_obj = dados.get("ies", {}) + area_obj = dados.get("areaConhecimento", {}) + projetos.append({ + "nome": dados.get("nome"), + "situacao": dados.get("situacao"), + "ano_inicio": dados.get("anoInicio"), + "linha_pesquisa": dados.get("linhaPesquisa"), + "programa": programa_obj.get("nome") if programa_obj else None, + "ies_sigla": ies_obj.get("sigla") if ies_obj else None, + "area": area_obj.get("nome") if area_obj else None, + }) + + premiacoes_detalhadas = [] + for a in atuacoes_raw: + if a.get("tipo") == "Premiação Prêmio": + dados = a.get("dadosPremiacaoPremio", {}) + if dados: + produto = dados.get("produto", {}) + ies_obj = dados.get("ies", {}) + programa_obj = dados.get("programa", {}) + area_obj = dados.get("areaConhecimento", {}) + premiacoes_detalhadas.append({ + "premio": dados.get("premio"), + "evento": dados.get("evento"), + "premiacao": dados.get("premiacao"), + "ano": dados.get("ano"), + "papel": dados.get("papelPessoa"), + "situacao": dados.get("situacao"), + "produto_nome": produto.get("nome") if produto else None, + "produto_tipo": produto.get("tipoProduto", {}).get("nome") if produto.get("tipoProduto") else None, + "produto_autor": produto.get("autor") if produto else None, + "ies_sigla": ies_obj.get("sigla") if ies_obj else None, + "programa": programa_obj.get("nome") if programa_obj else None, + "area": area_obj.get("nome") if area_obj else None, + }) + + estatisticas_orientacoes = None + for a in atuacoes_raw: + if a.get("tipo") == "Orientação de Discentes": + dados = a.get("dadosOrientacaoDiscente", {}) + if dados: + estatisticas_orientacoes = { + "mestrado_finalizado": dados.get("totalOrientacaoFinalizadaMestrado"), + "doutorado_finalizado": dados.get("totalOrientacaoFinalizadaDoutorado"), + "mestrado_andamento": dados.get("totalOrientacaoAndamentoMestrado"), + "doutorado_andamento": dados.get("totalOrientacaoAndamentoDoutorado"), + "pos_doutorado": dados.get("totalAcompanhamentoPosDoutorado"), + } + break + return { "encontrado": True, "id_lattes": id_lattes, @@ -575,6 +705,12 @@ async def obter_lattes( "endereco_profissional": endereco_profissional, "orientacoes_concluidas": orientacoes_concluidas[:20], "total_orientacoes": len(orientacoes_concluidas), + "estatisticas_orientacoes": estatisticas_orientacoes, + "docencias": docencias, + "empregos": empregos, + "projetos": projetos[:30], + "total_projetos": len(projetos), + "premiacoes_detalhadas": premiacoes_detalhadas, "data_atualizacao_lattes": None, } diff --git a/frontend/src/components/ConsultorCard.css b/frontend/src/components/ConsultorCard.css index aea0ef6..00aaa83 100644 --- a/frontend/src/components/ConsultorCard.css +++ b/frontend/src/components/ConsultorCard.css @@ -1607,3 +1607,34 @@ opacity: 1; transform: translateY(-50%) translateX(3px); } + +.modal-stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + gap: 0.75rem; + margin-bottom: 1rem; +} + +.modal-stat-card { + background: rgba(99, 102, 241, 0.1); + border: 1px solid rgba(99, 102, 241, 0.2); + border-radius: 8px; + padding: 0.75rem; + text-align: center; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.modal-stat-card .stat-number { + font-size: 1.5rem; + font-weight: 700; + color: var(--accent); +} + +.modal-stat-card .stat-label { + font-size: 0.7rem; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.03em; +} diff --git a/frontend/src/components/ConsultorCard.jsx b/frontend/src/components/ConsultorCard.jsx index a2f086b..7d5db6b 100644 --- a/frontend/src/components/ConsultorCard.jsx +++ b/frontend/src/components/ConsultorCard.jsx @@ -719,6 +719,10 @@ const ItemDetalheModal = ({ item, tipo, onClose }) => { case 'orientacao': return 'Orientação'; case 'orientacao_lattes': return 'Orientação Concluída'; case 'idioma': return 'Idioma'; + case 'docencia': return 'Docência em PPG'; + case 'emprego': return 'Vínculo Empregatício'; + case 'projeto': return 'Projeto de Pesquisa'; + case 'premiacao_lattes': return 'Premiação'; default: return 'Detalhes'; } }; @@ -736,6 +740,10 @@ const ItemDetalheModal = ({ item, tipo, onClose }) => { case 'orientacao': return '🎓'; case 'orientacao_lattes': return '👨‍🏫'; case 'idioma': return '🌐'; + case 'docencia': return '👨‍🏫'; + case 'emprego': return '🏢'; + case 'projeto': return '📊'; + case 'premiacao_lattes': return '🏆'; default: return '📄'; } }; @@ -792,6 +800,18 @@ const ItemDetalheModal = ({ item, tipo, onClose }) => { {it.codigo_programa} )} + {it.programa_modalidade && ( +
+ Modalidade + {it.programa_modalidade} +
+ )} + {it.programa_situacao && ( +
+ Situação Programa + {it.programa_situacao} +
+ )} {it.pais && (
País @@ -900,6 +920,269 @@ const ItemDetalheModal = ({ item, tipo, onClose }) => { ); } + case 'docencia': { + const d = currentItem; + return ( +
+
+ Programa + {d.programa || 'N/A'} +
+ {d.codigo_programa && ( +
+ Código + {d.codigo_programa} +
+ )} +
+ Instituição + {d.ies_nome} ({d.ies_sigla}) +
+ {d.modalidade && ( +
+ Modalidade + {d.modalidade} +
+ )} + {d.situacao_programa && ( +
+ Situação + {d.situacao_programa} +
+ )} + {d.area && ( +
+ Área + {d.area} +
+ )} + {d.area_avaliacao && ( +
+ Área de Avaliação + {d.area_avaliacao} +
+ )} + {d.categoria && ( +
+ Categoria + {d.categoria} +
+ )} + {d.tipo_vinculo && ( +
+ Tipo de Vínculo + {d.tipo_vinculo} +
+ )} + {d.regime_trabalho && ( +
+ Regime de Trabalho + {d.regime_trabalho} +
+ )} + {d.carga_horaria && ( +
+ Carga Horária + {d.carga_horaria}h +
+ )} + {d.linhas_pesquisa_ativas?.length > 0 && ( + <> +
Linhas de Pesquisa Ativas ({d.linhas_pesquisa_ativas.length} de {d.total_linhas_pesquisa})
+
    + {d.linhas_pesquisa_ativas.map((linha, idx) => ( +
  • + {linha} +
  • + ))} +
+ + )} +
+ ); + } + + case 'emprego': { + const e = currentItem; + return ( +
+
+ Empregador + {e.empregador || 'N/A'} +
+ {e.cnpj && ( +
+ CNPJ + {e.cnpj} +
+ )} + {e.tipo_emprego && ( +
+ Tipo + {e.tipo_emprego} +
+ )} + {e.atividade && ( +
+ Atividade + {e.atividade} +
+ )} + {e.vinculo && ( +
+ Vínculo + {e.vinculo} +
+ )} + {e.profissao && ( +
+ Profissão + {e.profissao} +
+ )} + {e.periodos?.length > 0 && ( + <> +
Períodos ({e.periodos.length})
+
    + {e.periodos.map((p, idx) => ( +
  • + + {p.inicio ? formatDate(p.inicio) : 'Sem data'} a {p.fim ? formatDate(p.fim) : 'Atual'} + +
  • + ))} +
+ + )} +
+ ); + } + + case 'projeto': { + const p = currentItem; + return ( +
+
+ Nome + {p.nome || 'N/A'} +
+ {p.situacao && ( +
+ Situação + + {p.situacao} + +
+ )} + {p.ano_inicio && ( +
+ Ano de Início + {p.ano_inicio} +
+ )} + {p.ies_sigla && ( +
+ Instituição + {p.ies_sigla} +
+ )} + {p.programa && ( +
+ Programa + {p.programa} +
+ )} + {p.area && ( +
+ Área + {p.area} +
+ )} + {p.linha_pesquisa && ( +
+ Linha de Pesquisa + {p.linha_pesquisa} +
+ )} +
+ ); + } + + case 'premiacao_lattes': { + const pr = currentItem; + return ( +
+
+ Evento + {pr.evento || pr.premio || 'N/A'} +
+ {pr.premiacao && ( +
+ Tipo + {pr.premiacao} +
+ )} + {pr.ano && ( +
+ Ano + {pr.ano} +
+ )} + {pr.papel && ( +
+ Papel + {pr.papel} +
+ )} + {pr.situacao && ( +
+ Situação + {pr.situacao} +
+ )} + {pr.ies_sigla && ( +
+ Instituição + {pr.ies_sigla} +
+ )} + {pr.programa && ( +
+ Programa + {pr.programa} +
+ )} + {pr.area && ( +
+ Área + {pr.area} +
+ )} + {pr.produto_nome && ( + <> +
Produto
+
+ Título + {pr.produto_nome} +
+ {pr.produto_tipo && ( +
+ Tipo + {pr.produto_tipo} +
+ )} + {pr.produto_autor && ( +
+ Autor + {pr.produto_autor} +
+ )} + + )} +
+ ); + } + case 'producoes_lattes': { const titulacoes = item.titulacoes || []; const idiomas = item.idiomas || []; @@ -1013,6 +1296,162 @@ const ItemDetalheModal = ({ item, tipo, onClose }) => { )} + {item.estatisticas_orientacoes && ( + <> +
Estatísticas de Orientações
+
+ {item.estatisticas_orientacoes.doutorado_finalizado > 0 && ( +
+ {item.estatisticas_orientacoes.doutorado_finalizado} + Doutorados Concluídos +
+ )} + {item.estatisticas_orientacoes.mestrado_finalizado > 0 && ( +
+ {item.estatisticas_orientacoes.mestrado_finalizado} + Mestrados Concluídos +
+ )} + {item.estatisticas_orientacoes.doutorado_andamento > 0 && ( +
+ {item.estatisticas_orientacoes.doutorado_andamento} + Doutorados em Andamento +
+ )} + {item.estatisticas_orientacoes.mestrado_andamento > 0 && ( +
+ {item.estatisticas_orientacoes.mestrado_andamento} + Mestrados em Andamento +
+ )} + {item.estatisticas_orientacoes.pos_doutorado > 0 && ( +
+ {item.estatisticas_orientacoes.pos_doutorado} + Pós-Doutorados +
+ )} +
+ + )} + + {item.docencias?.length > 0 && ( + <> +
Docência em PPG ({item.docencias.length})
+ + + )} + + {item.empregos?.length > 0 && ( + <> +
Vínculos Empregatícios ({item.empregos.length})
+ + + )} + + {item.projetos?.length > 0 && ( + <> +
+ Projetos de Pesquisa ({item.total_projetos}) +
+ + + )} + + {item.premiacoes_detalhadas?.length > 0 && ( + <> +
Premiações Detalhadas ({item.premiacoes_detalhadas.length})
+ + + )} + {endereco && ( <>
Endereço Profissional
@@ -1030,7 +1469,8 @@ const ItemDetalheModal = ({ item, tipo, onClose }) => { )} - {titulacoes.length === 0 && idiomas.length === 0 && orientacoes.length === 0 && ( + {titulacoes.length === 0 && idiomas.length === 0 && orientacoes.length === 0 && + !item.docencias?.length && !item.projetos?.length && !item.premiacoes_detalhadas?.length && (

Dados detalhados não disponíveis no ATUACAPES.

)}
@@ -1953,27 +2393,6 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
- {consultor.lattes.titulacoes - ?.sort((a, b) => { - const ordem = { 'Pos-Doutorado': 1, 'Doutorado': 2, 'Mestrado': 3, 'Graduacao': 4 }; - return (ordem[a.grau] || 99) - (ordem[b.grau] || 99); - }) - .map((t, idx) => ( - { - e.stopPropagation(); - setItemDetalhe({ - item: t, - tipo: 'titulacao' - }); - }} - title={`${t.grau}${t.area ? ` em ${t.area}` : ''}${t.ies_nome ? ` - ${t.ies_nome}` : ''}${t.pais ? ` (${t.pais})` : ''}`} - > - {t.grau}{t.ies_sigla ? ` (${t.ies_sigla})` : ''}{t.ano ? ` - ${t.ano}` : ''} - - ))} 📚 Producoes + {consultor.lattes.titulacoes + ?.sort((a, b) => { + const ordem = { 'Pos-Doutorado': 1, 'Doutorado': 2, 'Mestrado': 3, 'Graduacao': 4, 'Bacharelado': 5 }; + return (ordem[a.grau] || 99) - (ordem[b.grau] || 99); + }) + .map((t, idx) => ( + { + e.stopPropagation(); + setItemDetalhe({ + item: t, + tipo: 'titulacao' + }); + }} + title={`Clique para detalhes: ${t.grau}${t.area ? ` em ${t.area}` : ''}${t.programa ? ` (${t.programa})` : ''}${t.ies_nome ? ` - ${t.ies_nome}` : ''}`} + > + {t.grau}{t.ies_sigla ? ` (${t.ies_sigla})` : ''}{t.ano ? ` - ${t.ano}` : ''} + + ))}