Ontem o @Asvidzinski postou uma dúvida no Extra[18] que achei muito interessante e resolvi responder não lá no tópico mas criando um novo pois posso atingir mais pessoas com um novo tópico devido à visibilidade.
Ele perguntou se era possível pegar dados de uma tabela e gerar um documento a partir de um template no qual seriam adicionados somente os dados variáveis contantes nessa tabela.
Pensei que dava pra ensinar isso mais diretamente mas decidi complicar um pouco mais pra ensinar novos conceitos. Eu poderia muito bem já criar uma planilha com uns dados, abrir esses dados no Python e criar um documento a partir deles. Mas decidi criar a planilha usando Python e os dados eu obtive a partir de uma lib que gera dados falsos e é muito usada pela galera que desenvolve pra web pra testar a parte do Model do MVC (quem não sabe o que é MVC não se preocupe com isso agora, não é um conceito necessário hoje).
Preparem-se porque este de hoje vai ser grande.
Como nos tópicos teóricos estou abordando funções, vou criar um código usando o paradigma funcional para ter um código mais organizado. Vai ser uma boa oportunidade de ver como aplicar funções num código um pouco maior.
Vou instalar as libs necessárias dentro de um ambiente virtual criado para o tópico de hoje. A lib xlsxwriter vai criar uma planilha do zero, a lib Faker vai criar dados aleatórios pra gente popular essa planilha, a lib xlrd vai abrir a planilha já criada e a lib python-docx vai criar um documento .docx a partir desses dados.
(venv) Extra 19$ pip install xlsxwriter Faker xlrd python-docx
Com tudo instalado. Podemos começar. Mas antes, vou dar uma pincelada na lib Faker, para na hora de criar o código eu poder passar mais rápido por ele.
>>> from faker import Faker
>>> faker = Faker('pt_BR')
>>> faker.name()
'Lucca da Cruz'
>>> faker.name_male()
'Lucas da Mata'
>>> faker.name_female()
'Alice Teixeira'
>>> faker.job()
'Chefe de cozinha'
>>> faker.date_time()
datetime.datetime(1999, 9, 27, 15, 30, 59)
>>> faker.pyint()
1122
Acima eu importei a lib Faker, criei uma instância de Faker na variável faker e passei como argumento 'pt_BR' para obter valores próprios do Brasil. Agora eu posso pedir nomes quaisquer, nomes masculinos, femininos, ocupações, datas, números inteiros, etc. Irei usar essa lib para povoar minha tabela com dados de pessoas fictícias.
Vou iniciar o código criando uma função que irá criar os dados fictícios para mim. Nela, irei instanciar a classe Faker, tal como feito acima, e a partir dessa instância eu vou gerar dados fictícios para os indivíduos.
Os dados que irei precisar são:
- Nome outorgante
- Nacionalidade outorgante
- Estado civil outorgante
- Profissão outorgante
- CPF outorgante
- RG outorgante
- Endereço outorgante
- Nome outorgado
- Nacionalidade outorgado
- Estado civil outorgado
- Profissão outorgado
- CPF outorgado
- RG outorgado
- Endereço outorgado
- Objetivo da procuração
- Validade da procuração
Irei criar boa parte desses dados usando Faker. Os que não forem possíveis serem criados dessa forma, irei criar um código específico para tal, tal como os dados de estado civil.
Ainda não abordei tudo sobre funções na parte teórica, mas neste tópico vou adicionar um elemento a mais em nossas funções que são as docstrings. Docstrings são strings criadas logo abaixo da definição da função e são usadas para explicar nossa função.
A função que irá criar os dados terá um dicionário que irá receber como chave strings como "Person1", "Person2" e como valor de cada chave uma lista de atributos de cada indivíduo, como nome, nacionalidade, estado civil, etc.
Como eu quero povoar meus dados com pessoas do sexo masculino e feminino, criei uma condicional dentro do laço que irá criar os dados e pedirei um número para o Python entre 0 e 1. Se o número sorteado for 1, outorgado e outorgante serão do sexo masculino. Se for 0, outorgante e outorgado serão do sexo feminino.
Segue o código da primeira função de nosso programa:
from faker import Faker
from random import randint
def create_data():
"""
This function creates the data of each person using an instance of Faker
"""
# Cria uma instância de Faker, com dados do Brasil
faker = Faker('pt_BR')
# Cria o dicionário no qual serão adicionados os dados
data = dict()
# Cria duas listas com dados de estado civil
# A lib Faker não dá suporte para esse dado
marital_status_male = ['Solteiro', 'Casado', 'Divorciado', 'Viúvo']
marital_status_female = ['Solteira', 'Casada', 'Divorciada', 'Viúva']
# Cria os dados usando duas possibilidades
# Se random_number == 1, os nomes são masculinos
# Se random_number == 0, os nomes são femininos
for i in range(11):
random_number = randint(0, 1)
if random_number:
data[f'Person{i}'] = [
faker.name_male(), 'Brasileiro',
marital_status_male[randint(0, len(marital_status_male)-1)],
faker.job(),
faker.pyint(min_value=10000000000, max_value=99999999999),
faker.pyint(min_value=1000000000000, max_value=9999999999999),
faker.address().replace('\ n', ' '), faker.name_male(), 'Brasileiro',
marital_status_male[randint(0, len(marital_status_male)-1)],
faker.job(),
faker.pyint(min_value=10000000000, max_value=99999999999),
faker.pyint(min_value=1000000000000, max_value=9999999999999),
faker.address().replace('\ n', ' '),
f'Representar perante a empresa {faker.company()} para blabla',
faker.date(pattern='%d-%m-%Y', end_datetime='+20y')
]
else:
data[f'Person{i}'] = [
faker.name_female(), 'Brasileira',
marital_status_female[randint(0, len(marital_status_female)-1)],
faker.job(),
faker.pyint(min_value=10000000000, max_value=99999999999),
faker.pyint(min_value=1000000000000, max_value=9999999999999),
faker.address().replace('\ n', ' '), faker.name_female(),
'Brasileira',
marital_status_female[randint(0, len(marital_status_female)-1)],
faker.job(),
faker.pyint(min_value=10000000000, max_value=99999999999),
faker.pyint(min_value=1000000000000, max_value=9999999999999),
faker.address().replace('\ n', ' '),
f'representar perante a empresa {faker.company()} para blabla',
faker.date(pattern='%d-%m-%Y', end_datetime='+20y')
]
return data
Cada item da lista de valores que cada pessoa recebe é um dos itens citados acima. Dessa forma, ao criar um individuo do sexo feminino:
- Nome será criado usando faker.name_female()
- A nacionalidade será "Brasileira" por default
- O estado civil será criado a partir da lista marital_status_female, sorteando um número aleatório dentro de 0 e 3, e dependendo do número o estado será solteira, casada, divorciada ou viúva
- Ocupação será dada usando faker.job()
- CPF e RG serão dados usando faker.pyint, que gera um número inteiro dentro de um intervalo. CPFs serão criados dentro de um intervalo de 11 algarismos e RGs num intervalor de 13 algarismos
- O endereço será criado a partir de faker.address(). Aqui há uma peculiaridade que é o fato de a lib gerar endereços com quebras de linha ("\ n"), no meu código eu irei trocar quebras de linha por espaços
- O objetivo irá usar faker.company() para gerar uma empresa fictícia dentro de uma f-string
- A data de validade da procuração será criada usando faker.date(), que recebe um padrão de data, e aqui usei dd-mm-aaaa, e botei um intervalo a mais de 20 anos
Percebam que todos esses dados estão sendo criados dentro de um laço e a cada volta no laço um indivíduo é adicionado ao dicionário "data". Foram criados 11 indivíduos.
Se invocarmos nossa função e pedirmos o retorno dela, teremos:
>>> data = create_data()
>>> data
{'Person0': ['Sra. Heloísa Costa', 'Brasileira', 'Divorciada', 'Veterinário', 76877313147, 6042158310470, 'Rua de Pires, 501 Madre Gertrudes 19095981 Farias / DF', 'Bianca da Cunha', 'Brasileira', 'Viúva', 'Profissional de manutenção industrial', 75097081293, 4373785866070, 'Esplanada de Nascimento, 98 Vila Santa Monica 1ª Seção 88335-907 Cavalcanti das Pedras / PI', 'representar perante a empresa Costa S.A. para blabla', '13-12-2026'],
...
...
'Person10': ['Ana Luiza Carvalho', 'Brasileira', 'Divorciada', 'Agente comunitário de saúde', 18437398918, 3255444403323, 'Quadra Felipe Rodrigues, 36 Prado 08167-241 Gomes do Sul / TO', 'Luna Costa', 'Brasileira', 'Solteira', 'Passador', 37157416728, 2886980120003, 'Esplanada da Luz, 760 Alto Dos Pinheiros 76105-103 Pereira da Praia / MS', 'representar perante a empresa Monteiro Ltda. para blabla', '09-07-2014']}
Temos nossos dados. Agora preciso criar uma planilha e povoá-la com esses dados. Para isso, irei usar a lib xlsxwriter. Irei criar uma função que criará a planilha. A função receberá como argumento nosso conjunto de dados recém criado. Os comandos internos a esta nova função são auto-explicativos, então não vou em alongar neles:
...
import xlsxwriter
...
def create_worksheet(person_data):
"""
This function creates a worksheet and put the data on it
"""
# Cria o arquivo .xlsx
workbook = xlsxwriter.Workbook('vizviz.xlsx')
# Cria a planilha dentro do arquivo
worksheet = workbook.add_worksheet()
# Cria os nomes das colunas
labels = (
'Nome Outorgante', 'Nacionalidade Outorgante',
'Estado Civil Outorgante', 'Profissão Outorgante',
'CPF Outorgante', 'RG Outorgante', 'Endereço Outorgante',
'Nome Outorgado', 'Nacionalidade Outorgado', 'Estado Civil Outorgado',
'Profissão Outorgado', 'CPF Outorgado', 'RG Outorgado',
'Endereço Outorgado', 'Objetivo procuração', 'Validade Procuração'
)
# Escreve os nomes das colunas na planilha
worksheet.write_row('A1', labels)
# Coloca os dados das pessoas na planilha
for i, person in enumerate(person_data.values()):
worksheet.write_row(f'A{i+2}', person)
# Fecha o arquivo
workbook.close()
Acima eu criei um arquivo chamado vizviz.xlsx e atribuí tal arquivo à variável workbook. Adicionei uma planilha a ele usando a função add_worksheet, e atribuí essa planilha à variável worksheet.
Criei uma tupla com labels, que serão os nomes das colunas da planilha.
Inicialmente, escrevi todas as labels nas colunas usando a função write_row, que escreveu a partir da célula A1 todas as labels em sequência na mesma linha.
Por fim, iterei sobre meu conjunto de dados, usando somente os valores do dicionário, esquecendo das chaves, e escrevi a partir da linha da célula A2 os dados de cara pessoa.
No final, fechei minha planilha com a função "close()". Vamos abrir nossa planilha recém criada e dar uma olhadela nela:

