fix: corrigir filtro de ativos, remover count de areas e navegacao ao clicar
This commit is contained in:
70
scripts/estilizar_planilha.py
Normal file
70
scripts/estilizar_planilha.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from openpyxl import load_workbook
|
||||
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
||||
|
||||
|
||||
INPUT_PATH = Path("/home/fred/Downloads/Definição Ranking_ATUACAPES - Aba1 a Aba4(7).xlsx")
|
||||
OUTPUT_PATH = Path("/home/fred/projetos/ranking/docs/Definicao_Ranking_ATUACAPES_estilizada.xlsx")
|
||||
|
||||
|
||||
def apply_styles() -> None:
|
||||
wb = load_workbook(INPUT_PATH)
|
||||
|
||||
header_fill = PatternFill("solid", fgColor="E6F0FF")
|
||||
header_font = Font(bold=True, color="102A43")
|
||||
alt_fill = PatternFill("solid", fgColor="F7FAFC")
|
||||
thin = Side(style="thin", color="CBD5E0")
|
||||
border = Border(left=thin, right=thin, top=thin, bottom=thin)
|
||||
|
||||
for ws in wb.worksheets:
|
||||
max_col = ws.max_column
|
||||
max_row = ws.max_row
|
||||
|
||||
# Header styling
|
||||
for col in range(1, max_col + 1):
|
||||
cell = ws.cell(row=1, column=col)
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
||||
cell.border = border
|
||||
|
||||
# Data rows styling
|
||||
for row in range(2, max_row + 1):
|
||||
row_fill = alt_fill if row % 2 == 0 else None
|
||||
for col in range(1, max_col + 1):
|
||||
cell = ws.cell(row=row, column=col)
|
||||
cell.border = border
|
||||
cell.alignment = Alignment(vertical="top", wrap_text=True)
|
||||
if row_fill:
|
||||
cell.fill = row_fill
|
||||
|
||||
# Freeze header row
|
||||
ws.freeze_panes = "A2"
|
||||
|
||||
# Auto-filter across the used range
|
||||
ws.auto_filter.ref = ws.dimensions
|
||||
|
||||
# Adjust column widths with caps
|
||||
for col in ws.columns:
|
||||
col_letter = col[0].column_letter
|
||||
max_len = 0
|
||||
for cell in col[: min(max_row, 200)]:
|
||||
if cell.value is None:
|
||||
continue
|
||||
text = str(cell.value)
|
||||
if len(text) > max_len:
|
||||
max_len = len(text)
|
||||
width = max(12, min(45, int(max_len * 0.9)))
|
||||
ws.column_dimensions[col_letter].width = width
|
||||
|
||||
# Slightly taller header
|
||||
ws.row_dimensions[1].height = 28
|
||||
|
||||
OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
wb.save(OUTPUT_PATH)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
apply_styles()
|
||||
262
scripts/gerar_documento_criterios.py
Normal file
262
scripts/gerar_documento_criterios.py
Normal file
@@ -0,0 +1,262 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import datetime
|
||||
from html import escape
|
||||
|
||||
from openpyxl import load_workbook
|
||||
from weasyprint import HTML, CSS
|
||||
|
||||
|
||||
XLSX_PATH = Path("/home/fred/Downloads/Definição Ranking_ATUACAPES - Aba1 a Aba4(7).xlsx")
|
||||
PDF_PATH = Path("/home/fred/projetos/ranking/docs/Criterios_Ranking_Consultores.pdf")
|
||||
LOGO_PATH = Path("/home/fred/projetos/ranking/docs/assets/logo-capes.png")
|
||||
|
||||
|
||||
def _fmt(cell) -> str:
|
||||
if cell is None:
|
||||
return ""
|
||||
return str(cell).replace("\n", " ").strip()
|
||||
|
||||
|
||||
def _table_html(headers, rows, code_cols=None) -> str:
|
||||
code_cols = set(code_cols or [])
|
||||
thead = "<tr>" + "".join(f"<th>{escape(_fmt(h))}</th>" for h in headers) + "</tr>"
|
||||
body_rows = []
|
||||
for row in rows:
|
||||
cols = []
|
||||
for idx, cell in enumerate(row):
|
||||
text = _fmt(cell)
|
||||
if idx in code_cols and text:
|
||||
text = f"<code>{escape(text)}</code>"
|
||||
else:
|
||||
text = escape(text)
|
||||
cols.append(f"<td>{text}</td>")
|
||||
body_rows.append("<tr>" + "".join(cols) + "</tr>")
|
||||
tbody = "\n".join(body_rows)
|
||||
return f"<table class=\"criteria-table\"><thead>{thead}</thead><tbody>{tbody}</tbody></table>"
|
||||
|
||||
|
||||
def load_planilha() -> dict:
|
||||
wb = load_workbook(XLSX_PATH, data_only=True)
|
||||
|
||||
def extract(sheet, header_cols, row_cols, skip_score=False, formula=False):
|
||||
ws = wb[sheet]
|
||||
headers = [_fmt(c) for c in ws[1][:header_cols]]
|
||||
rows = []
|
||||
formula_row = ""
|
||||
for r in ws.iter_rows(min_row=2, values_only=True):
|
||||
if not any(r):
|
||||
continue
|
||||
if formula and r[0] is None and isinstance(r[1], str) and "tempo =" in r[1]:
|
||||
formula_row = r[1]
|
||||
continue
|
||||
if skip_score and _fmt(r[0]).lower() == "score":
|
||||
continue
|
||||
rows.append(r[:row_cols])
|
||||
return headers, rows, formula_row
|
||||
|
||||
h1, r1, _ = extract("Aba1_Mapa_Atuacoes", 9, 9)
|
||||
h2, r2, _ = extract("Aba2_Pontuacao_Base", 4, 4)
|
||||
h3, r3, f3 = extract("Aba3_Regras_Tempo", 7, 7, formula=True)
|
||||
h4, r4, _ = extract("Aba4_Bonus_Extras", 12, 12)
|
||||
h5, r5, _ = extract("Aba5_Detalh. Perfil_Indicadores", 6, 6, skip_score=True)
|
||||
|
||||
return {
|
||||
"aba1": {"headers": h1, "rows": r1},
|
||||
"aba2": {"headers": h2, "rows": r2},
|
||||
"aba3": {"headers": h3, "rows": r3, "formula": f3},
|
||||
"aba4": {"headers": h4, "rows": r4},
|
||||
"aba5": {"headers": h5, "rows": r5},
|
||||
}
|
||||
|
||||
|
||||
def build_html(data: dict) -> str:
|
||||
hoje = datetime.date.today().isoformat()
|
||||
logo_html = ""
|
||||
if LOGO_PATH.exists():
|
||||
logo_html = f"<img class=\"cover-logo\" src=\"{LOGO_PATH.as_posix()}\" alt=\"CAPES\" />"
|
||||
|
||||
return f"""
|
||||
<!doctype html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Critérios de Pontuação e Ordenação (Versão Executiva)</title>
|
||||
<style>
|
||||
@page {{
|
||||
size: A4;
|
||||
margin: 2.1cm 2.1cm 2.3cm 2.1cm;
|
||||
}}
|
||||
@page landscape {{
|
||||
size: A4 landscape;
|
||||
margin: 1.5cm 1.7cm 1.8cm 1.7cm;
|
||||
}}
|
||||
body {{
|
||||
font-family: "Liberation Serif", "Times New Roman", serif;
|
||||
color: #0f172a;
|
||||
font-size: 11pt;
|
||||
line-height: 1.4;
|
||||
}}
|
||||
h1, h2 {{
|
||||
font-family: "Liberation Sans", "Arial", sans-serif;
|
||||
color: #0b1f3a;
|
||||
margin: 0 0 0.4cm 0;
|
||||
}}
|
||||
h1 {{
|
||||
font-size: 25pt;
|
||||
letter-spacing: 0.3px;
|
||||
}}
|
||||
h2 {{
|
||||
font-size: 16pt;
|
||||
margin-top: 0;
|
||||
}}
|
||||
p {{
|
||||
margin: 0.3cm 0;
|
||||
}}
|
||||
.cover {{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 92vh;
|
||||
border: 2px solid #0b1f3a;
|
||||
padding: 2.2cm;
|
||||
text-align: center;
|
||||
}}
|
||||
.cover-logo {{
|
||||
width: 6.0cm;
|
||||
height: auto;
|
||||
margin-bottom: 0.7cm;
|
||||
}}
|
||||
.cover-subtitle {{
|
||||
font-size: 14pt;
|
||||
color: #1f3b63;
|
||||
margin-bottom: 1.1cm;
|
||||
max-width: 14cm;
|
||||
}}
|
||||
.cover-meta {{
|
||||
font-size: 10.6pt;
|
||||
color: #0f172a;
|
||||
border-top: 1px solid #1f3b63;
|
||||
padding-top: 0.5cm;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}}
|
||||
.label {{
|
||||
font-weight: 700;
|
||||
color: #0b1f3a;
|
||||
}}
|
||||
section {{
|
||||
break-before: page;
|
||||
}}
|
||||
section.first {{
|
||||
break-before: auto;
|
||||
}}
|
||||
.landscape {{
|
||||
page: landscape;
|
||||
}}
|
||||
table.criteria-table {{
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 0.35cm 0 0.5cm 0;
|
||||
font-size: 9.3pt;
|
||||
table-layout: fixed;
|
||||
}}
|
||||
table.criteria-table th, table.criteria-table td {{
|
||||
border: 1px solid #cbd5f0;
|
||||
padding: 5px 6px;
|
||||
vertical-align: top;
|
||||
word-break: break-word;
|
||||
hyphens: auto;
|
||||
}}
|
||||
table.criteria-table th {{
|
||||
background: #eef2ff;
|
||||
text-align: left;
|
||||
font-weight: 700;
|
||||
}}
|
||||
table.criteria-table tr:nth-child(even) td {{
|
||||
background: #f8fafc;
|
||||
}}
|
||||
.landscape table.criteria-table {{
|
||||
font-size: 8.2pt;
|
||||
}}
|
||||
.landscape table.criteria-table th,
|
||||
.landscape table.criteria-table td {{
|
||||
padding: 4px 5px;
|
||||
}}
|
||||
code {{
|
||||
font-family: "Liberation Mono", "Courier New", monospace;
|
||||
background: #f1f5f9;
|
||||
padding: 0 3px;
|
||||
border-radius: 3px;
|
||||
font-size: 9.2pt;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section class="cover">
|
||||
{logo_html}
|
||||
<h1>Sistema de Ranking de Consultores</h1>
|
||||
<div class="cover-subtitle">Critérios de Pontuação e Ordenação (Versão Executiva)</div>
|
||||
<div class="cover-meta">
|
||||
<div><span class="label">Versão:</span> 1.0</div>
|
||||
<div><span class="label">Data:</span> {hoje}</div>
|
||||
<div><span class="label">Finalidade:</span> Documento executivo para alta gestão</div>
|
||||
<div><span class="label">Escopo:</span> Critérios oficiais do ranking conforme planilha de definição (Abas 1 a 5)</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="first">
|
||||
<h2>Sumário Executivo</h2>
|
||||
<p>Este documento consolida, de forma hierárquica e fiel à planilha oficial, todos os critérios utilizados no ranking de consultores. A estrutura está organizada por abas: mapeamento das atuações (Aba 1), pontuação base e tetos (Aba 2), regras de tempo (Aba 3), bônus e selos (Aba 4) e indicadores não pontuáveis (Aba 5).</p>
|
||||
</section>
|
||||
|
||||
<section class="landscape">
|
||||
<h2>1. Aba 1 — Mapa de Atuações</h2>
|
||||
{_table_html(data["aba1"]["headers"], data["aba1"]["rows"], code_cols=[3])}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>2. Aba 2 — Pontuação Base e Teto por Atuação</h2>
|
||||
{_table_html(data["aba2"]["headers"], data["aba2"]["rows"], code_cols=[0])}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>3. Aba 3 — Regras de Tempo</h2>
|
||||
{_table_html(data["aba3"]["headers"], data["aba3"]["rows"], code_cols=[0])}
|
||||
<p><span class="label">Fórmula da planilha:</span> <code>{escape(_fmt(data["aba3"]["formula"]))}</code></p>
|
||||
</section>
|
||||
|
||||
<section class="landscape">
|
||||
<h2>4. Aba 4 — Bônus e Selos</h2>
|
||||
{_table_html(data["aba4"]["headers"], data["aba4"]["rows"], code_cols=[0])}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>5. Aba 5 — Indicadores Não Pontuáveis</h2>
|
||||
<p>Os itens abaixo constam na Aba 5 e <strong>não impactam a pontuação</strong>. O indicador <strong>Score</strong> é pontuável e já está coberto pelas regras das Abas 2 a 4.</p>
|
||||
{_table_html(data["aba5"]["headers"], data["aba5"]["rows"])}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>6. Fonte Oficial</h2>
|
||||
<p>Planilha: <code>{escape(str(XLSX_PATH))}</code></p>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
def main() -> None:
|
||||
data = load_planilha()
|
||||
html = build_html(data)
|
||||
HTML(string=html, base_url=str(PDF_PATH.parent)).write_pdf(
|
||||
target=str(PDF_PATH),
|
||||
stylesheets=[CSS(string="@page { size: A4; }")],
|
||||
)
|
||||
print(f"PDF: {PDF_PATH}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user