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 = "
" + "".join(f"| {escape(_fmt(h))} | " for h in headers) + "
"
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"{escape(text)}"
else:
text = escape(text)
cols.append(f"{text} | ")
body_rows.append("" + "".join(cols) + "
")
tbody = "\n".join(body_rows)
return f""
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"
"
return f"""
Critérios de Pontuação e Ordenação (Versão Executiva)
{logo_html}
Sistema de Ranking de Consultores
Critérios de Pontuação e Ordenação (Versão Executiva)
Sumário Executivo
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).
1. Aba 1 — Mapa de Atuações
{_table_html(data["aba1"]["headers"], data["aba1"]["rows"], code_cols=[3])}
2. Aba 2 — Pontuação Base e Teto por Atuação
{_table_html(data["aba2"]["headers"], data["aba2"]["rows"], code_cols=[0])}
3. Aba 3 — Regras de Tempo
{_table_html(data["aba3"]["headers"], data["aba3"]["rows"], code_cols=[0])}
Fórmula da planilha: {escape(_fmt(data["aba3"]["formula"]))}
4. Aba 4 — Bônus e Selos
{_table_html(data["aba4"]["headers"], data["aba4"]["rows"], code_cols=[0])}
5. Aba 5 — Indicadores Não Pontuáveis
Os itens abaixo constam na Aba 5 e não impactam a pontuação. O indicador Score é pontuável e já está coberto pelas regras das Abas 2 a 4.
{_table_html(data["aba5"]["headers"], data["aba5"]["rows"])}
6. Fonte Oficial
Planilha: {escape(str(XLSX_PATH))}
"""
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()