feat(pdf): adicionar exportação de ficha do consultor em PDF
- Novo endpoint GET /api/v1/consultor/{id}/pdf para download
- Serviço PDFService usando WeasyPrint para geração
- Template HTML com layout padrão governo federal
- Botão de exportar PDF no card e modal de dados brutos
This commit is contained in:
0
backend/src/infrastructure/pdf/__init__.py
Normal file
0
backend/src/infrastructure/pdf/__init__.py
Normal file
941
backend/src/infrastructure/pdf/templates/ficha_consultor.html
Normal file
941
backend/src/infrastructure/pdf/templates/ficha_consultor.html
Normal file
@@ -0,0 +1,941 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Cadastro do Consultor - {{ consultor.nome }}</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
{% set dp = raw.dadosPessoais if raw.dadosPessoais else {} %}
|
||||
{% set emails = raw.emails if raw.emails else [] %}
|
||||
{% set telefones = raw.telefones if raw.telefones else [] %}
|
||||
{% set enderecos = raw.enderecos if raw.enderecos else [] %}
|
||||
{% set identificadores = raw.identificadoresRegistrados if raw.identificadoresRegistrados else [] %}
|
||||
{% set identificador_lattes = raw.identificadorLattes if raw.identificadorLattes else None %}
|
||||
{% set titulacoes = raw.titulacoes if raw.titulacoes else [] %}
|
||||
{% set idiomas = raw.idiomas if raw.idiomas else [] %}
|
||||
{% set areas = raw.areasConhecimento if raw.areasConhecimento else [] %}
|
||||
{% set papeis = raw.papeis if raw.papeis else [] %}
|
||||
{% set bolsas = raw.bolsas if raw.bolsas else [] %}
|
||||
{% set dados_bancarios = raw.dadosBancarios if raw.dadosBancarios else [] %}
|
||||
{% set documentos = raw.documento if raw.documento else [] %}
|
||||
{% set atuacoes_raw = raw.atuacoes if raw.atuacoes else [] %}
|
||||
|
||||
{% set total_coordenacoes = consultor.coordenacoes_capes|length if consultor.coordenacoes_capes else 0 %}
|
||||
{% set total_inscricoes = consultor.inscricoes|length if consultor.inscricoes else 0 %}
|
||||
{% set total_avaliacoes = consultor.avaliacoes_comissao|length if consultor.avaliacoes_comissao else 0 %}
|
||||
{% set total_premiacoes = consultor.premiacoes|length if consultor.premiacoes else 0 %}
|
||||
{% set total_bolsas_cnpq = consultor.bolsas_cnpq|length if consultor.bolsas_cnpq else 0 %}
|
||||
{% set total_participacoes = consultor.participacoes|length if consultor.participacoes else 0 %}
|
||||
{% set total_orientacoes = consultor.orientacoes|length if consultor.orientacoes else 0 %}
|
||||
{% set total_bancas = consultor.membros_banca|length if consultor.membros_banca else 0 %}
|
||||
|
||||
<div class="documento">
|
||||
<section class="cover">
|
||||
<div>
|
||||
<table class="cover-header">
|
||||
<tr>
|
||||
<td>
|
||||
<strong>MINISTÉRIO DA EDUCAÇÃO</strong><br>
|
||||
Coordenação de Aperfeiçoamento de Pessoal de Nível Superior
|
||||
</td>
|
||||
<td class="right small">Relatório Executivo</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="cover-title">Cadastro do Consultor</div>
|
||||
<div class="cover-subtitle">Sistema de Ranking AtuaCAPES · Documento de Alta Gestão</div>
|
||||
|
||||
<div class="cover-name">{{ consultor.nome }}</div>
|
||||
<div class="tag-list">
|
||||
<span class="badge {{ 'success' if consultor.ativo else 'danger' }}">{{ 'Ativo' if consultor.ativo else 'Histórico' }}</span>
|
||||
{% if consultor.veterano %}
|
||||
<span class="badge">Veterano</span>
|
||||
{% endif %}
|
||||
{% if consultor.coordenador_ppg %}
|
||||
<span class="badge">Coordenação PPG</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="cover-meta">
|
||||
<tr>
|
||||
<td><strong>ID Pessoa:</strong> {{ consultor.id_pessoa }}</td>
|
||||
<td><strong>Posição no Ranking:</strong> {{ consultor.rank or '-' }}º</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Pontuação Total:</strong> {{ consultor.pontuacao.pontuacao_total }}</td>
|
||||
<td><strong>Anos de Atuação:</strong> {{ "%.1f"|format(consultor.anos_atuacao) }} anos</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Data de Geração:</strong> {{ data_geracao }}</td>
|
||||
<td><strong>Origem:</strong> Elasticsearch / ATUACAPES</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="section-divider"></div>
|
||||
|
||||
<div class="section-title">Resumo Executivo</div>
|
||||
<p class="lead">Visão consolidada dos indicadores do consultor, com foco em desempenho, volume e posicionamento estratégico.</p>
|
||||
|
||||
<table class="kpi-table">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="kpi-label">Pontuação total</div>
|
||||
<div class="kpi-value">{{ consultor.pontuacao.pontuacao_total }}</div>
|
||||
<div class="kpi-sub">Blocos A+C+D consolidados</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="kpi-label">Ranking nacional</div>
|
||||
<div class="kpi-value">{{ consultor.rank or '-' }}º</div>
|
||||
<div class="kpi-sub">Posição atual no ranking</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="kpi-label">Anos de atuação</div>
|
||||
<div class="kpi-value">{{ "%.1f"|format(consultor.anos_atuacao) }}</div>
|
||||
<div class="kpi-sub">Tempo de atuação CAPES</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="kpi-label">Status</div>
|
||||
<div class="kpi-value">{{ 'Ativo' if consultor.ativo else 'Histórico' }}</div>
|
||||
<div class="kpi-sub">{{ 'Consultoria em vigor' if consultor.ativo else 'Sem consultoria ativa' }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="subsection-title">Indicadores de Volume</div>
|
||||
<table class="kpi-table">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="kpi-label">Coordenações CAPES</div>
|
||||
<div class="kpi-value">{{ total_coordenacoes }}</div>
|
||||
<div class="kpi-sub">Bloco A</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="kpi-label">Consultorias & Vínculos</div>
|
||||
<div class="kpi-value">{{ consultor.consultoria.vinculos|length if consultor.consultoria and consultor.consultoria.vinculos else 0 }}</div>
|
||||
<div class="kpi-sub">Bloco C</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="kpi-label">Premiações & Avaliações</div>
|
||||
<div class="kpi-value">{{ total_premiacoes + total_avaliacoes + total_inscricoes }}</div>
|
||||
<div class="kpi-sub">Bloco D</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="kpi-label">Atuações no ES</div>
|
||||
<div class="kpi-value">{{ atuacoes_raw|length }}</div>
|
||||
<div class="kpi-sub">Base completa</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="section force-page-break">
|
||||
<div class="section-title">Identificação e Perfil</div>
|
||||
<table class="meta-table">
|
||||
<tr>
|
||||
<td class="label">Nome</td>
|
||||
<td class="value">{{ dp.nome or consultor.nome }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Nascimento</td>
|
||||
<td class="value">{{ dp.nascimento or '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Gênero</td>
|
||||
<td class="value">{{ dp.genero or '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Raça/Cor</td>
|
||||
<td class="value">{{ dp.raca or '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Nacionalidade</td>
|
||||
<td class="value">{{ dp.nacionalidade or '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Município/UF</td>
|
||||
<td class="value">{{ dp.municipio or '-' }}{% if dp.uf %} / {{ dp.uf }}{% endif %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Estado civil</td>
|
||||
<td class="value">{{ dp.estadoCivil or '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Nome da mãe</td>
|
||||
<td class="value">{{ dp.nomeMae or '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Nome do pai</td>
|
||||
<td class="value">{{ dp.nomePai or '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Tipo de pessoa</td>
|
||||
<td class="value">{{ dp.tipo or '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Ano de óbito</td>
|
||||
<td class="value">{{ dp.anoObito or '-' }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="section force-page-break">
|
||||
<div class="section-title">Contato e Identificadores</div>
|
||||
|
||||
<div class="subsection-title">E-mails</div>
|
||||
{% if emails %}
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>E-mail</th>
|
||||
<th>Tipo</th>
|
||||
<th>Principal</th>
|
||||
<th>Última alteração</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for e in emails %}
|
||||
<tr>
|
||||
<td>{{ e.email }}</td>
|
||||
<td>{{ e.tipo or '-' }}</td>
|
||||
<td>{{ e.principalFinalidade or '-' }}</td>
|
||||
<td>{{ e.ultimaAlteracao or '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="note">Sem registros de e-mail.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="subsection-title">Telefones</div>
|
||||
{% if telefones %}
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Telefone</th>
|
||||
<th>Tipo</th>
|
||||
<th>Finalidade</th>
|
||||
<th>Principal</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for t in telefones %}
|
||||
<tr>
|
||||
<td>({{ t.codigo }}) {{ t.numero }}</td>
|
||||
<td>{{ t.tipo or '-' }}</td>
|
||||
<td>{{ t.finalidade or '-' }}</td>
|
||||
<td>{{ t.principalFinalidade or '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="note">Sem registros de telefone.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="subsection-title">Endereços</div>
|
||||
{% if enderecos %}
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tipo</th>
|
||||
<th>Endereço</th>
|
||||
<th>Cidade</th>
|
||||
<th>CEP</th>
|
||||
<th>Principal</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for e in enderecos %}
|
||||
<tr>
|
||||
<td>{{ e.tipo or '-' }}</td>
|
||||
<td>{{ e.endereco or '-' }}{% if e.numero %}, {{ e.numero }}{% endif %}{% if e.complemento %} - {{ e.complemento }}{% endif %}</td>
|
||||
<td>{{ e.cidadeExterior or '-' }}</td>
|
||||
<td>{{ e.cep or '-' }}</td>
|
||||
<td>{{ e.principalFinalidade or '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="note">Sem registros de endereço.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="subsection-title">Identificadores e Documentos</div>
|
||||
{% if identificadores or documentos or identificador_lattes %}
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tipo</th>
|
||||
<th>Descrição</th>
|
||||
<th>Órgão expedidor</th>
|
||||
<th>Validade</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for i in identificadores %}
|
||||
<tr>
|
||||
<td>{{ i.tipo or '-' }}</td>
|
||||
<td>{{ i.descricao or '-' }}</td>
|
||||
<td>{{ i.orgaoExpedidor or '-' }}</td>
|
||||
<td>{{ i.inicioValidade or '-' }}{% if i.fimValidade %} a {{ i.fimValidade }}{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if identificador_lattes %}
|
||||
<tr>
|
||||
<td>{{ identificador_lattes.tipo or 'Identificador Lattes' }}</td>
|
||||
<td>{{ identificador_lattes.descricao or '-' }}</td>
|
||||
<td>{{ identificador_lattes.orgaoExpedidor or '-' }}</td>
|
||||
<td>{{ identificador_lattes.inicioValidade or '-' }}{% if identificador_lattes.fimValidade %} a {{ identificador_lattes.fimValidade }}{% endif %}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% for d in documentos %}
|
||||
<tr>
|
||||
<td>{{ d.tipo or '-' }}</td>
|
||||
<td>{{ d.descricao or '-' }}</td>
|
||||
<td>{{ d.orgaoExpedidor or '-' }}</td>
|
||||
<td>{{ d.inicioValidade or '-' }}{% if d.fimValidade %} a {{ d.fimValidade }}{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="note">Sem registros de identificadores/documentos.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="section force-page-break">
|
||||
<div class="section-title">Formação, Áreas e Competências</div>
|
||||
|
||||
<div class="subsection-title">Titulações</div>
|
||||
{% if titulacoes %}
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Grau</th>
|
||||
<th>IES</th>
|
||||
<th>Área</th>
|
||||
<th>Programa</th>
|
||||
<th>Período</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for t in titulacoes %}
|
||||
<tr>
|
||||
<td>{{ t.grauAcademico.nome if t.grauAcademico else '-' }}</td>
|
||||
<td>{{ t.ies.nome if t.ies else '-' }}{% if t.ies and t.ies.sigla %} ({{ t.ies.sigla }}){% endif %}</td>
|
||||
<td>{{ t.areaConhecimento.nome if t.areaConhecimento else '-' }}</td>
|
||||
<td>{{ t.programa.nome if t.programa else '-' }}</td>
|
||||
<td>{{ t.inicio|format_date_short if t.inicio else '-' }}{% if t.fim %} a {{ t.fim|format_date_short }}{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="note">Sem registros de titulação.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="subsection-title">Áreas de conhecimento</div>
|
||||
{% if areas %}
|
||||
<div class="tag-list">
|
||||
{% for a in areas %}
|
||||
<span class="tag">{{ a.nome or '-' }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="note">Sem áreas de conhecimento registradas.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="subsection-title">Idiomas</div>
|
||||
{% if idiomas %}
|
||||
<div class="tag-list">
|
||||
{% for i in idiomas %}
|
||||
<span class="tag">{{ i.idioma or i.nome or '-' }}{% if i.nivel %} · {{ i.nivel }}{% endif %}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="note">Sem idiomas registrados.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="subsection-title">Bolsas (base ES)</div>
|
||||
{% if bolsas %}
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Programa</th>
|
||||
<th>Instituição</th>
|
||||
<th>Período</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for b in bolsas %}
|
||||
<tr>
|
||||
<td>{{ b.programa or b.tipo or '-' }}</td>
|
||||
<td>{{ b.ies.nome if b.ies else '-' }}</td>
|
||||
<td>{{ b.inicio|format_date_short if b.inicio else '-' }}{% if b.fim %} a {{ b.fim|format_date_short }}{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="note">Sem bolsas registradas na base ES.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="subsection-title">Bolsas CNPq (pontuação)</div>
|
||||
{% if consultor.bolsas_cnpq %}
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Código</th>
|
||||
<th>Nível</th>
|
||||
<th>Área</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for b in consultor.bolsas_cnpq %}
|
||||
<tr>
|
||||
<td>{{ b.codigo }}</td>
|
||||
<td>{{ b.nivel }}</td>
|
||||
<td>{{ b.area or '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="note">Sem bolsas CNPq associadas.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="section force-page-break">
|
||||
<div class="section-title">Governança, Papéis e Consultoria</div>
|
||||
|
||||
<div class="subsection-title">Papéis cadastrados</div>
|
||||
{% if papeis %}
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tipo</th>
|
||||
<th>Status</th>
|
||||
<th>Origem</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for p in papeis %}
|
||||
<tr>
|
||||
<td>{{ p.tipo or '-' }}</td>
|
||||
<td>{{ p.status or '-' }}</td>
|
||||
<td>{{ p.procedencia.origem if p.procedencia else '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="note">Sem papéis registrados na base ES.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="subsection-title">Consultoria CAPES</div>
|
||||
{% if consultor.consultoria %}
|
||||
<table class="meta-table">
|
||||
<tr>
|
||||
<td class="label">Código</td>
|
||||
<td class="value">{{ consultor.consultoria.codigo }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Situação</td>
|
||||
<td class="value">{{ consultor.consultoria.situacao }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Período</td>
|
||||
<td class="value">{{ consultor.consultoria.periodo.inicio|format_date if consultor.consultoria.periodo else '-' }} a {{ consultor.consultoria.periodo.fim|format_date if consultor.consultoria.periodo and consultor.consultoria.periodo.fim else 'Atual' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Áreas</td>
|
||||
<td class="value">{{ consultor.consultoria.areas|join(', ') if consultor.consultoria.areas else '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Anos consecutivos</td>
|
||||
<td class="value">{{ consultor.consultoria.anos_consecutivos }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Retornos</td>
|
||||
<td class="value">{{ consultor.consultoria.retornos }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{% if consultor.consultoria.vinculos %}
|
||||
<div class="subsection-title">Vínculos institucionais</div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>IES</th>
|
||||
<th>Período</th>
|
||||
<th>Situação</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for v in consultor.consultoria.vinculos %}
|
||||
<tr>
|
||||
<td>{{ v.ies.nome if v.ies else '-' }}{% if v.ies and v.ies.sigla %} ({{ v.ies.sigla }}){% endif %}</td>
|
||||
<td>{{ v.periodo.inicio|format_date_short if v.periodo else '-' }} a {{ v.periodo.fim|format_date_short if v.periodo and v.periodo.fim else 'Atual' }}</td>
|
||||
<td>{{ v.situacao or '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="note">Sem registros de consultoria ativa ou histórica.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="subsection-title">Coordenação de PPG</div>
|
||||
<p class="lead">{{ 'Possui atuação de coordenação de PPG.' if consultor.coordenador_ppg else 'Sem indicação de coordenação de PPG nos registros.' }}</p>
|
||||
</section>
|
||||
|
||||
<section class="section force-page-break">
|
||||
<div class="section-title">Pontuação Consolidada</div>
|
||||
<p class="lead">Resumo dos blocos de pontuação conforme metodologia do Ranking AtuaCAPES.</p>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Bloco</th>
|
||||
<th>Descrição</th>
|
||||
<th>Pontos</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>A</td>
|
||||
<td>Coordenação CAPES</td>
|
||||
<td>{{ consultor.pontuacao.bloco_a.total }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>B</td>
|
||||
<td>Coordenação PPG (reservado)</td>
|
||||
<td>0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>C</td>
|
||||
<td>Consultoria CAPES</td>
|
||||
<td>{{ consultor.pontuacao.bloco_c.total }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>D</td>
|
||||
<td>Premiações, Avaliações e Participações</td>
|
||||
<td>{{ consultor.pontuacao.bloco_d.total }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="2" class="right"><strong>Pontuação total</strong></td>
|
||||
<td><strong>{{ consultor.pontuacao.pontuacao_total }}</strong></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="section force-page-break">
|
||||
<div class="section-title">Bloco A — Coordenações CAPES</div>
|
||||
{% if consultor.coordenacoes_capes %}
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Código</th>
|
||||
<th>Área de avaliação</th>
|
||||
<th>Período</th>
|
||||
<th>Anos</th>
|
||||
<th>Presidente</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for coord in consultor.coordenacoes_capes %}
|
||||
<tr>
|
||||
<td>{{ coord.codigo }}</td>
|
||||
<td>{{ coord.area_avaliacao or '-' }}</td>
|
||||
<td>{{ coord.periodo.inicio|format_date_short if coord.periodo else '-' }} a {{ coord.periodo.fim|format_date_short if coord.periodo and coord.periodo.fim else 'Atual' }}</td>
|
||||
<td>{{ "%.1f"|format(coord.periodo.anos_decorridos) if coord.periodo else '-' }}</td>
|
||||
<td>{{ 'Sim' if coord.presidente else 'Não' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% if consultor.pontuacao.bloco_a.atuacoes %}
|
||||
<div class="subsection-title">Pontuação detalhada</div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Código</th>
|
||||
<th>Quantidade</th>
|
||||
<th>Base</th>
|
||||
<th>Tempo</th>
|
||||
<th>Bônus</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for at in consultor.pontuacao.bloco_a.atuacoes %}
|
||||
<tr>
|
||||
<td>{{ at.codigo }}</td>
|
||||
<td>{{ at.quantidade }}</td>
|
||||
<td>{{ at.base }}</td>
|
||||
<td>{{ at.tempo }}</td>
|
||||
<td>{{ at.bonus }}</td>
|
||||
<td><strong>{{ at.total }}</strong></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="note">Nenhuma coordenação CAPES registrada para este consultor.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="section force-page-break">
|
||||
<div class="section-title">Bloco C — Consultoria CAPES</div>
|
||||
{% if consultor.consultoria %}
|
||||
<table class="meta-table">
|
||||
<tr>
|
||||
<td class="label">Código</td>
|
||||
<td class="value">{{ consultor.consultoria.codigo }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Situação</td>
|
||||
<td class="value">{{ consultor.consultoria.situacao }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Período</td>
|
||||
<td class="value">{{ consultor.consultoria.periodo.inicio|format_date if consultor.consultoria.periodo else '-' }} a {{ consultor.consultoria.periodo.fim|format_date if consultor.consultoria.periodo and consultor.consultoria.periodo.fim else 'Atual' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Anos consecutivos</td>
|
||||
<td class="value">{{ consultor.consultoria.anos_consecutivos }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Retornos</td>
|
||||
<td class="value">{{ consultor.consultoria.retornos }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{% if consultor.consultoria.vinculos %}
|
||||
<div class="subsection-title">Vínculos institucionais</div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>IES</th>
|
||||
<th>Período</th>
|
||||
<th>Situação</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for v in consultor.consultoria.vinculos %}
|
||||
<tr>
|
||||
<td>{{ v.ies.nome if v.ies else '-' }}{% if v.ies and v.ies.sigla %} ({{ v.ies.sigla }}){% endif %}</td>
|
||||
<td>{{ v.periodo.inicio|format_date_short if v.periodo else '-' }} a {{ v.periodo.fim|format_date_short if v.periodo and v.periodo.fim else 'Atual' }}</td>
|
||||
<td>{{ v.situacao or '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if consultor.pontuacao.bloco_c.atuacoes %}
|
||||
<div class="subsection-title">Pontuação detalhada</div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Código</th>
|
||||
<th>Quantidade</th>
|
||||
<th>Base</th>
|
||||
<th>Tempo</th>
|
||||
<th>Bônus</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for at in consultor.pontuacao.bloco_c.atuacoes %}
|
||||
<tr>
|
||||
<td>{{ at.codigo }}</td>
|
||||
<td>{{ at.quantidade }}</td>
|
||||
<td>{{ at.base }}</td>
|
||||
<td>{{ at.tempo }}</td>
|
||||
<td>{{ at.bonus }}</td>
|
||||
<td><strong>{{ at.total }}</strong></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="note">Nenhum registro de consultoria encontrado para este consultor.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="section force-page-break">
|
||||
<div class="section-title">Bloco D — Premiações, Avaliações e Participações</div>
|
||||
|
||||
{% if consultor.avaliacoes_comissao %}
|
||||
<div class="subsection-title">Avaliações em comissões ({{ consultor.avaliacoes_comissao|length }})</div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Código</th>
|
||||
<th>Tipo</th>
|
||||
<th>Prêmio</th>
|
||||
<th>Ano</th>
|
||||
<th>Comissão</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for a in consultor.avaliacoes_comissao %}
|
||||
<tr>
|
||||
<td>{{ a.codigo }}</td>
|
||||
<td>{{ a.tipo }}</td>
|
||||
<td>{{ a.premio }}</td>
|
||||
<td>{{ a.ano }}</td>
|
||||
<td>{{ a.nome_comissao or '-' }}{% if a.comissao_tipo %} ({{ a.comissao_tipo }}){% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if consultor.premiacoes %}
|
||||
<div class="subsection-title">Premiações recebidas ({{ consultor.premiacoes|length }})</div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Código</th>
|
||||
<th>Tipo</th>
|
||||
<th>Prêmio</th>
|
||||
<th>Ano</th>
|
||||
<th>Papel</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for p in consultor.premiacoes %}
|
||||
<tr>
|
||||
<td>{{ p.codigo }}</td>
|
||||
<td>{{ p.tipo }}</td>
|
||||
<td>{{ p.nome_premio }}</td>
|
||||
<td>{{ p.ano }}</td>
|
||||
<td>{{ p.papel or '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if consultor.inscricoes %}
|
||||
<div class="subsection-title">Inscrições em prêmios ({{ consultor.inscricoes|length }})</div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Código</th>
|
||||
<th>Tipo</th>
|
||||
<th>Prêmio</th>
|
||||
<th>Ano</th>
|
||||
<th>Situação</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for i in consultor.inscricoes %}
|
||||
<tr>
|
||||
<td>{{ i.codigo }}</td>
|
||||
<td>{{ i.tipo }}</td>
|
||||
<td>{{ i.premio }}</td>
|
||||
<td>{{ i.ano }}</td>
|
||||
<td>{{ i.situacao }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if consultor.orientacoes %}
|
||||
<div class="subsection-title">Orientações acadêmicas ({{ consultor.orientacoes|length }})</div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Código</th>
|
||||
<th>Tipo</th>
|
||||
<th>Nível</th>
|
||||
<th>Ano</th>
|
||||
<th>Co-orient.</th>
|
||||
<th>Premiada</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for o in consultor.orientacoes %}
|
||||
<tr>
|
||||
<td>{{ o.codigo }}</td>
|
||||
<td>{{ o.tipo }}</td>
|
||||
<td>{{ o.nivel }}</td>
|
||||
<td>{{ o.ano or '-' }}</td>
|
||||
<td>{{ 'Sim' if o.coorientacao else 'Não' }}</td>
|
||||
<td>{{ 'Sim' if o.premiada else 'Não' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if consultor.membros_banca %}
|
||||
<div class="subsection-title">Bancas examinadoras ({{ consultor.membros_banca|length }})</div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Código</th>
|
||||
<th>Tipo</th>
|
||||
<th>Nível</th>
|
||||
<th>Ano</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for b in consultor.membros_banca %}
|
||||
<tr>
|
||||
<td>{{ b.codigo }}</td>
|
||||
<td>{{ b.tipo }}</td>
|
||||
<td>{{ b.nivel }}</td>
|
||||
<td>{{ b.ano or '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if consultor.participacoes %}
|
||||
<div class="subsection-title">Participações ({{ consultor.participacoes|length }})</div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Código</th>
|
||||
<th>Tipo</th>
|
||||
<th>Descrição</th>
|
||||
<th>Ano</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for p in consultor.participacoes %}
|
||||
<tr>
|
||||
<td>{{ p.codigo }}</td>
|
||||
<td>{{ p.tipo }}</td>
|
||||
<td>{{ p.descricao }}</td>
|
||||
<td>{{ p.ano or '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if consultor.pontuacao.bloco_d.atuacoes %}
|
||||
<div class="subsection-title">Pontuação detalhada</div>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Código</th>
|
||||
<th>Quantidade</th>
|
||||
<th>Base</th>
|
||||
<th>Tempo</th>
|
||||
<th>Bônus</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for at in consultor.pontuacao.bloco_d.atuacoes %}
|
||||
<tr>
|
||||
<td>{{ at.codigo }}</td>
|
||||
<td>{{ at.quantidade }}</td>
|
||||
<td>{{ at.base }}</td>
|
||||
<td>{{ at.tempo }}</td>
|
||||
<td>{{ at.bonus }}</td>
|
||||
<td><strong>{{ at.total }}</strong></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="section force-page-break">
|
||||
<div class="section-title">Atuações no Elasticsearch</div>
|
||||
<p class="lead">Base completa de atuações registradas no ATUACAPES. Esta seção consolida todas as entradas encontradas no índice.</p>
|
||||
{% if atuacoes_raw %}
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tipo</th>
|
||||
<th>Descrição</th>
|
||||
<th>Período</th>
|
||||
<th>Procedência</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for a in atuacoes_raw %}
|
||||
<tr>
|
||||
<td>{{ a.tipo or '-' }}</td>
|
||||
<td>{{ a.descricao or a.nome or '-' }}</td>
|
||||
<td>{{ a.inicio or '-' }}{% if a.fim %} a {{ a.fim }}{% endif %}</td>
|
||||
<td>{{ a.procedencia.origem if a.procedencia else '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="note">Sem atuações registradas no índice.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="section force-page-break">
|
||||
<div class="section-title">Apêndice Técnico</div>
|
||||
<div class="subsection-title">Dados bancários</div>
|
||||
{% if dados_bancarios %}
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Banco</th>
|
||||
<th>Agência</th>
|
||||
<th>Conta</th>
|
||||
<th>Tipo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for b in dados_bancarios %}
|
||||
<tr>
|
||||
<td>{{ b.banco or '-' }}</td>
|
||||
<td>{{ b.agencia or '-' }}</td>
|
||||
<td>{{ b.conta or '-' }}</td>
|
||||
<td>{{ b.tipoConta or '-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="note">Sem dados bancários registrados.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="subsection-title">Metadados do documento</div>
|
||||
<table class="meta-table">
|
||||
<tr>
|
||||
<td class="label">Consultor ID</td>
|
||||
<td class="value">{{ consultor.id_pessoa }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Fonte</td>
|
||||
<td class="value">Elasticsearch ATUACAPES</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Data de geração</td>
|
||||
<td class="value">{{ data_geracao }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="footer">
|
||||
Documento gerado automaticamente. Em caso de divergência, prevalecem os registros oficiais do sistema.
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
307
backend/src/infrastructure/pdf/templates/styles.css
Normal file
307
backend/src/infrastructure/pdf/templates/styles.css
Normal file
@@ -0,0 +1,307 @@
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 1.6cm 1.4cm 1.8cm 1.4cm;
|
||||
|
||||
@bottom-left {
|
||||
content: "Sistema de Ranking AtuaCAPES";
|
||||
font-family: "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 8pt;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
@bottom-right {
|
||||
content: "Página " counter(page) " de " counter(pages);
|
||||
font-family: "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 8pt;
|
||||
color: #64748b;
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--ink: #0f172a;
|
||||
--muted: #475569;
|
||||
--accent: #0f3d68;
|
||||
--accent-2: #1d4ed8;
|
||||
--border: #e2e8f0;
|
||||
--bg-soft: #f8fafc;
|
||||
--bg-strong: #eef2f7;
|
||||
--success: #15803d;
|
||||
--danger: #b91c1c;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 10pt;
|
||||
line-height: 1.45;
|
||||
color: var(--ink);
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.documento {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.force-page-break {
|
||||
page-break-before: always !important;
|
||||
break-before: page !important;
|
||||
}
|
||||
|
||||
.no-break {
|
||||
page-break-inside: avoid !important;
|
||||
break-inside: avoid !important;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
page-break-after: avoid;
|
||||
break-after: avoid;
|
||||
}
|
||||
|
||||
.cover {
|
||||
border: 2px solid var(--accent);
|
||||
padding: 28px 32px;
|
||||
min-height: 23cm;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
background: var(--bg-soft);
|
||||
}
|
||||
|
||||
.cover-header {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 10px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.cover-header td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.cover-title {
|
||||
font-size: 20pt;
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1.5px;
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
.cover-subtitle {
|
||||
color: var(--muted);
|
||||
font-size: 11pt;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.cover-name {
|
||||
font-size: 16pt;
|
||||
font-weight: 700;
|
||||
margin: 18px 0 8px;
|
||||
}
|
||||
|
||||
.cover-meta {
|
||||
width: 100%;
|
||||
border-top: 1px solid var(--border);
|
||||
padding-top: 10px;
|
||||
border-collapse: collapse;
|
||||
font-size: 9.5pt;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.cover-meta td {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.cover-meta strong {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
background: #e2e8f0;
|
||||
color: var(--ink);
|
||||
font-size: 8.5pt;
|
||||
font-weight: 600;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.badge.success {
|
||||
background: #dcfce7;
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.badge.danger {
|
||||
background: #fee2e2;
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 10px 0 4px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 13pt;
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
border-bottom: 2px solid var(--accent);
|
||||
padding-bottom: 6px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.subsection-title {
|
||||
font-size: 11pt;
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
margin: 16px 0 8px;
|
||||
}
|
||||
|
||||
.lead {
|
||||
color: var(--muted);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.kpi-table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.kpi-table td {
|
||||
background: var(--bg-strong);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.kpi-label {
|
||||
font-size: 8.5pt;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.kpi-value {
|
||||
font-size: 14pt;
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.kpi-sub {
|
||||
font-size: 9pt;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid var(--border);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.data-table thead {
|
||||
background: var(--accent);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.data-table th,
|
||||
.data-table td {
|
||||
padding: 8px 10px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.data-table th {
|
||||
font-size: 8.5pt;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.data-table tbody tr:nth-child(even) {
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--muted);
|
||||
font-weight: 600;
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.meta-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.meta-table td {
|
||||
padding: 8px 10px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.meta-table tr:nth-child(even) {
|
||||
background: var(--bg-soft);
|
||||
}
|
||||
|
||||
.tag-list {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
margin: 0 6px 6px 0;
|
||||
background: var(--bg-strong);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
font-size: 8.5pt;
|
||||
}
|
||||
|
||||
.note {
|
||||
background: var(--bg-soft);
|
||||
border: 1px dashed var(--border);
|
||||
padding: 10px 12px;
|
||||
color: var(--muted);
|
||||
font-size: 9pt;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.section-divider {
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 18px;
|
||||
font-size: 8.5pt;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 8.5pt;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -141,19 +141,20 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
||||
if not consultorias:
|
||||
return None
|
||||
|
||||
visto_ids: Dict[int, Dict[str, Any]] = {}
|
||||
vistos: Dict[tuple, Dict[str, Any]] = {}
|
||||
for c in consultorias:
|
||||
cid = c.get("id")
|
||||
if cid is None:
|
||||
continue
|
||||
if cid not in visto_ids:
|
||||
visto_ids[cid] = c
|
||||
dc = c.get("dadosConsultoria", {}) or {}
|
||||
ies_id = (dc.get("ies", {}) or {}).get("id")
|
||||
inicio = c.get("inicio") or dc.get("inicioVinculacao") or dc.get("inicioSituacao")
|
||||
chave = (ies_id, inicio)
|
||||
if chave not in vistos:
|
||||
vistos[chave] = c
|
||||
else:
|
||||
existente_fim = visto_ids[cid].get("fim")
|
||||
existente_fim = vistos[chave].get("fim")
|
||||
novo_fim = c.get("fim")
|
||||
if existente_fim and not novo_fim:
|
||||
visto_ids[cid] = c
|
||||
consultorias = list(visto_ids.values()) if visto_ids else consultorias
|
||||
vistos[chave] = c
|
||||
consultorias = list(vistos.values()) if vistos else consultorias
|
||||
|
||||
periodos: List[Periodo] = []
|
||||
vinculos: List[VinculoConsultoria] = []
|
||||
|
||||
Reference in New Issue
Block a user