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"{thead}{tbody}
" 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"\"CAPES\"" 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)
Versão: 1.0
Data: {hoje}
Finalidade: Documento executivo para alta gestão
Escopo: Critérios oficiais do ranking conforme planilha de definição (Abas 1 a 5)

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()