From e8b3868d2806233faeb542125865800b495408e0 Mon Sep 17 00:00:00 2001 From: Frederico Castro Date: Fri, 26 Dec 2025 23:45:08 -0300 Subject: [PATCH] feat(lattes): melhorias na integracao Lattes e estilos da secao - Aprimorar extracao de dados Lattes no backend - Melhorar estilos CSS da secao Lattes - Ajustes no componente ConsultorCard para exibicao do Lattes --- backend/src/interface/api/routes.py | 72 ++++++++++- frontend/src/components/ConsultorCard.css | 41 ++++++- frontend/src/components/ConsultorCard.jsx | 143 +++++++++++++++++----- 3 files changed, 217 insertions(+), 39 deletions(-) diff --git a/backend/src/interface/api/routes.py b/backend/src/interface/api/routes.py index 59557cc..37bfa01 100644 --- a/backend/src/interface/api/routes.py +++ b/backend/src/interface/api/routes.py @@ -473,7 +473,10 @@ async def obter_lattes( ): docs = await es_client.buscar_por_ids( [id_pessoa], - source_fields=["id", "identificadorLattes", "titulacoes"], + source_fields=[ + "id", "dadosPessoais", "identificadorLattes", "titulacoes", + "idiomas", "areasConhecimento", "enderecos", "atuacoes" + ], ) if not docs: return {"encontrado": False, "motivo": "Consultor não encontrado"} @@ -485,31 +488,94 @@ async def obter_lattes( return {"encontrado": False, "motivo": "Currículo Lattes não cadastrado"} id_lattes = id_lattes_obj.get("descricao") + dados_pessoais = doc.get("dadosPessoais", {}) titulacoes_raw = doc.get("titulacoes", []) + idiomas_raw = doc.get("idiomas", []) + areas_raw = doc.get("areasConhecimento", []) + enderecos_raw = doc.get("enderecos", []) + atuacoes_raw = doc.get("atuacoes", []) titulacoes = [] 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.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"), + "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": t.get("programa", {}).get("nome") if t.get("programa") else None, + "programa": programa_obj.get("nome") if programa_obj else None, + "codigo_programa": programa_obj.get("codigo") if programa_obj else None, }) + titulacoes.sort(key=lambda x: (x.get("hierarquia") or 99, -(x.get("ano") or 0))) + + idiomas = [] + for i in idiomas_raw: + idiomas.append({ + "idioma": i.get("idioma"), + "proficiencia_leitura": i.get("proficienciaLeitura"), + "proficiencia_escrita": i.get("proficienciaEscrita"), + "proficiencia_fala": i.get("proficienciaFala"), + "proficiencia_compreensao": i.get("proficienciaCompreensao"), + }) + + areas_conhecimento = [] + for a in areas_raw: + areas_conhecimento.append({ + "nome": a.get("nome"), + "area_avaliacao": a.get("areaAvaliacao", {}).get("nome") if a.get("areaAvaliacao") else None, + }) + + endereco_profissional = None + for e in enderecos_raw: + if e.get("tipo") == "Profissional" or e.get("principalFinalidade") == "Sim": + endereco_profissional = { + "logradouro": e.get("endereco"), + "numero": e.get("numero"), + "complemento": e.get("complemento"), + "bairro": e.get("bairro"), + "cep": e.get("cep"), + "cidade": e.get("cidadeExterior") or e.get("cidade"), + "pais": e.get("pais"), + } + break + + orientacoes_concluidas = [] + for a in atuacoes_raw: + tipo = a.get("tipo", "") + if "Orientação" in tipo and "Concluída" in tipo: + dados = a.get("dadosOrientacao", {}) + orientacoes_concluidas.append({ + "tipo": tipo, + "titulo": dados.get("titulo"), + "ano": dados.get("ano"), + "orientando": dados.get("orientando", {}).get("nome") if dados.get("orientando") else None, + "programa": dados.get("programa", {}).get("nome") if dados.get("programa") else None, + "ies": dados.get("ies", {}).get("sigla") if dados.get("ies") else None, + }) return { "encontrado": True, "id_lattes": id_lattes, "url": f"http://lattes.cnpq.br/{id_lattes}", + "nome": dados_pessoais.get("nome"), + "data_nascimento": dados_pessoais.get("nascimento"), + "nacionalidade": dados_pessoais.get("nacionalidade"), "titulacoes": titulacoes, - "orientacoes_lattes": len([t for t in titulacoes if t.get("grau") in ["Doutorado", "Mestrado"]]), + "idiomas": idiomas, + "areas_conhecimento": areas_conhecimento, + "endereco_profissional": endereco_profissional, + "orientacoes_concluidas": orientacoes_concluidas[:20], + "total_orientacoes": len(orientacoes_concluidas), + "data_atualizacao_lattes": None, } diff --git a/frontend/src/components/ConsultorCard.css b/frontend/src/components/ConsultorCard.css index cf58ffa..5be68aa 100644 --- a/frontend/src/components/ConsultorCard.css +++ b/frontend/src/components/ConsultorCard.css @@ -1335,8 +1335,9 @@ .modal-item { display: flex; - align-items: center; - gap: 0.75rem; + flex-direction: column; + align-items: flex-start; + gap: 0.2rem; padding: 0.6rem 0.8rem; background: rgba(255, 255, 255, 0.03); border-radius: 8px; @@ -1355,6 +1356,42 @@ font-weight: 500; } +.modal-item-detail { + color: var(--muted); + font-size: 0.8rem; +} + +.modal-item-sub { + display: block; + width: 100%; + margin-top: 0.25rem; + font-size: 0.75rem; + line-height: 1.3; +} + +.modal-section-title { + color: var(--accent-2); + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.5px; + margin: 1.25rem 0 0.5rem; + padding-bottom: 0.25rem; + border-bottom: 1px solid var(--stroke); +} + +.modal-section-title:first-of-type { + margin-top: 0.75rem; +} + +.lattes-link-inline { + color: var(--accent) !important; + text-decoration: none; +} + +.lattes-link-inline:hover { + text-decoration: underline; +} + .modal-summary { display: flex; gap: 1.5rem; diff --git a/frontend/src/components/ConsultorCard.jsx b/frontend/src/components/ConsultorCard.jsx index 0047bab..0503be9 100644 --- a/frontend/src/components/ConsultorCard.jsx +++ b/frontend/src/components/ConsultorCard.jsx @@ -765,47 +765,122 @@ const ItemDetalheModal = ({ item, tipo, onClose }) => { } case 'producoes_lattes': { - const producoes = item.producoes || item.producoes_recentes || []; + const titulacoes = item.titulacoes || []; + const idiomas = item.idiomas || []; + const orientacoes = item.orientacoes_concluidas || []; + const endereco = item.endereco_profissional; return (
- Total - {item.total_producoes ?? 0} + ID Lattes + + {item.id_lattes} ↗ +
-
- Bibliográfica - {item.producao_bibliografica ?? 0} -
-
- Técnica - {item.producao_tecnica ?? 0} -
-
- Orientações - {item.orientacoes_lattes ?? 0} -
- {item.data_atualizacao_lattes && ( + {item.nacionalidade && (
- Atualização - {formatDate(item.data_atualizacao_lattes)} + Nacionalidade + {item.nacionalidade}
)} -
- Produções recentes - {producoes.length} -
- {producoes.length > 0 ? ( - - ) : ( -

Nenhuma produção encontrada.

+ + {titulacoes.length > 0 && ( + <> +
Formação Acadêmica
+ + + )} + + {idiomas.length > 0 && ( + <> +
Idiomas
+ + + )} + + {orientacoes.length > 0 && ( + <> +
+ Orientações Concluídas ({item.total_orientacoes}) +
+ + + )} + + {endereco && ( + <> +
Endereço Profissional
+
+ + {[ + endereco.logradouro, + endereco.numero, + endereco.bairro, + endereco.cidade, + endereco.pais + ].filter(Boolean).join(', ')} + +
+ + )} + + {titulacoes.length === 0 && idiomas.length === 0 && orientacoes.length === 0 && ( +

Dados detalhados não disponíveis no ATUACAPES.

)}
);