Notem que a planilha foi criada com sucesso e os dados estão lá. Agora falta eu abrir a planilha, resgatar os dados novamente e criar um documento usando um template padrão e só ir adicionando o que varia entre cada documento que são os nomes, endereços, etc.
Aqui, como estou usando o mesmo programa, eu poderia simplesmente pegar o retorno da função "create_data" para criar meu documento. Mas a intenção inicial era abrir os dados de uma planilha e criar o documento a partir dela. Então vou esquecer que criei esses dados no meu programa e vou resgatar todos os dados a partir da planilha recém criada. Aqui, usarei a lib xlrd, que é feita para abrir documentos no formato .xlsx e .xls e criarei um novo dicionário com os dados:
...
import xlrd
from os import listdir
...
def read_xlsx():
"""
This function read the created workbook
"""
# Pega o arquivo .xlsx no diretório atual
excel_file = [i for i in listdir('.') if i.endswith('.xlsx')][0]
# Abre o arquivo usando a lib xlrd
book = xlrd.open_workbook(excel_file)
# Pega a primeira planilha do arquivo
sheet = book.sheet_by_index(0)
# Adiciona os dados de cada pessoa a um dicionário
data = dict()
for i in range(1, sheet.nrows):
data[f'Person{i}'] = sheet.row_values(i)
return data
Logo de início eu capturei o arquivo recém criado usando a função listdir da lib "os". Ela lista todos os documentos em um diretório específico. Se eu não especificar diretório, basta usar ("."). Fiz uma list comprehension para listar todos os documentos que terminam em ".xlsx" e peguei o primeiro elemento da lista recém criada usando [0].
Se ficou confuso, vamos abrir o console Python e refazer esse passo:
>>> from os import listdir
>>> [i for i in listdir('.') if i.endswith('.xlsx')]
['vizviz.xlsx']
Criei em seguida a variável "book" e abri o arquivo, atribuindo o resultado a essa variável.
Capturei a primeira planilha usando a função "sheet_by_index" passando como argumento o índice 0.
Por fim, criei um novo dicionário, na variável "data", iterei sobre a quantidade de linhas da planilha, mas partindo da segunda linha (pois a primeira tem nossas labels e não quero as labels, só os dados) e adicionei cada dado ao dicionário. Por fim a função retorna nossos dados.
Esses dados retornados por essa função serão passados como argumento para nossa última função, que cria um documento .docx para cada indivíduo. Aqui, irei usar a lib python-docx. Ela tem suas limitações mas para o que precisamos aqui dá pro gasto.
...
from docx import Document
...
def create_docx(person_data):
"""
This function creates a template in docx file and add the data to it
for each person
"""
# Itera sobre os dados de cada pessoa, criando um documento para cada um
for person in person_data.values():
# Transforma os dados de cada pessoa em uma lista
data = list(person)
# Cria o documento
document = Document()
# Adiciona um título
document.add_heading('Procuração')
# Primeiro parágrafo
paragraph_1 = f'''Outorgante: Eu, {data[0]}, {data[1]}, {data[2]}, {data[3]}, portador (a) do CPF nº {data[4]}, e do RG nº {data[5]}, residente e domicilado (a) em {data[6]}, pelo presente instrumento, nomeio como meu (minha) procurador (a)'''
# Segundo parágrafo
paragraph_2 = f'''Outorgado (a): {data[7]}, {data[8]}, {data[9]}, {data[10]}, portador (a) do CPF nº {data[11]}, e do RG nº {data[12]}, residente e domiciliado (a) em {data[13]}, com poderes para representar o outorgante com objetivo de {data[14]}, responsabilizando-me por todos os atos praticados no cumprimento deste instrumento, cessando seus efeitos em {data[15]}.
Local, Data
Assinatura
'''
# Adiciona os parágrafos ao documento
document.add_paragraph(paragraph_1)
document.add_paragraph(paragraph_2)
# Salva o documento
document.save(f'procuracoes/Procuração de {data[0]}.docx')
Acima, novamente realizo uma iteração sobre os valores do dicionário. Mas para povoar o documento com os dados eu decidi pegar dado por dado por índice, e dicionários não suportam a busca por índice. Então transformei o retorno de person_data.values() em uma lista para poder acessar os valores por índice.
O resto do laço é auto-explicativo. Crio um documento, adiciono um título, adiciono o primeiro parágrafo e em seguida o segundo parágrafo a partir dos dados de cada indivíduo. Por fim adiciono os parágrafos ao documento e salvo cada documento na pasta "procuracoes" com o nome do indivíduo.
Tem formas melhores de criar esse template. Poderia ter sido criado num arquivo .txt e eu dentro do meu laço abrir o template e só adicionar os dados. Mas precisaria explicar mais algumas coisas e o post já está gigante, então fui pela solução mais grosseira mesmo.
Temos nossas 4 funções definidas. Agora, basta realizar a invocação de cada função e pegar os retornos que interessam de cada uma e passar como argumento para as funções que necessitam desses retornos:
data = create_data()
table = create_worksheet(data)
new_data = read_xlsx()
create_docx(new_data)
Rodando nosso código, temos:

