feat(vinculos): adicionar vínculos de consultoria com IES e ordenação cronológica
- Adicionar entidades e DTOs para vínculos de consultoria (IES, período, situação) - Extrair vínculos do Elasticsearch com datas e informações da IES - Exibir vínculos no card do consultor com sigla e nome completo da IES - Ordenar todas as listas do detalhe por data/ano decrescente (mais recente primeiro)
This commit is contained in:
@@ -21,6 +21,20 @@ class CoordenacaoCapesDTO:
|
|||||||
presidente: bool
|
presidente: bool
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class IESDTO:
|
||||||
|
id: str
|
||||||
|
nome: str
|
||||||
|
sigla: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class VinculoConsultoriaDTO:
|
||||||
|
periodo: PeriodoDTO
|
||||||
|
ies: Optional[IESDTO] = None
|
||||||
|
situacao: str = ""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ConsultoriaDTO:
|
class ConsultoriaDTO:
|
||||||
codigo: str
|
codigo: str
|
||||||
@@ -29,6 +43,11 @@ class ConsultoriaDTO:
|
|||||||
areas: List[str]
|
areas: List[str]
|
||||||
anos_consecutivos: int
|
anos_consecutivos: int
|
||||||
retornos: int
|
retornos: int
|
||||||
|
vinculos: List[VinculoConsultoriaDTO] = None
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
if self.vinculos is None:
|
||||||
|
self.vinculos = []
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
@@ -131,7 +131,21 @@ class ProcessarRankingJob:
|
|||||||
"fim": consultor.consultoria.periodo.fim.isoformat() if consultor.consultoria.periodo.fim else None,
|
"fim": consultor.consultoria.periodo.fim.isoformat() if consultor.consultoria.periodo.fim else None,
|
||||||
"areas": consultor.consultoria.areas,
|
"areas": consultor.consultoria.areas,
|
||||||
"anos_consecutivos": consultor.consultoria.anos_consecutivos,
|
"anos_consecutivos": consultor.consultoria.anos_consecutivos,
|
||||||
"retornos": consultor.consultoria.retornos
|
"retornos": consultor.consultoria.retornos,
|
||||||
|
"vinculos": [
|
||||||
|
{
|
||||||
|
"inicio": v.periodo.inicio.isoformat() if v.periodo.inicio else None,
|
||||||
|
"fim": v.periodo.fim.isoformat() if v.periodo.fim else None,
|
||||||
|
"ativo": v.periodo.ativo,
|
||||||
|
"situacao": v.situacao,
|
||||||
|
"ies": {
|
||||||
|
"id": v.ies.id,
|
||||||
|
"nome": v.ies.nome,
|
||||||
|
"sigla": v.ies.sigla,
|
||||||
|
} if v.ies else None,
|
||||||
|
}
|
||||||
|
for v in consultor.consultoria.vinculos
|
||||||
|
],
|
||||||
} if consultor.consultoria else None,
|
} if consultor.consultoria else None,
|
||||||
"inscricoes": [
|
"inscricoes": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ from ..dtos.consultor_dto import (
|
|||||||
PeriodoDTO,
|
PeriodoDTO,
|
||||||
CoordenacaoCapesDTO,
|
CoordenacaoCapesDTO,
|
||||||
ConsultoriaDTO,
|
ConsultoriaDTO,
|
||||||
|
IESDTO,
|
||||||
|
VinculoConsultoriaDTO,
|
||||||
InscricaoDTO,
|
InscricaoDTO,
|
||||||
AvaliacaoComissaoDTO,
|
AvaliacaoComissaoDTO,
|
||||||
PremiacaoDTO,
|
PremiacaoDTO,
|
||||||
@@ -91,6 +93,23 @@ class ObterRankingUseCase:
|
|||||||
areas=consultor.consultoria.areas,
|
areas=consultor.consultoria.areas,
|
||||||
anos_consecutivos=consultor.consultoria.anos_consecutivos,
|
anos_consecutivos=consultor.consultoria.anos_consecutivos,
|
||||||
retornos=consultor.consultoria.retornos,
|
retornos=consultor.consultoria.retornos,
|
||||||
|
vinculos=[
|
||||||
|
VinculoConsultoriaDTO(
|
||||||
|
periodo=PeriodoDTO(
|
||||||
|
inicio=v.periodo.inicio.isoformat() if v.periodo.inicio else "",
|
||||||
|
fim=v.periodo.fim.isoformat() if v.periodo.fim else None,
|
||||||
|
ativo=v.periodo.ativo,
|
||||||
|
anos_decorridos=v.periodo.anos_decorridos,
|
||||||
|
),
|
||||||
|
ies=IESDTO(
|
||||||
|
id=v.ies.id,
|
||||||
|
nome=v.ies.nome,
|
||||||
|
sigla=v.ies.sigla,
|
||||||
|
) if v.ies else None,
|
||||||
|
situacao=v.situacao,
|
||||||
|
)
|
||||||
|
for v in consultor.consultoria.vinculos
|
||||||
|
],
|
||||||
) if consultor.consultoria else None,
|
) if consultor.consultoria else None,
|
||||||
inscricoes=[
|
inscricoes=[
|
||||||
InscricaoDTO(
|
InscricaoDTO(
|
||||||
|
|||||||
@@ -17,12 +17,27 @@ class CoordenacaoCapes:
|
|||||||
presidente: bool = False
|
presidente: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class IES:
|
||||||
|
id: str
|
||||||
|
nome: str
|
||||||
|
sigla: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class VinculoConsultoria:
|
||||||
|
periodo: Periodo
|
||||||
|
ies: Optional[IES] = None
|
||||||
|
situacao: str = ""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Consultoria:
|
class Consultoria:
|
||||||
codigo: str
|
codigo: str
|
||||||
situacao: str
|
situacao: str
|
||||||
periodo: Periodo
|
periodo: Periodo
|
||||||
periodos: List[Periodo] = field(default_factory=list)
|
periodos: List[Periodo] = field(default_factory=list)
|
||||||
|
vinculos: List[VinculoConsultoria] = field(default_factory=list)
|
||||||
areas: List[str] = field(default_factory=list)
|
areas: List[str] = field(default_factory=list)
|
||||||
anos_consecutivos: int = 0
|
anos_consecutivos: int = 0
|
||||||
retornos: int = 0
|
retornos: int = 0
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ from ...domain.entities.consultor import (
|
|||||||
Consultor,
|
Consultor,
|
||||||
CoordenacaoCapes,
|
CoordenacaoCapes,
|
||||||
Consultoria,
|
Consultoria,
|
||||||
|
IES,
|
||||||
|
VinculoConsultoria,
|
||||||
Inscricao,
|
Inscricao,
|
||||||
AvaliacaoComissao,
|
AvaliacaoComissao,
|
||||||
Premiacao,
|
Premiacao,
|
||||||
@@ -140,6 +142,7 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
periodos: List[Periodo] = []
|
periodos: List[Periodo] = []
|
||||||
|
vinculos: List[VinculoConsultoria] = []
|
||||||
situacoes: List[str] = []
|
situacoes: List[str] = []
|
||||||
areas: List[str] = []
|
areas: List[str] = []
|
||||||
|
|
||||||
@@ -162,9 +165,25 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
|||||||
|
|
||||||
if inicio and fim and fim < inicio:
|
if inicio and fim and fim < inicio:
|
||||||
fim = None
|
fim = None
|
||||||
|
|
||||||
|
ies_data = dc.get("ies", {}) or {}
|
||||||
|
ies = None
|
||||||
|
if ies_data and (ies_data.get("id") or ies_data.get("nome")):
|
||||||
|
ies = IES(
|
||||||
|
id=str(ies_data.get("id", "")),
|
||||||
|
nome=ies_data.get("nome", ""),
|
||||||
|
sigla=ies_data.get("sigla"),
|
||||||
|
)
|
||||||
|
|
||||||
if inicio:
|
if inicio:
|
||||||
try:
|
try:
|
||||||
periodos.append(Periodo(inicio=inicio, fim=fim))
|
periodo = Periodo(inicio=inicio, fim=fim)
|
||||||
|
periodos.append(periodo)
|
||||||
|
vinculos.append(VinculoConsultoria(
|
||||||
|
periodo=periodo,
|
||||||
|
ies=ies,
|
||||||
|
situacao=situacao or "",
|
||||||
|
))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -176,6 +195,8 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
|||||||
if not periodos:
|
if not periodos:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
vinculos.sort(key=lambda v: v.periodo.inicio, reverse=True)
|
||||||
|
|
||||||
mesclados = mesclar_periodos(periodos)
|
mesclados = mesclar_periodos(periodos)
|
||||||
periodo_ativo = next((p for p in mesclados if p.ativo), None)
|
periodo_ativo = next((p for p in mesclados if p.ativo), None)
|
||||||
anos_consecutivos = periodo_ativo.anos_completos(datetime.now()) if periodo_ativo else 0
|
anos_consecutivos = periodo_ativo.anos_completos(datetime.now()) if periodo_ativo else 0
|
||||||
@@ -205,6 +226,7 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
|||||||
situacao=situacao_final,
|
situacao=situacao_final,
|
||||||
periodo=Periodo(inicio=primeiro_evento, fim=fim_final),
|
periodo=Periodo(inicio=primeiro_evento, fim=fim_final),
|
||||||
periodos=mesclados,
|
periodos=mesclados,
|
||||||
|
vinculos=vinculos,
|
||||||
areas=areas,
|
areas=areas,
|
||||||
anos_consecutivos=anos_consecutivos,
|
anos_consecutivos=anos_consecutivos,
|
||||||
retornos=retornos,
|
retornos=retornos,
|
||||||
|
|||||||
@@ -9,6 +9,18 @@ class PeriodoSchema(BaseModel):
|
|||||||
anos_decorridos: float
|
anos_decorridos: float
|
||||||
|
|
||||||
|
|
||||||
|
class IESSchema(BaseModel):
|
||||||
|
id: str
|
||||||
|
nome: str
|
||||||
|
sigla: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class VinculoConsultoriaSchema(BaseModel):
|
||||||
|
periodo: PeriodoSchema
|
||||||
|
ies: Optional[IESSchema] = None
|
||||||
|
situacao: str = ""
|
||||||
|
|
||||||
|
|
||||||
class CoordenacaoCapesSchema(BaseModel):
|
class CoordenacaoCapesSchema(BaseModel):
|
||||||
codigo: str
|
codigo: str
|
||||||
tipo: str
|
tipo: str
|
||||||
@@ -23,6 +35,7 @@ class ConsultoriaSchema(BaseModel):
|
|||||||
codigo: str
|
codigo: str
|
||||||
situacao: str
|
situacao: str
|
||||||
periodo: PeriodoSchema
|
periodo: PeriodoSchema
|
||||||
|
vinculos: List[VinculoConsultoriaSchema] = []
|
||||||
areas: List[str]
|
areas: List[str]
|
||||||
anos_consecutivos: int
|
anos_consecutivos: int
|
||||||
retornos: int
|
retornos: int
|
||||||
|
|||||||
@@ -458,6 +458,13 @@
|
|||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-item .ies-nome {
|
||||||
|
flex: 1;
|
||||||
|
color: var(--text);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.details-grid {
|
.details-grid {
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
|||||||
@@ -346,11 +346,44 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{consultoria?.vinculos?.length > 0 && (
|
||||||
|
<div className="extra-details">
|
||||||
|
<h4>Vinculos de Consultoria</h4>
|
||||||
|
<div className="list-items">
|
||||||
|
{[...consultoria.vinculos]
|
||||||
|
.sort((a, b) => new Date(b.periodo?.inicio || 0) - new Date(a.periodo?.inicio || 0))
|
||||||
|
.map((vinculo, idx) => {
|
||||||
|
const periodo = vinculo.periodo || {};
|
||||||
|
const isAtivo = periodo.ativo ?? !periodo.fim;
|
||||||
|
return (
|
||||||
|
<div key={idx} className="list-item">
|
||||||
|
<span className={`badge ${isAtivo ? 'badge-ativo' : 'badge-historico'}`}>
|
||||||
|
{isAtivo ? 'ATIVO' : 'ENCERRADO'}
|
||||||
|
</span>
|
||||||
|
<span className="ies-nome">
|
||||||
|
{vinculo.ies
|
||||||
|
? vinculo.ies.sigla && vinculo.ies.nome
|
||||||
|
? `${vinculo.ies.sigla} - ${vinculo.ies.nome}`
|
||||||
|
: vinculo.ies.sigla || vinculo.ies.nome
|
||||||
|
: 'IES nao informada'}
|
||||||
|
</span>
|
||||||
|
<span className="muted">
|
||||||
|
{formatDate(periodo.inicio)} - {isAtivo ? 'Atual' : formatDate(periodo.fim)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{consultor.coordenacoes_capes?.length > 0 && (
|
{consultor.coordenacoes_capes?.length > 0 && (
|
||||||
<div className="extra-details">
|
<div className="extra-details">
|
||||||
<h4>Coordenacoes CAPES</h4>
|
<h4>Coordenacoes CAPES</h4>
|
||||||
<div className="list-items">
|
<div className="list-items">
|
||||||
{consultor.coordenacoes_capes.map((coord, idx) => (
|
{[...consultor.coordenacoes_capes]
|
||||||
|
.sort((a, b) => new Date(b.inicio || b.periodo?.inicio || 0) - new Date(a.inicio || a.periodo?.inicio || 0))
|
||||||
|
.map((coord, idx) => (
|
||||||
<div key={idx} className="list-item">
|
<div key={idx} className="list-item">
|
||||||
<span className="badge">{coord.codigo || coord.tipo}</span>
|
<span className="badge">{coord.codigo || coord.tipo}</span>
|
||||||
<span className="pontos">{PONTOS_BASE[coord.codigo] || 0} pts</span>
|
<span className="pontos">{PONTOS_BASE[coord.codigo] || 0} pts</span>
|
||||||
@@ -368,7 +401,9 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
<div className="extra-details">
|
<div className="extra-details">
|
||||||
<h4>Premiacoes</h4>
|
<h4>Premiacoes</h4>
|
||||||
<div className="list-items">
|
<div className="list-items">
|
||||||
{consultor.premiacoes.map((prem, idx) => (
|
{[...consultor.premiacoes]
|
||||||
|
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||||||
|
.map((prem, idx) => (
|
||||||
<div key={idx} className="list-item">
|
<div key={idx} className="list-item">
|
||||||
<span className="badge">{prem.codigo}</span>
|
<span className="badge">{prem.codigo}</span>
|
||||||
<span className="pontos">{PONTOS_BASE[prem.codigo] || 0} pts</span>
|
<span className="pontos">{PONTOS_BASE[prem.codigo] || 0} pts</span>
|
||||||
@@ -384,7 +419,9 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
<div className="extra-details">
|
<div className="extra-details">
|
||||||
<h4>Avaliacoes de Comissao</h4>
|
<h4>Avaliacoes de Comissao</h4>
|
||||||
<div className="list-items">
|
<div className="list-items">
|
||||||
{consultor.avaliacoes_comissao.map((aval, idx) => (
|
{[...consultor.avaliacoes_comissao]
|
||||||
|
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||||||
|
.map((aval, idx) => (
|
||||||
<div key={idx} className="list-item">
|
<div key={idx} className="list-item">
|
||||||
<span className="badge">{aval.codigo}</span>
|
<span className="badge">{aval.codigo}</span>
|
||||||
<span className="pontos">{PONTOS_BASE[aval.codigo] || 0} pts</span>
|
<span className="pontos">{PONTOS_BASE[aval.codigo] || 0} pts</span>
|
||||||
@@ -400,7 +437,9 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
<div className="extra-details">
|
<div className="extra-details">
|
||||||
<h4>Inscricoes</h4>
|
<h4>Inscricoes</h4>
|
||||||
<div className="list-items">
|
<div className="list-items">
|
||||||
{consultor.inscricoes.map((insc, idx) => (
|
{[...consultor.inscricoes]
|
||||||
|
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||||||
|
.map((insc, idx) => (
|
||||||
<div key={idx} className="list-item">
|
<div key={idx} className="list-item">
|
||||||
<span className="badge">{insc.codigo}</span>
|
<span className="badge">{insc.codigo}</span>
|
||||||
<span className="pontos">{PONTOS_BASE[insc.codigo] || 0} pts</span>
|
<span className="pontos">{PONTOS_BASE[insc.codigo] || 0} pts</span>
|
||||||
@@ -416,7 +455,10 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
<div className="extra-details">
|
<div className="extra-details">
|
||||||
<h4>Participacoes (Eventos/Projetos)</h4>
|
<h4>Participacoes (Eventos/Projetos)</h4>
|
||||||
<div className="list-items">
|
<div className="list-items">
|
||||||
{consultor.participacoes.slice(0, 10).map((part, idx) => (
|
{[...consultor.participacoes]
|
||||||
|
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||||||
|
.slice(0, 10)
|
||||||
|
.map((part, idx) => (
|
||||||
<div key={idx} className="list-item">
|
<div key={idx} className="list-item">
|
||||||
<span className="badge">{part.codigo}</span>
|
<span className="badge">{part.codigo}</span>
|
||||||
<span className="pontos">{PONTOS_BASE[part.codigo] || 0} pts</span>
|
<span className="pontos">{PONTOS_BASE[part.codigo] || 0} pts</span>
|
||||||
|
|||||||
@@ -69,6 +69,15 @@ export const rankingService = {
|
|||||||
areas: consultoria.areas || [],
|
areas: consultoria.areas || [],
|
||||||
anos_consecutivos: consultoria.anos_consecutivos || 0,
|
anos_consecutivos: consultoria.anos_consecutivos || 0,
|
||||||
retornos: consultoria.retornos || 0,
|
retornos: consultoria.retornos || 0,
|
||||||
|
vinculos: (consultoria.vinculos || []).map((v) => ({
|
||||||
|
periodo: {
|
||||||
|
inicio: v.inicio || v.periodo?.inicio || null,
|
||||||
|
fim: v.fim || v.periodo?.fim || null,
|
||||||
|
ativo: v.ativo ?? v.periodo?.ativo ?? !v.fim,
|
||||||
|
},
|
||||||
|
ies: v.ies || null,
|
||||||
|
situacao: v.situacao || '',
|
||||||
|
})),
|
||||||
},
|
},
|
||||||
coordenacoes_capes: coordenacoesCapes,
|
coordenacoes_capes: coordenacoesCapes,
|
||||||
inscricoes: c.inscricoes || [],
|
inscricoes: c.inscricoes || [],
|
||||||
|
|||||||
Reference in New Issue
Block a user