feat: extrair docencias PPG e simplificar blocos de pontuacao
Backend: - Adicionar entidade DocenciaPPG para dados de docencia - Extrair docencias do Elasticsearch (tipo "Docência") - Serializar docencias no JSON de detalhes do consultor - Aumentar batch size de 500 para 2000 para melhor performance Frontend: - Remover Bloco B (Coord. PPG) - reservado para V2 - Simplificar formula para: Bloco A + Bloco C + Bloco D - Filtrar orientacoes/bancas da listagem (sao apenas selos) - Atualizar Header com nota que PPG_COORD e apenas indicador - Exibir pontuacao base nos modais de orientacao/banca
This commit is contained in:
@@ -214,6 +214,23 @@ class ProcessarRankingJob:
|
|||||||
}
|
}
|
||||||
for m in consultor.membros_banca
|
for m in consultor.membros_banca
|
||||||
],
|
],
|
||||||
|
"docencias": [
|
||||||
|
{
|
||||||
|
"programa": d.programa,
|
||||||
|
"codigo_programa": d.codigo_programa,
|
||||||
|
"ies_sigla": d.ies_sigla,
|
||||||
|
"ies_nome": d.ies_nome,
|
||||||
|
"categoria": d.categoria,
|
||||||
|
"area_avaliacao": d.area_avaliacao,
|
||||||
|
"modalidade": d.modalidade,
|
||||||
|
"inicio": d.periodo.inicio.isoformat() if d.periodo.inicio else None,
|
||||||
|
"fim": d.periodo.fim.isoformat() if d.periodo.fim else None,
|
||||||
|
"ativo": d.periodo.ativo,
|
||||||
|
"carga_horaria": d.carga_horaria,
|
||||||
|
"linhas_pesquisa": d.linhas_pesquisa,
|
||||||
|
}
|
||||||
|
for d in consultor.docencias
|
||||||
|
],
|
||||||
"pontuacao": pontuacao,
|
"pontuacao": pontuacao,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,7 +306,7 @@ class ProcessarRankingJob:
|
|||||||
if limpar_antes:
|
if limpar_antes:
|
||||||
self.ranking_oracle_repo.limpar_tabela()
|
self.ranking_oracle_repo.limpar_tabela()
|
||||||
|
|
||||||
batch_size = 500
|
batch_size = 2000
|
||||||
for i in range(0, len(consultores), batch_size):
|
for i in range(0, len(consultores), batch_size):
|
||||||
batch = consultores[i:i + batch_size]
|
batch = consultores[i:i + batch_size]
|
||||||
self.ranking_oracle_repo.inserir_batch(batch)
|
self.ranking_oracle_repo.inserir_batch(batch)
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class RankingMapper:
|
|||||||
participacoes = None
|
participacoes = None
|
||||||
orientacoes = None
|
orientacoes = None
|
||||||
membros_banca = None
|
membros_banca = None
|
||||||
|
docencias = None
|
||||||
pontuacao = None
|
pontuacao = None
|
||||||
tipos_atuacao = []
|
tipos_atuacao = []
|
||||||
|
|
||||||
@@ -68,6 +69,7 @@ class RankingMapper:
|
|||||||
participacoes = jd.get("participacoes")
|
participacoes = jd.get("participacoes")
|
||||||
orientacoes = jd.get("orientacoes")
|
orientacoes = jd.get("orientacoes")
|
||||||
membros_banca = jd.get("membros_banca")
|
membros_banca = jd.get("membros_banca")
|
||||||
|
docencias = jd.get("docencias")
|
||||||
pontuacao = jd.get("pontuacao")
|
pontuacao = jd.get("pontuacao")
|
||||||
tipos_atuacao = RankingMapper._extrair_tipos_atuacao(jd)
|
tipos_atuacao = RankingMapper._extrair_tipos_atuacao(jd)
|
||||||
except (json.JSONDecodeError, TypeError) as e:
|
except (json.JSONDecodeError, TypeError) as e:
|
||||||
@@ -104,6 +106,7 @@ class RankingMapper:
|
|||||||
participacoes=participacoes,
|
participacoes=participacoes,
|
||||||
orientacoes=orientacoes,
|
orientacoes=orientacoes,
|
||||||
membros_banca=membros_banca,
|
membros_banca=membros_banca,
|
||||||
|
docencias=docencias,
|
||||||
pontuacao=pontuacao_ajustada if pontuacao_ajustada else None,
|
pontuacao=pontuacao_ajustada if pontuacao_ajustada else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,20 @@ class MembroBanca:
|
|||||||
ano: Optional[int] = None
|
ano: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DocenciaPPG:
|
||||||
|
programa: str
|
||||||
|
codigo_programa: str
|
||||||
|
ies_sigla: str
|
||||||
|
ies_nome: str
|
||||||
|
categoria: str
|
||||||
|
area_avaliacao: str
|
||||||
|
modalidade: str
|
||||||
|
periodo: Periodo
|
||||||
|
carga_horaria: Optional[int] = None
|
||||||
|
linhas_pesquisa: List[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Consultor:
|
class Consultor:
|
||||||
id_pessoa: int
|
id_pessoa: int
|
||||||
@@ -120,6 +134,7 @@ class Consultor:
|
|||||||
participacoes: List[Participacao] = field(default_factory=list)
|
participacoes: List[Participacao] = field(default_factory=list)
|
||||||
orientacoes: List[Orientacao] = field(default_factory=list)
|
orientacoes: List[Orientacao] = field(default_factory=list)
|
||||||
membros_banca: List[MembroBanca] = field(default_factory=list)
|
membros_banca: List[MembroBanca] = field(default_factory=list)
|
||||||
|
docencias: List[DocenciaPPG] = field(default_factory=list)
|
||||||
pontuacao: Optional[PontuacaoCompleta] = None
|
pontuacao: Optional[PontuacaoCompleta] = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class RankingOracleRepository:
|
|||||||
|
|
||||||
def inserir_batch(self, consultores: List[Dict[str, Any]]) -> int:
|
def inserir_batch(self, consultores: List[Dict[str, Any]]) -> int:
|
||||||
"""
|
"""
|
||||||
Insere ou atualiza um batch de consultores usando MERGE.
|
Insere batch de consultores usando executemany (muito mais rápido).
|
||||||
Retorna o número de registros processados.
|
Retorna o número de registros processados.
|
||||||
"""
|
"""
|
||||||
import oracledb
|
import oracledb
|
||||||
@@ -20,68 +20,39 @@ class RankingOracleRepository:
|
|||||||
if not consultores:
|
if not consultores:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
merge_sql = """
|
insert_sql = """
|
||||||
MERGE INTO TB_RANKING_CONSULTOR t
|
INSERT INTO TB_RANKING_CONSULTOR (
|
||||||
USING (
|
|
||||||
SELECT
|
|
||||||
:id_pessoa AS ID_PESSOA,
|
|
||||||
:nome AS NOME,
|
|
||||||
:pontuacao_total AS PONTUACAO_TOTAL,
|
|
||||||
:componente_a AS COMPONENTE_A,
|
|
||||||
:componente_b AS COMPONENTE_B,
|
|
||||||
:componente_c AS COMPONENTE_C,
|
|
||||||
:componente_d AS COMPONENTE_D,
|
|
||||||
:ativo AS ATIVO,
|
|
||||||
:anos_atuacao AS ANOS_ATUACAO,
|
|
||||||
TO_CLOB(:json_detalhes) AS JSON_DETALHES
|
|
||||||
FROM DUAL
|
|
||||||
) s
|
|
||||||
ON (t.ID_PESSOA = s.ID_PESSOA)
|
|
||||||
WHEN MATCHED THEN
|
|
||||||
UPDATE SET
|
|
||||||
t.NOME = s.NOME,
|
|
||||||
t.PONTUACAO_TOTAL = s.PONTUACAO_TOTAL,
|
|
||||||
t.COMPONENTE_A = s.COMPONENTE_A,
|
|
||||||
t.COMPONENTE_B = s.COMPONENTE_B,
|
|
||||||
t.COMPONENTE_C = s.COMPONENTE_C,
|
|
||||||
t.COMPONENTE_D = s.COMPONENTE_D,
|
|
||||||
t.ATIVO = s.ATIVO,
|
|
||||||
t.ANOS_ATUACAO = s.ANOS_ATUACAO,
|
|
||||||
t.DT_CALCULO = CURRENT_TIMESTAMP,
|
|
||||||
t.JSON_DETALHES = s.JSON_DETALHES
|
|
||||||
WHEN NOT MATCHED THEN
|
|
||||||
INSERT (
|
|
||||||
ID_PESSOA, NOME, PONTUACAO_TOTAL,
|
ID_PESSOA, NOME, PONTUACAO_TOTAL,
|
||||||
COMPONENTE_A, COMPONENTE_B, COMPONENTE_C, COMPONENTE_D,
|
COMPONENTE_A, COMPONENTE_B, COMPONENTE_C, COMPONENTE_D,
|
||||||
ATIVO, ANOS_ATUACAO, JSON_DETALHES, DT_CALCULO
|
ATIVO, ANOS_ATUACAO, JSON_DETALHES, DT_CALCULO
|
||||||
)
|
) VALUES (
|
||||||
VALUES (
|
:id_pessoa, :nome, :pontuacao_total,
|
||||||
s.ID_PESSOA, s.NOME, s.PONTUACAO_TOTAL,
|
:componente_a, :componente_b, :componente_c, :componente_d,
|
||||||
s.COMPONENTE_A, s.COMPONENTE_B, s.COMPONENTE_C, s.COMPONENTE_D,
|
:ativo, :anos_atuacao, :json_detalhes, CURRENT_TIMESTAMP
|
||||||
s.ATIVO, s.ANOS_ATUACAO, s.JSON_DETALHES, CURRENT_TIMESTAMP
|
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
batch_data = []
|
||||||
|
for consultor in consultores:
|
||||||
|
json_str = json.dumps(consultor, ensure_ascii=False)
|
||||||
|
batch_data.append({
|
||||||
|
"id_pessoa": int(consultor["id_pessoa"]),
|
||||||
|
"nome": str(consultor.get("nome", ""))[:500],
|
||||||
|
"pontuacao_total": int(consultor.get("pontuacao_total") or 0),
|
||||||
|
"componente_a": int(consultor.get("bloco_a") or consultor.get("componente_a") or 0),
|
||||||
|
"componente_b": int(consultor.get("bloco_b") or consultor.get("componente_b") or 0),
|
||||||
|
"componente_c": int(consultor.get("bloco_c") or consultor.get("componente_c") or 0),
|
||||||
|
"componente_d": int(consultor.get("bloco_d") or consultor.get("componente_d") or 0),
|
||||||
|
"ativo": "S" if consultor.get("ativo") else "N",
|
||||||
|
"anos_atuacao": float(consultor.get("anos_atuacao") or 0),
|
||||||
|
"json_detalhes": json_str
|
||||||
|
})
|
||||||
|
|
||||||
with self.client.get_connection() as conn:
|
with self.client.get_connection() as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
try:
|
try:
|
||||||
for consultor in consultores:
|
|
||||||
json_str = json.dumps(consultor, ensure_ascii=False)
|
|
||||||
cursor.setinputsizes(json_detalhes=oracledb.DB_TYPE_CLOB)
|
cursor.setinputsizes(json_detalhes=oracledb.DB_TYPE_CLOB)
|
||||||
params = {
|
cursor.executemany(insert_sql, batch_data)
|
||||||
"id_pessoa": consultor["id_pessoa"],
|
|
||||||
"nome": consultor["nome"],
|
|
||||||
"pontuacao_total": consultor["pontuacao_total"],
|
|
||||||
"componente_a": consultor.get("bloco_a") or consultor.get("componente_a", 0),
|
|
||||||
"componente_b": consultor.get("bloco_b") or consultor.get("componente_b", 0),
|
|
||||||
"componente_c": consultor.get("bloco_c") or consultor.get("componente_c", 0),
|
|
||||||
"componente_d": consultor.get("bloco_d") or consultor.get("componente_d", 0),
|
|
||||||
"ativo": "S" if consultor.get("ativo") else "N",
|
|
||||||
"anos_atuacao": consultor.get("anos_atuacao", 0),
|
|
||||||
"json_detalhes": json_str
|
|
||||||
}
|
|
||||||
cursor.execute(merge_sql, params)
|
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return len(consultores)
|
return len(consultores)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -372,14 +343,18 @@ class RankingOracleRepository:
|
|||||||
"""
|
"""
|
||||||
Limpa todos os registros da tabela de ranking.
|
Limpa todos os registros da tabela de ranking.
|
||||||
Usar apenas quando for reprocessar do zero.
|
Usar apenas quando for reprocessar do zero.
|
||||||
|
TRUNCATE é muito mais rápido que DELETE para grandes volumes.
|
||||||
"""
|
"""
|
||||||
with self.client.get_connection() as conn:
|
with self.client.get_connection() as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
try:
|
||||||
|
cursor.execute("TRUNCATE TABLE TB_RANKING_CONSULTOR")
|
||||||
|
except Exception as e:
|
||||||
try:
|
try:
|
||||||
cursor.execute("DELETE FROM TB_RANKING_CONSULTOR")
|
cursor.execute("DELETE FROM TB_RANKING_CONSULTOR")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
except Exception as e:
|
except Exception as e2:
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
raise RuntimeError(f"Erro ao limpar tabela: {e}")
|
raise RuntimeError(f"Erro ao limpar tabela: {e2}")
|
||||||
finally:
|
finally:
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from ...domain.entities.consultor import (
|
|||||||
Participacao,
|
Participacao,
|
||||||
Orientacao,
|
Orientacao,
|
||||||
MembroBanca,
|
MembroBanca,
|
||||||
|
DocenciaPPG,
|
||||||
)
|
)
|
||||||
from ...domain.repositories.consultor_repository import ConsultorRepository
|
from ...domain.repositories.consultor_repository import ConsultorRepository
|
||||||
from ...domain.services.calculador_pontuacao import CalculadorPontuacao
|
from ...domain.services.calculador_pontuacao import CalculadorPontuacao
|
||||||
@@ -554,6 +555,51 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
|||||||
|
|
||||||
return membros
|
return membros
|
||||||
|
|
||||||
|
def _extrair_docencias(self, atuacoes: List[Dict[str, Any]]) -> List[DocenciaPPG]:
|
||||||
|
docencias = []
|
||||||
|
|
||||||
|
for a in atuacoes:
|
||||||
|
if a.get("tipo") != "Docência":
|
||||||
|
continue
|
||||||
|
|
||||||
|
dados = a.get("dadosDocencia", {}) or {}
|
||||||
|
if not dados:
|
||||||
|
continue
|
||||||
|
|
||||||
|
programa_data = dados.get("programa", {}) or {}
|
||||||
|
ies_data = dados.get("ies", {}) or {}
|
||||||
|
area_data = dados.get("areaConhecimento", {}) or {}
|
||||||
|
area_aval_data = area_data.get("areaAvaliacao", {}) or {}
|
||||||
|
|
||||||
|
programa = programa_data.get("nome", "") or a.get("descricao", "")
|
||||||
|
codigo_programa = programa_data.get("codigo", "")
|
||||||
|
if not programa and not codigo_programa:
|
||||||
|
continue
|
||||||
|
|
||||||
|
inicio = self._parse_date(a.get("inicio"))
|
||||||
|
fim = self._parse_date(a.get("fim"))
|
||||||
|
|
||||||
|
linhas = []
|
||||||
|
for lp in dados.get("linhaPesquisa", []) or []:
|
||||||
|
if lp.get("nome"):
|
||||||
|
linhas.append(lp["nome"])
|
||||||
|
|
||||||
|
docencias.append(DocenciaPPG(
|
||||||
|
programa=programa,
|
||||||
|
codigo_programa=codigo_programa,
|
||||||
|
ies_sigla=ies_data.get("sigla", ""),
|
||||||
|
ies_nome=ies_data.get("nome", ""),
|
||||||
|
categoria=dados.get("categoria", ""),
|
||||||
|
area_avaliacao=area_aval_data.get("nome", "") or area_data.get("nome", ""),
|
||||||
|
modalidade=programa_data.get("modalidade", ""),
|
||||||
|
periodo=Periodo(inicio=inicio, fim=fim),
|
||||||
|
carga_horaria=dados.get("cargaHoraria"),
|
||||||
|
linhas_pesquisa=linhas[:3],
|
||||||
|
))
|
||||||
|
|
||||||
|
docencias.sort(key=lambda d: (d.periodo.fim is not None, d.periodo.inicio or datetime.min), reverse=True)
|
||||||
|
return docencias
|
||||||
|
|
||||||
async def _construir_consultor(self, doc: Dict[str, Any]) -> Consultor:
|
async def _construir_consultor(self, doc: Dict[str, Any]) -> Consultor:
|
||||||
id_pessoa = doc["id"]
|
id_pessoa = doc["id"]
|
||||||
dados_pessoais = doc.get("dadosPessoais", {})
|
dados_pessoais = doc.get("dadosPessoais", {})
|
||||||
@@ -569,6 +615,7 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
|||||||
orientacoes = self._extrair_orientacoes(atuacoes)
|
orientacoes = self._extrair_orientacoes(atuacoes)
|
||||||
coorientacoes = self._extrair_coorientacoes(atuacoes)
|
coorientacoes = self._extrair_coorientacoes(atuacoes)
|
||||||
membros_banca = self._extrair_membros_banca(atuacoes)
|
membros_banca = self._extrair_membros_banca(atuacoes)
|
||||||
|
docencias = self._extrair_docencias(atuacoes)
|
||||||
coordenador_ppg = self._tem_coordenacao_ppg(atuacoes)
|
coordenador_ppg = self._tem_coordenacao_ppg(atuacoes)
|
||||||
|
|
||||||
consultor = Consultor(
|
consultor = Consultor(
|
||||||
@@ -585,6 +632,7 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
|||||||
participacoes=participacoes,
|
participacoes=participacoes,
|
||||||
orientacoes=orientacoes + coorientacoes,
|
orientacoes=orientacoes + coorientacoes,
|
||||||
membros_banca=membros_banca,
|
membros_banca=membros_banca,
|
||||||
|
docencias=docencias,
|
||||||
)
|
)
|
||||||
|
|
||||||
consultor.pontuacao = self.calculador.calcular_pontuacao_completa(consultor)
|
consultor.pontuacao = self.calculador.calcular_pontuacao_completa(consultor)
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ async def ranking_paginado(
|
|||||||
participacoes=d.get("participacoes"),
|
participacoes=d.get("participacoes"),
|
||||||
orientacoes=d.get("orientacoes"),
|
orientacoes=d.get("orientacoes"),
|
||||||
membros_banca=d.get("membros_banca"),
|
membros_banca=d.get("membros_banca"),
|
||||||
|
docencias=d.get("docencias"),
|
||||||
pontuacao=d.get("pontuacao"),
|
pontuacao=d.get("pontuacao"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class ConsultorRankingResumoSchema(BaseModel):
|
|||||||
participacoes: Optional[list] = None
|
participacoes: Optional[list] = None
|
||||||
orientacoes: Optional[list] = None
|
orientacoes: Optional[list] = None
|
||||||
membros_banca: Optional[list] = None
|
membros_banca: Optional[list] = None
|
||||||
|
docencias: Optional[list] = None
|
||||||
pontuacao: Optional[dict] = None
|
pontuacao: Optional[dict] = None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -107,15 +107,13 @@ const CompararModal = ({ consultor1, consultor2, onClose }) => {
|
|||||||
|
|
||||||
const blocoA1 = p1.bloco_a || { total: consultor1.bloco_a || 0 };
|
const blocoA1 = p1.bloco_a || { total: consultor1.bloco_a || 0 };
|
||||||
const blocoA2 = p2.bloco_a || { total: consultor2.bloco_a || 0 };
|
const blocoA2 = p2.bloco_a || { total: consultor2.bloco_a || 0 };
|
||||||
const blocoB1 = p1.bloco_b || { total: consultor1.bloco_b || 0 };
|
|
||||||
const blocoB2 = p2.bloco_b || { total: consultor2.bloco_b || 0 };
|
|
||||||
const blocoC1 = p1.bloco_c || { total: consultor1.bloco_c || 0 };
|
const blocoC1 = p1.bloco_c || { total: consultor1.bloco_c || 0 };
|
||||||
const blocoC2 = p2.bloco_c || { total: consultor2.bloco_c || 0 };
|
const blocoC2 = p2.bloco_c || { total: consultor2.bloco_c || 0 };
|
||||||
const blocoD1 = p1.bloco_d || { total: consultor1.bloco_d || 0 };
|
const blocoD1 = p1.bloco_d || { total: consultor1.bloco_d || 0 };
|
||||||
const blocoD2 = p2.bloco_d || { total: consultor2.bloco_d || 0 };
|
const blocoD2 = p2.bloco_d || { total: consultor2.bloco_d || 0 };
|
||||||
|
|
||||||
const total1 = (blocoA1.total || 0) + (blocoB1.total || 0) + (blocoC1.total || 0) + (blocoD1.total || 0);
|
const total1 = Number(consultor1.pontuacao_total ?? 0);
|
||||||
const total2 = (blocoA2.total || 0) + (blocoB2.total || 0) + (blocoC2.total || 0) + (blocoD2.total || 0);
|
const total2 = Number(consultor2.pontuacao_total ?? 0);
|
||||||
|
|
||||||
const c1 = consultor1.consultoria;
|
const c1 = consultor1.consultoria;
|
||||||
const c2 = consultor2.consultoria;
|
const c2 = consultor2.consultoria;
|
||||||
@@ -193,11 +191,6 @@ const CompararModal = ({ consultor1, consultor2, onClose }) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="comparacao-secao">
|
|
||||||
<h3 style={{ color: 'var(--accent)' }}>B - Coordenacao PPG</h3>
|
|
||||||
{renderLinhaComparacao('Total', blocoB1.total, blocoB2.total, 'var(--accent)')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="comparacao-secao">
|
<div className="comparacao-secao">
|
||||||
<h3 style={{ color: 'var(--gold)' }}>C - Consultoria</h3>
|
<h3 style={{ color: 'var(--gold)' }}>C - Consultoria</h3>
|
||||||
{renderLinhaComparacao('Total', blocoC1.total, blocoC2.total, 'var(--gold)')}
|
{renderLinhaComparacao('Total', blocoC1.total, blocoC2.total, 'var(--gold)')}
|
||||||
|
|||||||
@@ -1010,6 +1010,12 @@
|
|||||||
color: #f9a8d4;
|
color: #f9a8d4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tipo-docente {
|
||||||
|
background: linear-gradient(135deg, rgba(20, 184, 166, 0.2), rgba(20, 184, 166, 0.08));
|
||||||
|
border-color: rgba(20, 184, 166, 0.35);
|
||||||
|
color: #5eead4;
|
||||||
|
}
|
||||||
|
|
||||||
.tipo-default {
|
.tipo-default {
|
||||||
background: rgba(148, 163, 184, 0.12);
|
background: rgba(148, 163, 184, 0.12);
|
||||||
border-color: rgba(148, 163, 184, 0.25);
|
border-color: rgba(148, 163, 184, 0.25);
|
||||||
|
|||||||
@@ -818,8 +818,8 @@ const ItemDetalheModal = ({ item, tipo, onClose }) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="modal-detalhe-row">
|
<div className="modal-detalhe-row">
|
||||||
<span className="modal-detalhe-label">Pontuação</span>
|
<span className="modal-detalhe-label">Pontuação Base</span>
|
||||||
<span className="modal-detalhe-value muted">Apenas selo (sem pontuação)</span>
|
<span className="modal-detalhe-value pontos">{PONTOS_BASE[item.codigo] || 0} pts</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -853,17 +853,13 @@ const FORMULAS = {
|
|||||||
titulo: 'Coordenacao CAPES',
|
titulo: 'Coordenacao CAPES',
|
||||||
descricao: 'CA=200 | CAJ=150 | CAJ_MP=120 | CAM=100\nTempo: multiplicador por ano (anos completos)\nBônus atualidade (mandato vigente) + Retorno (mandato anterior)',
|
descricao: 'CA=200 | CAJ=150 | CAJ_MP=120 | CAM=100\nTempo: multiplicador por ano (anos completos)\nBônus atualidade (mandato vigente) + Retorno (mandato anterior)',
|
||||||
},
|
},
|
||||||
bloco_b: {
|
|
||||||
titulo: 'Coordenacao PPG',
|
|
||||||
descricao: 'Reservado no V1: PPG_COORD base=0 | teto=0 (dados incompletos no ATUACAPES para pontuar).',
|
|
||||||
},
|
|
||||||
bloco_c: {
|
bloco_c: {
|
||||||
titulo: 'Consultoria',
|
titulo: 'Consultoria',
|
||||||
descricao: 'CONS_ATIVO=150 | CONS_HIST=100 | CONS_FALECIDO=100\nTempo: 5 pts/ano (max 50)\nContinuidade: 3a=+5, 5a=+10, 8a+=+20 (escalonado)\nRetorno (reativação): +15 (uma vez)',
|
descricao: 'CONS_ATIVO=150 | CONS_HIST=100 | CONS_FALECIDO=100\nTempo: 5 pts/ano (max 50)\nContinuidade: 3a=+5, 5a=+10, 8a+=+20 (escalonado)\nRetorno (reativação): +15 (uma vez)',
|
||||||
},
|
},
|
||||||
bloco_d: {
|
bloco_d: {
|
||||||
titulo: 'Premiacoes/Avaliacoes',
|
titulo: 'Premiacoes/Avaliacoes',
|
||||||
descricao: 'Premiações: GP=100 (teto 300) | Prêmio=50 (teto 150) | Menção=30 (teto 90)\nBolsas: BPQ=30 (teto 60)\nInscrições/Avaliações/Comissões/Participações (com tetos por código)\nOrientações/Bancas: apenas selos (0 pts)',
|
descricao: 'Premiações: GP=100 (teto 300) | Prêmio=50 (teto 150) | Menção=30 (teto 90)\nBolsas: BPQ=30 (teto 60)\nInscrições/Avaliações/Comissões/Participações (com tetos por código)\nOrientações e Bancas: apenas selos (sem pontuação)',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -876,9 +872,6 @@ const PONTOS_BASE = {
|
|||||||
PREMIACAO_GP_AUTOR: 100, PREMIACAO_AUTOR: 50, MENCAO_AUTOR: 30,
|
PREMIACAO_GP_AUTOR: 100, PREMIACAO_AUTOR: 50, MENCAO_AUTOR: 30,
|
||||||
BOL_BPQ_NIVEL: 30,
|
BOL_BPQ_NIVEL: 30,
|
||||||
EVENTO: 1, PROJ: 10,
|
EVENTO: 1, PROJ: 10,
|
||||||
ORIENT_POS_DOC: 0, ORIENT_TESE: 0, ORIENT_DISS: 0,
|
|
||||||
CO_ORIENT_POS_DOC: 0, CO_ORIENT_TESE: 0, CO_ORIENT_DISS: 0,
|
|
||||||
MB_BANCA_POS_DOC: 0, MB_BANCA_TESE: 0, MB_BANCA_DISS: 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const TETOS = {
|
const TETOS = {
|
||||||
@@ -894,15 +887,6 @@ const TETOS = {
|
|||||||
EVENTO: { teto: 5, doc: '3.5 Participações Acadêmicas', bonus: '+1/participação (max 10)' },
|
EVENTO: { teto: 5, doc: '3.5 Participações Acadêmicas', bonus: '+1/participação (max 10)' },
|
||||||
PROJ: { teto: 30, doc: '3.5 Participações Acadêmicas', bonus: '+2/participação (max 10)' },
|
PROJ: { teto: 30, doc: '3.5 Participações Acadêmicas', bonus: '+2/participação (max 10)' },
|
||||||
BOL_BPQ_NIVEL: { teto: 60, doc: '3.4 Premiações e Bolsas' },
|
BOL_BPQ_NIVEL: { teto: 60, doc: '3.4 Premiações e Bolsas' },
|
||||||
ORIENT_POS_DOC: { teto: 0, doc: 'Selo (sem pontuação)' },
|
|
||||||
ORIENT_TESE: { teto: 0, doc: 'Selo (sem pontuação)' },
|
|
||||||
ORIENT_DISS: { teto: 0, doc: 'Selo (sem pontuação)' },
|
|
||||||
CO_ORIENT_POS_DOC: { teto: 0, doc: 'Selo (sem pontuação)' },
|
|
||||||
CO_ORIENT_TESE: { teto: 0, doc: 'Selo (sem pontuação)' },
|
|
||||||
CO_ORIENT_DISS: { teto: 0, doc: 'Selo (sem pontuação)' },
|
|
||||||
MB_BANCA_POS_DOC: { teto: 0, doc: 'Selo (sem pontuação)' },
|
|
||||||
MB_BANCA_TESE: { teto: 0, doc: 'Selo (sem pontuação)' },
|
|
||||||
MB_BANCA_DISS: { teto: 0, doc: 'Selo (sem pontuação)' },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const PontuacaoModal = ({ dados, onClose }) => {
|
const PontuacaoModal = ({ dados, onClose }) => {
|
||||||
@@ -1054,7 +1038,7 @@ const PontuacaoModal = ({ dados, onClose }) => {
|
|||||||
<div className="modal-formula-section">
|
<div className="modal-formula-section">
|
||||||
<span className="modal-detalhe-label">Fórmula</span>
|
<span className="modal-detalhe-label">Fórmula</span>
|
||||||
<div className="modal-formula-box">
|
<div className="modal-formula-box">
|
||||||
<div className="modal-formula-line">Bloco A + Bloco B + Bloco C + Bloco D</div>
|
<div className="modal-formula-line">Bloco A + Bloco C + Bloco D</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1132,10 +1116,9 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
|
|
||||||
const { consultoria, pontuacao } = consultor;
|
const { consultoria, pontuacao } = consultor;
|
||||||
const blocoA = pontuacao?.bloco_a || { total: consultor.bloco_a || 0 };
|
const blocoA = pontuacao?.bloco_a || { total: consultor.bloco_a || 0 };
|
||||||
const blocoB = pontuacao?.bloco_b || { total: consultor.bloco_b || 0 };
|
|
||||||
const blocoC = pontuacao?.bloco_c || { total: consultor.bloco_c || 0 };
|
const blocoC = pontuacao?.bloco_c || { total: consultor.bloco_c || 0 };
|
||||||
const blocoD = pontuacao?.bloco_d || { total: consultor.bloco_d || 0 };
|
const blocoD = pontuacao?.bloco_d || { total: consultor.bloco_d || 0 };
|
||||||
const pontuacaoTotal = (blocoA.total || 0) + (blocoB.total || 0) + (blocoC.total || 0) + (blocoD.total || 0);
|
const pontuacaoTotal = Number(consultor.pontuacao_total ?? 0);
|
||||||
|
|
||||||
const selos = useMemo(() => gerarSelos(consultor), [consultor]);
|
const selos = useMemo(() => gerarSelos(consultor), [consultor]);
|
||||||
|
|
||||||
@@ -1246,18 +1229,6 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
bloco: blocoA
|
bloco: blocoA
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<ScoreItemClickable
|
|
||||||
value={blocoB.total}
|
|
||||||
label="BLOCO B"
|
|
||||||
style={{ color: blocoB.total > 0 ? 'var(--accent)' : 'var(--muted)' }}
|
|
||||||
onClick={() => setPontuacaoModal({
|
|
||||||
tipo: 'bloco',
|
|
||||||
label: 'BLOCO B - Coordenação PPG',
|
|
||||||
value: blocoB.total,
|
|
||||||
formula: FORMULAS.bloco_b.descricao,
|
|
||||||
bloco: blocoB
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<ScoreItemClickable
|
<ScoreItemClickable
|
||||||
value={blocoC.total}
|
value={blocoC.total}
|
||||||
label="BLOCO C"
|
label="BLOCO C"
|
||||||
@@ -1299,10 +1270,6 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
<BlocoDetalhes titulo="A - Coordenacao CAPES" bloco={blocoA} cor="var(--accent-2)" onItemClick={setPontuacaoModal} />
|
<BlocoDetalhes titulo="A - Coordenacao CAPES" bloco={blocoA} cor="var(--accent-2)" onItemClick={setPontuacaoModal} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(blocoB.total > 0 || (blocoB.atuacoes && blocoB.atuacoes.length > 0)) && (
|
|
||||||
<BlocoDetalhes titulo="B - Coordenacao PPG" bloco={blocoB} cor="var(--accent)" onItemClick={setPontuacaoModal} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{blocoC.atuacoes && blocoC.atuacoes.length > 0 && (
|
{blocoC.atuacoes && blocoC.atuacoes.length > 0 && (
|
||||||
<BlocoDetalhes titulo="C - Consultoria" bloco={blocoC} cor="var(--gold)" onItemClick={setPontuacaoModal} />
|
<BlocoDetalhes titulo="C - Consultoria" bloco={blocoC} cor="var(--gold)" onItemClick={setPontuacaoModal} />
|
||||||
)}
|
)}
|
||||||
@@ -1571,11 +1538,23 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
|
|
||||||
ConsultorCard.displayName = 'ConsultorCard';
|
ConsultorCard.displayName = 'ConsultorCard';
|
||||||
|
|
||||||
const BlocoDetalhes = memo(({ titulo, bloco, cor, onItemClick }) => (
|
const CODIGOS_APENAS_SELO = [
|
||||||
|
'ORIENT_POS_DOC', 'ORIENT_TESE', 'ORIENT_DISS',
|
||||||
|
'CO_ORIENT_POS_DOC', 'CO_ORIENT_TESE', 'CO_ORIENT_DISS',
|
||||||
|
'MB_BANCA_POS_DOC', 'MB_BANCA_TESE', 'MB_BANCA_DISS',
|
||||||
|
'ORIENT_POS_DOC_PREM', 'ORIENT_TESE_PREM', 'ORIENT_DISS_PREM',
|
||||||
|
'CO_ORIENT_POS_DOC_PREM', 'CO_ORIENT_TESE_PREM', 'CO_ORIENT_DISS_PREM',
|
||||||
|
'MB_BANCA_POS_DOC_PREM', 'MB_BANCA_TESE_PREM', 'MB_BANCA_DISS_PREM',
|
||||||
|
];
|
||||||
|
|
||||||
|
const BlocoDetalhes = memo(({ titulo, bloco, cor, onItemClick }) => {
|
||||||
|
const atuacoesFiltradas = bloco.atuacoes?.filter(at => !CODIGOS_APENAS_SELO.includes(at.codigo)) || [];
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="detail-section">
|
<div className="detail-section">
|
||||||
<h4 style={{ color: cor }}>{titulo}</h4>
|
<h4 style={{ color: cor }}>{titulo}</h4>
|
||||||
<div className="score-breakdown">
|
<div className="score-breakdown">
|
||||||
{bloco.atuacoes?.map((at, idx) => (
|
{atuacoesFiltradas.map((at, idx) => (
|
||||||
<div
|
<div
|
||||||
key={idx}
|
key={idx}
|
||||||
className="score-item-wrapper score-item-clicavel"
|
className="score-item-wrapper score-item-clicavel"
|
||||||
@@ -1608,7 +1587,8 @@ const BlocoDetalhes = memo(({ titulo, bloco, cor, onItemClick }) => (
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
));
|
);
|
||||||
|
});
|
||||||
|
|
||||||
BlocoDetalhes.displayName = 'BlocoDetalhes';
|
BlocoDetalhes.displayName = 'BlocoDetalhes';
|
||||||
|
|
||||||
|
|||||||
@@ -64,17 +64,9 @@ const Header = ({ total }) => {
|
|||||||
<div className="criteria-section bloco-e">
|
<div className="criteria-section bloco-e">
|
||||||
<div className="section-header">
|
<div className="section-header">
|
||||||
<h4>E - Coord. PPG</h4>
|
<h4>E - Coord. PPG</h4>
|
||||||
<span className="max-pts">V2</span>
|
<span className="max-pts">indicador</span>
|
||||||
</div>
|
</div>
|
||||||
<table className="criteria-table">
|
<div className="criteria-note">Nao entra no score (apenas selo/indicador no perfil)</div>
|
||||||
<thead>
|
|
||||||
<tr><th>Cod</th><th>Base</th><th>Teto</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>PPG_COORD</td><td>0</td><td>0</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div className="criteria-note">Dados incompletos no V1</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user