Adiciona suporte ao Bloco B (Coordenacao PPG) e melhora tooltips

- Adiciona Bloco B no ConsultorCard com formula e detalhes
- Ajusta calculo de pontuacao total para incluir Bloco B
- Melhora tooltips com informacoes de teto e calculo detalhado
- Ajusta routes.py para preencher blocos corretamente
- Adiciona bloco_b no schema de ranking
This commit is contained in:
Frederico Castro
2025-12-14 20:31:47 -03:00
parent 0707c14e05
commit 7000268261
6 changed files with 112 additions and 11 deletions

View File

@@ -157,14 +157,44 @@ def _consultor_resumo_from_ranking(c):
except Exception: except Exception:
pass pass
# Ajusta pontuação detalhada para refletir os valores atuais do ranking (incluindo COMPONENTE_B),
# já que o JSON pode ter sido gerado antes do job de preenchimento do Componente B.
pontuacao_total = float(c.pontuacao_total or 0)
bloco_a = float(c.componente_a or 0)
bloco_b = float(c.componente_b or 0)
bloco_c = float(c.componente_c or 0)
bloco_d = float(c.componente_d or 0)
if isinstance(pontuacao, dict):
pontuacao_ajustada = dict(pontuacao)
else:
pontuacao_ajustada = {}
def _ajustar_bloco(chave: str, total: float, letra: str):
b = pontuacao_ajustada.get(chave)
if isinstance(b, dict):
b2 = dict(b)
b2["bloco"] = letra
b2["total"] = total
pontuacao_ajustada[chave] = b2
else:
pontuacao_ajustada[chave] = {"bloco": letra, "total": total, "atuacoes": []}
_ajustar_bloco("bloco_a", bloco_a, "A")
_ajustar_bloco("bloco_b", bloco_b, "B")
_ajustar_bloco("bloco_c", bloco_c, "C")
_ajustar_bloco("bloco_d", bloco_d, "D")
pontuacao_ajustada["pontuacao_total"] = pontuacao_total
return ConsultorRankingResumoSchema( return ConsultorRankingResumoSchema(
id_pessoa=c.id_pessoa, id_pessoa=c.id_pessoa,
nome=c.nome, nome=c.nome,
posicao=c.posicao, posicao=c.posicao,
pontuacao_total=c.pontuacao_total, pontuacao_total=pontuacao_total,
bloco_a=c.componente_a, bloco_a=bloco_a,
bloco_c=c.componente_c, bloco_b=bloco_b,
bloco_d=c.componente_d, bloco_c=bloco_c,
bloco_d=bloco_d,
ativo=c.ativo, ativo=c.ativo,
anos_atuacao=c.anos_atuacao, anos_atuacao=c.anos_atuacao,
consultoria=consultoria, consultoria=consultoria,
@@ -176,7 +206,7 @@ def _consultor_resumo_from_ranking(c):
participacoes=participacoes, participacoes=participacoes,
orientacoes=orientacoes, orientacoes=orientacoes,
membros_banca=membros_banca, membros_banca=membros_banca,
pontuacao=pontuacao, pontuacao=pontuacao_ajustada if pontuacao_ajustada else None,
) )

View File

@@ -9,6 +9,7 @@ class ConsultorRankingResumoSchema(BaseModel):
posicao: Optional[int] posicao: Optional[int]
pontuacao_total: float pontuacao_total: float
bloco_a: float bloco_a: float
bloco_b: float
bloco_c: float bloco_c: float
bloco_d: float bloco_d: float
ativo: bool ativo: bool

View File

@@ -8,6 +8,8 @@ services:
- "8010:8000" - "8010:8000"
env_file: env_file:
- ./backend/.env - ./backend/.env
extra_hosts:
- "host.docker.internal:host-gateway"
environment: environment:
- API_HOST=0.0.0.0 - API_HOST=0.0.0.0
- API_PORT=8000 - API_PORT=8000

View File

@@ -35,13 +35,15 @@ 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 = p1.pontuacao_total || consultor1.pontuacao_total || 0; const total1 = (blocoA1.total || 0) + (blocoB1.total || 0) + (blocoC1.total || 0) + (blocoD1.total || 0);
const total2 = p2.pontuacao_total || consultor2.pontuacao_total || 0; const total2 = (blocoA2.total || 0) + (blocoB2.total || 0) + (blocoC2.total || 0) + (blocoD2.total || 0);
const c1 = consultor1.consultoria; const c1 = consultor1.consultoria;
const c2 = consultor2.consultoria; const c2 = consultor2.consultoria;
@@ -97,6 +99,11 @@ 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)')}

