Construindo uma CLI de Mail Merge com Typer e Python-docx

Aprenda a criar uma CLI moderna e idiomática usando Typer e python-docx para automatizar o mail merge de documentos corporativos com eficiência.

O Desafio da Geração Automatizada de Documentos

A personalização em lote de relatórios, contratos e certificados é uma necessidade constante em fluxos corporativos. Muitas equipes recorrem a processos manuais de mala direta ou manipulações desestruturadas que quebram a formatação de arquivos .docx. A automação desse fluxo por linha de comando reduz erros operacionais e viabiliza a integração direta com pipelines de dados existentes.

Arquitetura com Typer e python-docx

Para construir uma ferramenta de automação eficiente e de fácil manutenção, duas bibliotecas se destacam no ecossistema Python:

  • Typer: Responsável pela interface de linha de comando (CLI). Baseado em anotações de tipo do Python (type hints), simplifica parsing de argumentos, validações e gera telas de ajuda automáticas.
  • python-docx: Permite ler, inspecionar e alterar arquivos Word estruturados em parágrafos, tabelas e runs (fragmentos de texto formatados).

Implementação de Código Idiomático e Tipado

Como especialista em IA e arquitetura de software, priorizo implementações desacopladas, tipadas e devidamente documentadas. A manipulação de tags em documentos Word exige cuidado específico com runs para preservar estilo e layout.

from pathlib import Path
from typing import Dict, List
import typer
from docx import Document

app = typer.Typer(help="Utilitário CLI para Mala Direta em documentos DOCX.")

def replace_tags_in_document(doc: Document, context: Dict[str, str]) -> None:
    """
    Substitui placeholders nos parágrafos e tabelas de um Documento.

    :param doc: Objeto Document carregado via python-docx.
    :param context: Dicionário contendo as chaves de busca e valores finais.
    """
    for paragraph in doc.paragraphs:
        for key, value in context.items():
            tag = f"{{{{{key}}}}}"
            if tag in paragraph.text:
                paragraph.text = paragraph.text.replace(tag, value)

    for table in doc.tables:
        for row in table.rows:
            for cell in row.cells:
                for paragraph in cell.paragraphs:
                    for key, value in context.items():
                        tag = f"{{{{{key}}}}}"
                        if tag in paragraph.text:
                            paragraph.text = paragraph.text.replace(tag, value)

@app.command()
def merge(
    template: Path = typer.Option(..., exists=True, file_okay=True, dir_okay=False, help="Caminho do arquivo DOCX modelo."),
    output_dir: Path = typer.Option(Path("./output"), help="Diretório de saída dos documentos gerados."),
    recipient_name: str = typer.Option(..., help="Nome do destinatário para substituição no template.")
) -> None:
    """
    Executa a substituição dos campos e salva o novo arquivo formatado.
    """
    output_dir.mkdir(parents=True, exist_ok=True)
    doc = Document(template)

    context = {"NOME": recipient_name}
    replace_tags_in_document(doc, context)

    output_path = output_dir / f"{recipient_name.lower().replace(' ', '_')}.docx"
    doc.save(output_path)
    typer.echo(f"Documento gerado com sucesso: {output_path}")

if __name__ == "__main__":
    app()

Fluxo de Execução Recomendado

  1. Padronização do Modelo: Insira marcadores previsíveis no documento original, como {{NOME}} ou {{DATA}}.
  2. Consumo de Dados: Integre a CLI com arquivos CSV ou JSON para iterar sobre centenas de registros sem sobrecarga de memória.
  3. Validação com Typer: Garanta que caminhos de arquivos e parâmetros obrigatórios sejam validados antes do processamento pesado.

Evoluindo a Automação

Essa base pode ser expandida para lidar com blocos de texto dinâmicos, assinaturas eletrônicas ou conversão direta para PDF após o merge. Se a sua empresa busca automatizar fluxos complexos de documentos e integrar inteligência artificial aos seus processos operacionais, entre em contato para estruturarmos uma solução sob medida.

Preencha o formulário abaixo para que eu consiga entrar em contato com você.