Mas nossa procuração saiu com um errinho. A lib python-docx adicionou CPF e RG como float e não como int, então saiu um ".0" ao final. Posso resolver isso facilmente usando a função int() em cada um desses dados, tal como "int(data[11])". Corrigindo esse bug, temos:

Pronto, enfim temos nosso gerador de procurações e de quebra temos um gerador de nomes fictícios para testar nosso gerador de procurações.
O código completo ficou meio grande mas aqui está ele:
# Vizviz.py
# Criador de procurações em Python
import xlsxwriter
from docx import Document
import xlrd
from faker import Faker
from random import randint
from os import listdir
def create_worksheet(person_data):
"""
This function creates a worksheet and put the data on it
"""
# Cria o arquivo .xlsx
workbook = xlsxwriter.Workbook('vizviz.xlsx')
# Cria a planilha dentro do arquivo
worksheet = workbook.add_worksheet()
# Cria os nomes das colunas
labels = (
'Nome Outorgante', 'Nacionalidade Outorgante',
'Estado Civil Outorgante', 'Profissão Outorgante',
'CPF Outorgante', 'RG Outorgante', 'Endereço Outorgante',
'Nome Outorgado', 'Nacionalidade Outorgado', 'Estado Civil Outorgado',
'Profissão Outorgado', 'CPF Outorgado', 'RG Outorgado',
'Endereço Outorgado', 'Objetivo procuração', 'Validade Procuração'
)
# Escreve os nomes das colunas na planilha
worksheet.write_row('A1', labels)
# Coloca os dados das pessoas na planilha
for i, person in enumerate(person_data.values()):
worksheet.write_row(f'A{i+2}', person)
# Fecha o arquivo
workbook.close()
def create_data():
"""
This function creates the data of each person using an instance of Faker
"""
# Cria uma instância de Faker, com dados do Brasil
faker = Faker('pt_BR')
# Cria o dicionário no qual serão adicionados os dados
data = dict()
# Cria duas listas com dados de estado civil
# A lib Faker não dá suporte para esse dado
marital_status_male = ['Solteiro', 'Casado', 'Divorciado', 'Viúvo']
marital_status_female = ['Solteira', 'Casada', 'Divorciada', 'Viúva']
# Cria os dados usando duas possibilidades
# Se random_number == 1, os nomes são masculinos
# Se random_number == 0, os nomes são femininos
for i in range(11):
random_number = randint(0, 1)
if random_number:
data[f'Person{i}'] = [
faker.name_male(), 'Brasileiro',
marital_status_male[randint(0, len(marital_status_male)-1)],
faker.job(),
faker.pyint(min_value=10000000000, max_value=99999999999),
faker.pyint(min_value=1000000000000, max_value=9999999999999),
faker.address(), faker.name_male(), 'Brasileiro',
marital_status_male[randint(0, len(marital_status_male)-1)],
faker.job(),
faker.pyint(min_value=10000000000, max_value=99999999999),
faker.pyint(min_value=1000000000000, max_value=9999999999999),
faker.address().replace('\ n', ' '),
f'Representar perante a empresa {faker.company()} para blabla',
faker.date(pattern='%d-%m-%Y', end_datetime='+20y')
]
else:
data[f'Person{i}'] = [
faker.name_female(), 'Brasileira',
marital_status_female[randint(0, len(marital_status_female)-1)],
faker.job(),
faker.pyint(min_value=10000000000, max_value=99999999999),
faker.pyint(min_value=1000000000000, max_value=9999999999999),
faker.address().replace('\ n', ' '), faker.name_female(), 'Brasileira',
marital_status_female[randint(0, len(marital_status_female)-1)],
faker.job(),
faker.pyint(min_value=10000000000, max_value=99999999999),
faker.pyint(min_value=1000000000000, max_value=9999999999999),
faker.address(),
f'representar perante a empresa {faker.company()} para blabla',
faker.date(pattern='%d-%m-%Y', end_datetime='+20y')
]
return data
def read_xlsx():
"""
This function read the created workbook
"""
# Pega o arquivo .xlsx no diretório atual
excel_file = [i for i in listdir('.') if i.endswith('.xlsx')][0]
# Abre o arquivo usando a lib xlrd
book = xlrd.open_workbook(excel_file)
# Pega a primeira planilha do arquivo
sheet = book.sheet_by_index(0)
# Adiciona os dados de cada pessoa a um dicionário
data = dict()
for i in range(1, sheet.nrows):
data[f'Person{i}'] = sheet.row_values(i)
return data
def create_docx(person_data):
"""
This function creates a template in docx file and add the data to it
for each person
"""
# Itera sobre os dados de cada pessoa, criando um documento para cada um
for person in person_data.values():
# Transforma os dados de cada pessoa em uma lista
data = list(person)
# Cria o documento
document = Document()
# Adiciona um título
document.add_heading('Procuração')
# Primeiro parágrafo
paragraph_1 = f'''Outorgante: Eu, {data[0]}, {data[1]}, {data[2]}, {data[3]}, portador (a) do CPF nº {int(data[4])}, e do RG nº {int(data[5])}, residente e domicilado (a) em {data[6]}, pelo presente instrumento, nomeio como meu (minha) procurador (a)'''
# Segundo parágrafo
paragraph_2 = f'''Outorgado (a): {data[7]}, {data[8]}, {data[9]}, {data[10]}, portador (a) do CPF nº {int(data[11])}, e do RG nº {int(data[12])}, residente e domiciliado (a) em {data[13]}, com poderes para representar o outorgante com objetivo de {data[14]}, responsabilizando-me por todos os atos praticados no cumprimento deste instrumento, cessando seus efeitos em {data[15]}.
Local, Data
Assinatura
'''
# Adiciona os parágrafos ao documento
document.add_paragraph(paragraph_1)
document.add_paragraph(paragraph_2)
# Salva o documento
document.save(f'procuracoes/Procuração de {data[0]}.docx')
data = create_data()
table = create_worksheet(data)
new_data = read_xlsx()
create_docx(new_data)