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 && ( +
Dados detalhados não disponíveis no ATUACAPES.
)}