fix(backend): corrigir exibicao de idiomas e selos multilingue
- Adicionar idiomas e formacoes ao _source das queries ES (client.py) - Corrigir type mismatch int/str no endpoint paginado (routes.py) - Adicionar campo evento nas inscricoes para nome do premio - Implementar extracao de idiomas do ES no repository - Ajustar frontend para exibir selo multilingue corretamente
This commit is contained in:
@@ -57,6 +57,7 @@ class InscricaoDTO:
|
||||
premio: str
|
||||
ano: int
|
||||
situacao: str
|
||||
evento: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -112,6 +113,15 @@ class MembroBancaDTO:
|
||||
ano: Optional[int]
|
||||
|
||||
|
||||
@dataclass
|
||||
class IdiomaDTO:
|
||||
idioma: str
|
||||
nivel_leitura: str = ""
|
||||
nivel_escrita: str = ""
|
||||
nivel_fala: str = ""
|
||||
nivel_compreensao: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class PontuacaoAtuacaoDTO:
|
||||
codigo: str
|
||||
@@ -168,6 +178,7 @@ class ConsultorDetalhadoDTO:
|
||||
participacoes: List[ParticipacaoDTO]
|
||||
orientacoes: List[OrientacaoDTO]
|
||||
membros_banca: List[MembroBancaDTO]
|
||||
idiomas: List[IdiomaDTO]
|
||||
pontuacao: PontuacaoCompletaDTO
|
||||
rank: Optional[int] = None
|
||||
|
||||
|
||||
@@ -143,7 +143,8 @@ class ProcessarRankingJob:
|
||||
"tipo": i.tipo,
|
||||
"premio": i.premio,
|
||||
"ano": i.ano,
|
||||
"situacao": i.situacao
|
||||
"situacao": i.situacao,
|
||||
"evento": i.evento
|
||||
}
|
||||
for i in consultor.inscricoes
|
||||
],
|
||||
@@ -223,6 +224,17 @@ class ProcessarRankingJob:
|
||||
}
|
||||
for d in consultor.docencias
|
||||
],
|
||||
"idiomas": [
|
||||
{
|
||||
"idioma": i.idioma,
|
||||
"nivel_leitura": i.nivel_leitura,
|
||||
"nivel_escrita": i.nivel_escrita,
|
||||
"nivel_fala": i.nivel_fala,
|
||||
"nivel_compreensao": i.nivel_compreensao,
|
||||
}
|
||||
for i in consultor.idiomas
|
||||
],
|
||||
"titulacao": consultor.titulacao,
|
||||
"pontuacao": pontuacao,
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ from ..dtos.consultor_dto import (
|
||||
ParticipacaoDTO,
|
||||
OrientacaoDTO,
|
||||
MembroBancaDTO,
|
||||
IdiomaDTO,
|
||||
PontuacaoAtuacaoDTO,
|
||||
PontuacaoBlocoDTO,
|
||||
PontuacaoCompletaDTO,
|
||||
@@ -118,6 +119,7 @@ class ObterRankingUseCase:
|
||||
premio=i.premio,
|
||||
ano=i.ano,
|
||||
situacao=i.situacao,
|
||||
evento=i.evento,
|
||||
)
|
||||
for i in consultor.inscricoes
|
||||
],
|
||||
@@ -180,6 +182,16 @@ class ObterRankingUseCase:
|
||||
)
|
||||
for m in consultor.membros_banca
|
||||
],
|
||||
idiomas=[
|
||||
IdiomaDTO(
|
||||
idioma=i.idioma,
|
||||
nivel_leitura=i.nivel_leitura,
|
||||
nivel_escrita=i.nivel_escrita,
|
||||
nivel_fala=i.nivel_fala,
|
||||
nivel_compreensao=i.nivel_compreensao,
|
||||
)
|
||||
for i in consultor.idiomas
|
||||
],
|
||||
pontuacao=PontuacaoCompletaDTO(
|
||||
bloco_a=PontuacaoBlocoDTO(
|
||||
bloco="A",
|
||||
|
||||
@@ -50,6 +50,7 @@ class Inscricao:
|
||||
premio: str
|
||||
ano: int
|
||||
situacao: str = ""
|
||||
evento: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -119,6 +120,15 @@ class DocenciaPPG:
|
||||
linhas_pesquisa: List[str] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Idioma:
|
||||
idioma: str
|
||||
nivel_leitura: str = ""
|
||||
nivel_escrita: str = ""
|
||||
nivel_fala: str = ""
|
||||
nivel_compreensao: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class Consultor:
|
||||
id_pessoa: int
|
||||
@@ -135,6 +145,8 @@ class Consultor:
|
||||
orientacoes: List[Orientacao] = field(default_factory=list)
|
||||
membros_banca: List[MembroBanca] = field(default_factory=list)
|
||||
docencias: List[DocenciaPPG] = field(default_factory=list)
|
||||
idiomas: List[Idioma] = field(default_factory=list)
|
||||
titulacao: Optional[str] = None
|
||||
pontuacao: Optional[PontuacaoCompleta] = None
|
||||
|
||||
@property
|
||||
|
||||
@@ -52,7 +52,7 @@ class ElasticsearchClient:
|
||||
try:
|
||||
query = {
|
||||
"query": {"term": {"id": id_pessoa}},
|
||||
"_source": ["id", "dadosPessoais", "atuacoes"],
|
||||
"_source": ["id", "dadosPessoais", "atuacoes", "idiomas", "formacoes"],
|
||||
"size": 1,
|
||||
}
|
||||
|
||||
@@ -68,6 +68,28 @@ class ElasticsearchClient:
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Erro ao buscar consultor {id_pessoa}: {e}")
|
||||
|
||||
async def buscar_por_ids(self, ids: list, source_fields: Optional[list] = None) -> list:
|
||||
if not ids:
|
||||
return []
|
||||
try:
|
||||
query = {
|
||||
"query": {"terms": {"id": ids}},
|
||||
"size": len(ids),
|
||||
}
|
||||
if source_fields:
|
||||
query["_source"] = source_fields
|
||||
|
||||
response = await self.client.post(
|
||||
f"{self.url}/{self.index}/_search",
|
||||
json=query
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
return [hit["_source"] for hit in data.get("hits", {}).get("hits", [])]
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Erro ao buscar consultores por ids: {e}")
|
||||
|
||||
async def buscar_documento_completo(self, id_pessoa: int) -> Optional[dict]:
|
||||
try:
|
||||
query = {
|
||||
@@ -104,7 +126,7 @@ class ElasticsearchClient:
|
||||
"query": {"exists": {"field": "atuacoes.tipo"}}
|
||||
}
|
||||
},
|
||||
"_source": ["id", "dadosPessoais", "atuacoes"],
|
||||
"_source": ["id", "dadosPessoais", "atuacoes", "idiomas", "formacoes"],
|
||||
"size": size,
|
||||
"from": from_,
|
||||
"sort": [{"id": "asc"}],
|
||||
@@ -196,7 +218,7 @@ class ElasticsearchClient:
|
||||
"minimum_should_match": 1
|
||||
}
|
||||
},
|
||||
"_source": ["id", "dadosPessoais", "atuacoes"],
|
||||
"_source": ["id", "dadosPessoais", "atuacoes", "idiomas", "formacoes"],
|
||||
"sort": [{"_score": "desc"}]
|
||||
}
|
||||
|
||||
@@ -379,7 +401,7 @@ class ElasticsearchClient:
|
||||
"boost_mode": "replace"
|
||||
}
|
||||
},
|
||||
"_source": ["id", "dadosPessoais", "atuacoes"],
|
||||
"_source": ["id", "dadosPessoais", "atuacoes", "idiomas", "formacoes"],
|
||||
"sort": [{"_score": "desc"}]
|
||||
}
|
||||
|
||||
@@ -412,7 +434,7 @@ class ElasticsearchClient:
|
||||
"query": {"exists": {"field": "atuacoes.tipo"}}
|
||||
}
|
||||
},
|
||||
"_source": ["id", "dadosPessoais", "atuacoes"],
|
||||
"_source": ["id", "dadosPessoais", "atuacoes", "idiomas", "formacoes"],
|
||||
"size": size,
|
||||
"sort": [{"id": "asc"}]
|
||||
}
|
||||
@@ -638,7 +660,7 @@ class ElasticsearchClient:
|
||||
"minimum_should_match": 1
|
||||
}
|
||||
},
|
||||
"_source": ["id", "dadosPessoais", "atuacoes"],
|
||||
"_source": ["id", "dadosPessoais", "atuacoes", "idiomas", "formacoes"],
|
||||
"sort": [{"_score": "desc"}]
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ from ...domain.entities.consultor import (
|
||||
Orientacao,
|
||||
MembroBanca,
|
||||
DocenciaPPG,
|
||||
Idioma,
|
||||
)
|
||||
from ...domain.repositories.consultor_repository import ConsultorRepository
|
||||
from ...domain.services.calculador_pontuacao import CalculadorPontuacao
|
||||
@@ -277,6 +278,7 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
||||
premio=nome_premio,
|
||||
ano=ano,
|
||||
situacao=dados.get("situacao", ""),
|
||||
evento=dados.get("evento", ""),
|
||||
))
|
||||
|
||||
return inscricoes
|
||||
@@ -604,6 +606,77 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
||||
docencias.sort(key=lambda d: (d.periodo.fim is not None, d.periodo.inicio or datetime.min), reverse=True)
|
||||
return docencias
|
||||
|
||||
def _extrair_idiomas(self, doc: Dict[str, Any]) -> List[Idioma]:
|
||||
idiomas_data = doc.get("idiomas") or []
|
||||
if not idiomas_data:
|
||||
dados_pessoais = doc.get("dadosPessoais", {}) or {}
|
||||
idiomas_data = (
|
||||
dados_pessoais.get("idiomas")
|
||||
or dados_pessoais.get("idiomasEstrangeiros")
|
||||
or []
|
||||
)
|
||||
idiomas = []
|
||||
|
||||
for item in idiomas_data:
|
||||
if isinstance(item, str):
|
||||
idioma_nome = item
|
||||
item_dict = {}
|
||||
else:
|
||||
item_dict = item or {}
|
||||
idioma_nome = item_dict.get("idioma") or item_dict.get("nome") or item_dict.get("descricao")
|
||||
if isinstance(idioma_nome, dict):
|
||||
idioma_nome = (
|
||||
idioma_nome.get("nome")
|
||||
or idioma_nome.get("descricao")
|
||||
or idioma_nome.get("idioma")
|
||||
)
|
||||
if not idioma_nome:
|
||||
continue
|
||||
nivel_padrao = (
|
||||
item_dict.get("nivel")
|
||||
or item_dict.get("nivelConhecimento")
|
||||
or item_dict.get("nivel_leitura")
|
||||
or item_dict.get("nivelLeitura")
|
||||
or ""
|
||||
)
|
||||
idiomas.append(Idioma(
|
||||
idioma=idioma_nome,
|
||||
nivel_leitura=item_dict.get("nivelLeitura", "") or item_dict.get("leitura", "") or nivel_padrao,
|
||||
nivel_escrita=item_dict.get("nivelEscrita", "") or item_dict.get("escrita", "") or nivel_padrao,
|
||||
nivel_fala=item_dict.get("nivelFala", "") or item_dict.get("fala", "") or nivel_padrao,
|
||||
nivel_compreensao=item_dict.get("nivelCompreensao", "") or item_dict.get("compreensao", "") or nivel_padrao,
|
||||
))
|
||||
|
||||
return idiomas
|
||||
|
||||
def _extrair_titulacao(self, doc: Dict[str, Any]) -> Optional[str]:
|
||||
dados_pessoais = doc.get("dadosPessoais", {}) or {}
|
||||
titulacao = dados_pessoais.get("titulacao") or dados_pessoais.get("grauFormacao")
|
||||
if titulacao:
|
||||
return titulacao
|
||||
|
||||
formacoes = doc.get("formacoes", []) or []
|
||||
for f in formacoes:
|
||||
nivel = f.get("nivel", "").lower()
|
||||
if "pós-doutorado" in nivel or "pos-doutorado" in nivel or "posdoc" in nivel:
|
||||
return "Pós-Doutorado"
|
||||
elif "doutorado" in nivel:
|
||||
return "Doutorado"
|
||||
elif "mestrado" in nivel:
|
||||
return "Mestrado"
|
||||
|
||||
atuacoes = doc.get("atuacoes", []) or []
|
||||
for a in atuacoes:
|
||||
if a.get("tipo") == "Docência":
|
||||
dados = a.get("dadosDocencia", {}) or {}
|
||||
categoria = (dados.get("categoria", "") or "").lower()
|
||||
if "doutor" in categoria:
|
||||
return "Doutorado"
|
||||
elif "mestre" in categoria:
|
||||
return "Mestrado"
|
||||
|
||||
return None
|
||||
|
||||
async def _construir_consultor(self, doc: Dict[str, Any]) -> Consultor:
|
||||
id_pessoa = doc["id"]
|
||||
dados_pessoais = doc.get("dadosPessoais", {})
|
||||
@@ -621,6 +694,8 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
||||
membros_banca = self._extrair_membros_banca(atuacoes)
|
||||
docencias = self._extrair_docencias(atuacoes)
|
||||
coordenador_ppg = self._tem_coordenacao_ppg(atuacoes)
|
||||
idiomas = self._extrair_idiomas(doc)
|
||||
titulacao = self._extrair_titulacao(doc)
|
||||
|
||||
consultor = Consultor(
|
||||
id_pessoa=id_pessoa,
|
||||
@@ -637,6 +712,8 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
||||
orientacoes=orientacoes + coorientacoes,
|
||||
membros_banca=membros_banca,
|
||||
docencias=docencias,
|
||||
idiomas=idiomas,
|
||||
titulacao=titulacao,
|
||||
)
|
||||
|
||||
consultor.pontuacao = self.calculador.calcular_pontuacao_completa(consultor)
|
||||
|
||||
@@ -206,6 +206,8 @@ async def ranking_paginado(
|
||||
ativo: Optional[bool] = Query(default=None, description="Filtrar por status ativo"),
|
||||
selos: Optional[str] = Query(default=None, description="Filtrar por selos (separados por vírgula)"),
|
||||
oracle_repo = Depends(get_ranking_oracle_repo),
|
||||
es_client: ElasticsearchClient = Depends(get_es_client),
|
||||
repository: ConsultorRepositoryImpl = Depends(get_repository),
|
||||
):
|
||||
import json as json_lib
|
||||
|
||||
@@ -223,12 +225,46 @@ async def ranking_paginado(
|
||||
total_pages = (total + size - 1) // size
|
||||
|
||||
consultores_schema = []
|
||||
consultores_dados = []
|
||||
faltando_idiomas = []
|
||||
for c in consultores:
|
||||
try:
|
||||
d = json_lib.loads(c.json_detalhes) if isinstance(c.json_detalhes, str) else c.json_detalhes or {}
|
||||
except (json_lib.JSONDecodeError, TypeError):
|
||||
d = {}
|
||||
consultores_dados.append((c, d))
|
||||
if not d.get("idiomas"):
|
||||
faltando_idiomas.append((c.id_pessoa, d))
|
||||
|
||||
if faltando_idiomas:
|
||||
ids = [item[0] for item in faltando_idiomas]
|
||||
docs = await es_client.buscar_por_ids(
|
||||
ids,
|
||||
source_fields=["id", "dadosPessoais", "idiomas", "atuacoes", "formacoes"],
|
||||
)
|
||||
docs_map = {int(doc.get("id")): doc for doc in docs if doc.get("id")}
|
||||
for id_pessoa, detalhes in faltando_idiomas:
|
||||
doc = docs_map.get(int(id_pessoa))
|
||||
if not doc:
|
||||
continue
|
||||
idiomas = repository._extrair_idiomas(doc)
|
||||
if idiomas:
|
||||
detalhes["idiomas"] = [
|
||||
{
|
||||
"idioma": i.idioma,
|
||||
"nivel_leitura": i.nivel_leitura,
|
||||
"nivel_escrita": i.nivel_escrita,
|
||||
"nivel_fala": i.nivel_fala,
|
||||
"nivel_compreensao": i.nivel_compreensao,
|
||||
}
|
||||
for i in idiomas
|
||||
]
|
||||
if not detalhes.get("titulacao"):
|
||||
titulacao = repository._extrair_titulacao(doc)
|
||||
if titulacao:
|
||||
detalhes["titulacao"] = titulacao
|
||||
|
||||
for c, d in consultores_dados:
|
||||
tipos_atuacao = RankingMapper._extrair_tipos_atuacao(d)
|
||||
consultores_schema.append(
|
||||
ConsultorRankingResumoSchema(
|
||||
@@ -255,6 +291,8 @@ async def ranking_paginado(
|
||||
orientacoes=d.get("orientacoes"),
|
||||
membros_banca=d.get("membros_banca"),
|
||||
docencias=d.get("docencias"),
|
||||
idiomas=d.get("idiomas"),
|
||||
titulacao=d.get("titulacao"),
|
||||
pontuacao=d.get("pontuacao"),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -47,6 +47,7 @@ class InscricaoSchema(BaseModel):
|
||||
premio: str
|
||||
ano: int
|
||||
situacao: str
|
||||
evento: str = ""
|
||||
|
||||
|
||||
class AvaliacaoComissaoSchema(BaseModel):
|
||||
@@ -96,6 +97,14 @@ class MembroBancaSchema(BaseModel):
|
||||
ano: Optional[int] = None
|
||||
|
||||
|
||||
class IdiomaSchema(BaseModel):
|
||||
idioma: str
|
||||
nivel_leitura: str = ""
|
||||
nivel_escrita: str = ""
|
||||
nivel_fala: str = ""
|
||||
nivel_compreensao: str = ""
|
||||
|
||||
|
||||
class PontuacaoAtuacaoSchema(BaseModel):
|
||||
codigo: str
|
||||
base: int
|
||||
@@ -147,6 +156,7 @@ class ConsultorDetalhadoSchema(BaseModel):
|
||||
participacoes: List[ParticipacaoSchema]
|
||||
orientacoes: List[OrientacaoSchema]
|
||||
membros_banca: List[MembroBancaSchema]
|
||||
idiomas: List[IdiomaSchema] = []
|
||||
pontuacao: PontuacaoCompletaSchema
|
||||
rank: Optional[int] = None
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ class ConsultorRankingResumoSchema(BaseModel):
|
||||
orientacoes: Optional[list] = None
|
||||
membros_banca: Optional[list] = None
|
||||
docencias: Optional[list] = None
|
||||
idiomas: Optional[list] = None
|
||||
titulacao: Optional[str] = None
|
||||
pontuacao: Optional[dict] = None
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user