View File

@@ -6,6 +6,10 @@ const FORMULAS = {
titulo: 'Coordenacao CAPES', titulo: 'Coordenacao CAPES',
descricao: 'CA=200 | CAJ=150 | CAJ_MP=120 | CAM=100\nTempo: multiplicador por ano\nBonus atualidade + Retorno', descricao: 'CA=200 | CAJ=150 | CAJ_MP=120 | CAM=100\nTempo: multiplicador por ano\nBonus atualidade + Retorno',
}, },
bloco_b: {
titulo: 'Coordenacao PPG',
descricao: 'Base=70 | Tempo=5 pts/ano (max 50)\nExtras por programas distintos (max 40)\nBonus por maior nota do programa (max 20)',
},
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 8a+=15 | Retorno=15', descricao: 'CONS_ATIVO=150 | CONS_HIST=100 | CONS_FALECIDO=100\nTempo: 5 pts/ano (max 50)\nContinuidade 8a+=15 | Retorno=15',
@@ -30,6 +34,28 @@ const PONTOS_BASE = {
MB_BANCA_POS_DOC: 3, MB_BANCA_TESE: 3, MB_BANCA_DISS: 2, MB_BANCA_POS_DOC: 3, MB_BANCA_TESE: 3, MB_BANCA_DISS: 2,
}; };
// Teto oficial por código, conforme documento de critérios (seção 3.x)
const TETOS = {
// 3.3 Inscrições
INSC_AUTOR: { teto: 20, doc: '3.3 Inscrições' },
INSC_INST: { teto: 60, doc: '3.3 Inscrições' },
// 3.4 Avaliação / Coordenação de Comissão
AVAL_COMIS_PREMIO: { teto: 60, doc: '3.4 Avaliação/Comissão' },
AVAL_COMIS_GP: { teto: 100, doc: '3.4 Avaliação/Comissão' },
COORD_COMIS_PREMIO: { teto: 100, doc: '3.4 Avaliação/Comissão' },
COORD_COMIS_GP: { teto: 120, doc: '3.4 Avaliação/Comissão' },
// 3.6 Premiações
PREMIACAO: { teto: 180, doc: '3.6 Premiações' },
PREMIACAO_GP: { teto: 60, doc: '3.6 Premiações' },
MENCAO: { teto: 20, doc: '3.6 Premiações' },
// 3.7 Participações
EVENTO: { teto: 5, doc: '3.7 Participações' },
PROJ: { teto: 40, doc: '3.7 Participações' },
// 3.5 Bolsas CNPQ (nomes resumidos)
BOL_BPQ_SUPERIOR: { teto: 60, doc: '3.5 Bolsas CNPQ' },
BOL_BPQ_INTERMEDIARIO: { teto: 100, doc: '3.5 Bolsas CNPQ' },
};
const ScoreItemWithTooltip = ({ value, label, formula, style }) => ( const ScoreItemWithTooltip = ({ value, label, formula, style }) => (
<div className="score-item-wrapper"> <div className="score-item-wrapper">
<div className="score-item" style={style}> <div className="score-item" style={style}>
@@ -71,9 +97,10 @@ const ConsultorCard = ({ consultor, highlight, selecionado, onToggleSelecionado
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 = pontuacao?.pontuacao_total || consultor.pontuacao_total || 0; const pontuacaoTotal = (blocoA.total || 0) + (blocoB.total || 0) + (blocoC.total || 0) + (blocoD.total || 0);
return ( return (
<div ref={cardRef} className={`ranking-card ${expanded ? 'expanded' : ''} ${highlight ? 'highlight' : ''} ${selecionado ? 'selecionado' : ''}`} onClick={() => setExpanded(!expanded)}> <div ref={cardRef} className={`ranking-card ${expanded ? 'expanded' : ''} ${highlight ? 'highlight' : ''} ${selecionado ? 'selecionado' : ''}`} onClick={() => setExpanded(!expanded)}>
@@ -134,6 +161,12 @@ const ConsultorCard = ({ consultor, highlight, selecionado, onToggleSelecionado
formula={FORMULAS.bloco_a.descricao} formula={FORMULAS.bloco_a.descricao}
style={{ color: blocoA.total > 0 ? 'var(--accent-2)' : 'var(--muted)' }} style={{ color: blocoA.total > 0 ? 'var(--accent-2)' : 'var(--muted)' }}
/> />
<ScoreItemWithTooltip
value={blocoB.total}
label="BLOCO B"
formula={FORMULAS.bloco_b.descricao}
style={{ color: blocoB.total > 0 ? 'var(--accent)' : 'var(--muted)' }}
/>
<ScoreItemWithTooltip <ScoreItemWithTooltip
value={blocoC.total} value={blocoC.total}
label="BLOCO C" label="BLOCO C"
@@ -151,7 +184,7 @@ const ConsultorCard = ({ consultor, highlight, selecionado, onToggleSelecionado
<div className="score-item-value">{pontuacaoTotal}</div> <div className="score-item-value">{pontuacaoTotal}</div>
<div className="score-item-label">TOTAL</div> <div className="score-item-label">TOTAL</div>
</div> </div>
<div className="score-tooltip">Bloco A + Bloco C + Bloco D</div> <div className="score-tooltip">Bloco A + Bloco B + Bloco C + Bloco D</div>
</div> </div>
</div> </div>
</div> </div>
@@ -160,6 +193,10 @@ const ConsultorCard = ({ consultor, highlight, selecionado, onToggleSelecionado
<BlocoDetalhes titulo="A - Coordenacao CAPES" bloco={blocoA} cor="var(--accent-2)" /> <BlocoDetalhes titulo="A - Coordenacao CAPES" bloco={blocoA} cor="var(--accent-2)" />
)} )}
{(blocoB.total > 0 || (blocoB.atuacoes && blocoB.atuacoes.length > 0)) && (
<BlocoDetalhes titulo="B - Coordenacao PPG" bloco={blocoB} cor="var(--accent)" />
)}
{blocoC.atuacoes && blocoC.atuacoes.length > 0 && ( {blocoC.atuacoes && blocoC.atuacoes.length > 0 && (
<BlocoDetalhes titulo="C - Consultoria" bloco={blocoC} cor="var(--gold)" /> <BlocoDetalhes titulo="C - Consultoria" bloco={blocoC} cor="var(--gold)" />
)} )}
@@ -270,8 +307,30 @@ const BlocoDetalhes = ({ titulo, bloco, cor }) => (
<div className="score-item-label">{at.codigo}</div> <div className="score-item-label">{at.codigo}</div>
</div> </div>
<div className="score-tooltip"> <div className="score-tooltip">
Base: {at.base} | Tempo: {at.tempo} | Bonus: {at.bonus} {(() => {
{at.quantidade > 1 && ` | Qtd: ${at.quantidade}`} const base = at.base || 0;
const tempo = at.tempo || 0;
const bonus = at.bonus || 0;
const bruto = base + tempo + bonus;
const meta = TETOS[at.codigo];
const capped = bruto !== at.total;
const unidade = at.quantidade > 1 ? Math.round(base / at.quantidade) : null;
const partes = [];
partes.push(
unidade
? `Base ${unidade} x ${at.quantidade} = ${base}`
: `Base ${base}`
);
if (capped) {
partes.push(`Bruto ${bruto}`);
}
if (tempo) partes.push(`Tempo ${tempo}`);
if (bonus) partes.push(`Bônus ${bonus}`);
if (meta) partes.push(`Teto ${meta.teto}`);
partes.push(capped && meta ? `Total ${at.total} (teto)` : `Total ${at.total}`);
return partes.join(" | ");
})()}
</div> </div>
</div> </div>
))} ))}

View File

@@ -44,6 +44,7 @@ export const rankingService = {
posicao: c.posicao, posicao: c.posicao,
pontuacao_total: c.pontuacao_total, pontuacao_total: c.pontuacao_total,
bloco_a: c.bloco_a, bloco_a: c.bloco_a,
bloco_b: c.bloco_b,
bloco_c: c.bloco_c, bloco_c: c.bloco_c,
bloco_d: c.bloco_d, bloco_d: c.bloco_d,
ativo: c.ativo, ativo: c.ativo,
@@ -52,6 +53,7 @@ export const rankingService = {
pontuacao: c.pontuacao || { pontuacao: c.pontuacao || {
pontuacao_total: c.pontuacao_total, pontuacao_total: c.pontuacao_total,
bloco_a: { total: c.bloco_a, atuacoes: [] }, bloco_a: { total: c.bloco_a, atuacoes: [] },
bloco_b: { total: c.bloco_b, atuacoes: [] },
bloco_c: { total: c.bloco_c, atuacoes: [] }, bloco_c: { total: c.bloco_c, atuacoes: [] },
bloco_d: { total: c.bloco_d, atuacoes: [] }, bloco_d: { total: c.bloco_d, atuacoes: [] },
}, },