"""Gerador de documento contendo apenas anexos do modelo selecionado.""" from __future__ import annotations import re from datetime import datetime from io import BytesIO from pathlib import Path from docx import Document from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.shared import Cm from .banco_dados import anexo_banco_dados_cabe_na_largura, gerar_anexo_banco_dados from .calculo_valor import gerar_anexo_calculo from .graficos import gerar_anexo_graficos from .planilha_calculo import gerar_anexo_planilha from .formatters import ( criar_paragrafo_formatado, iniciar_secao_paisagem, iniciar_secao_retrato, ) from .model_data import ModelData class AnexosGenerator: """Gera um DOCX com os anexos técnicos de um modelo .dai.""" def __init__(self, output_dir: Path) -> None: self.output_dir = output_dir def gerar( self, modelo: ModelData, banco_dados_colunas: list[str] | None = None, avaliacao: dict | None = None, ) -> Path: doc = Document() self._configurar_margens(doc) self._gerar_anexos( doc, modelo, banco_dados_colunas=banco_dados_colunas, avaliacao=avaliacao, ) self.output_dir.mkdir(parents=True, exist_ok=True) output_path = self._gerar_nome_arquivo(modelo.nome) doc.save(str(output_path)) return output_path def gerar_em_memoria( self, modelo: ModelData, banco_dados_colunas: list[str] | None = None, avaliacao: dict | None = None, ) -> tuple[bytes, str]: """Gera o DOCX sem persistir o anexo no filesystem.""" doc = Document() self._configurar_margens(doc) self._gerar_anexos( doc, modelo, banco_dados_colunas=banco_dados_colunas, avaliacao=avaliacao, ) buffer = BytesIO() doc.save(buffer) return buffer.getvalue(), self._gerar_nome_arquivo(modelo.nome).name def _configurar_margens(self, doc: Document) -> None: for section in doc.sections: section.top_margin = Cm(2.5) section.bottom_margin = Cm(2.5) section.left_margin = Cm(3) section.right_margin = Cm(2) def _criar_titulo_anexo(self, doc: Document, titulo: str) -> None: criar_paragrafo_formatado( doc, titulo, negrito=True, sublinhado=True, tamanho=10, espaco_antes=18, espaco_depois=12, alinhamento=WD_ALIGN_PARAGRAPH.CENTER, ) def _largura_util_secao_atual(self, doc: Document) -> int: section = doc.sections[-1] return int(section.page_width - section.left_margin - section.right_margin) def _gerar_anexos( self, doc: Document, modelo: ModelData, banco_dados_colunas: list[str] | None = None, avaliacao: dict | None = None, ) -> None: banco_cabe_em_retrato = anexo_banco_dados_cabe_na_largura( modelo, banco_dados_colunas, self._largura_util_secao_atual(doc), ) if not banco_cabe_em_retrato: iniciar_secao_paisagem(doc, nova_pagina=False) self._criar_titulo_anexo(doc, "ANEXO I - BANCO DE DADOS") gerar_anexo_banco_dados(doc, modelo, colunas=banco_dados_colunas) iniciar_secao_retrato(doc) self._criar_titulo_anexo( doc, "ANEXO II - PLANILHA DE CÁLCULO E RESULTADOS ESTATÍSTICOS", ) gerar_anexo_planilha(doc, modelo) doc.add_page_break() self._criar_titulo_anexo(doc, "ANEXO III - GRÁFICOS") gerar_anexo_graficos(doc, modelo) doc.add_page_break() self._criar_titulo_anexo(doc, "ANEXO IV - CÁLCULO DO VALOR") gerar_anexo_calculo(doc, modelo, avaliacao=avaliacao) def _gerar_nome_arquivo(self, modelo_nome: str) -> Path: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f") modelo_safe = re.sub(r"[^\w\-]", "_", modelo_nome) return self.output_dir / f"Anexos_{modelo_safe}_{timestamp}.docx"