Skip to content

Instantly share code, notes, and snippets.

@elyssonmr
Last active July 23, 2025 22:19
Show Gist options
  • Save elyssonmr/b23bbfa4345300597d649a9ae74f316c to your computer and use it in GitHub Desktop.
Save elyssonmr/b23bbfa4345300597d649a9ae74f316c to your computer and use it in GitHub Desktop.
# Install faker: pip install Faker
from datetime import datetime, timezone, timedelta
import random
import time
import json
import os
import sys
from faker import Faker
faker_factory = Faker()
def get_invoice_data():
return {
'name': faker_factory.name(),
'address': faker_factory.address(),
'installation_identification': str(
faker_factory.random_number(digits=8, fix_len=True)
),
'due_date': (datetime.now(tz=timezone.utc) + timedelta(days=5)).isoformat(),
'consumption': (
f'{faker_factory.random_number(digits=3)}.'
f'{faker_factory.random_number(digits=2)}'
),
'email': faker_factory.email(True, domain='elyblog.com'),
}
def save_invoice_data(output_path, data):
int_timestamp = str(
datetime.now(tz=timezone.utc).timestamp()
).replace('.', '')
file_name = os.path.join(output_path, f'invoice_{int_timestamp}.json')
with open(file_name, mode='w', encoding='utf-8') as output_file:
json.dump(data, fp=output_file, indent=2)
return file_name
if __name__ == '__main__':
if len(sys.argv) == 1:
print('You should provide a path in order to save the invoice data')
sys.exit()
output_path = os.path.abspath(sys.argv[1])
if not os.path.exists(output_path):
print(f'Creating folder {output_path}')
os.mkdir(output_path)
print('Starting the invoice generator.\nYou can pause it using: CTRL+C (break)')
try:
while True:
invoice_name = save_invoice_data(output_path, get_invoice_data())
print(f'Invoice: {invoice_name} generated')
time.sleep(random.random() * 2)
except KeyboardInterrupt:
print('Stopping the generator')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ElyssonMR's Blog Consumption Invoice</title>
<style>
body {
font-family: 'Helvetica Neue', 'Helvetica', Arial, sans-serif;
margin: 0;
padding: 20px;
color: #333;
line-height: 1.6;
}
.container {
width: 80%;
margin: 0 auto;
background-color: #fff;
padding: 30px;
border: 1px solid #eee;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
}
.header {
text-align: center;
margin-bottom: 40px;
border-bottom: 2px solid #eee;
padding-bottom: 20px;
}
.header h1 {
color: #4CAF50; /* A pleasant green for the title */
margin: 0;
font-size: 2em;
}
.section {
margin-bottom: 30px;
}
.section h2 {
color: #555;
border-bottom: 1px solid #eee;
padding-bottom: 5px;
margin-bottom: 15px;
}
.data-item {
margin-bottom: 10px;
}
.data-item strong {
display: inline-block;
width: 150px; /* Aligning labels */
color: #666;
}
.footer {
text-align: center;
margin-top: 50px;
font-size: 0.9em;
color: #777;
border-top: 1px solid #eee;
padding-top: 20px;
}
/* Basic table styling for potential future use or structured data */
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
color: #555;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Consumption Report / Invoice</h1>
</div>
<div class="section">
<h2>Customer Information</h2>
<div class="data-item">
<strong>Name:</strong> {{ name }}
</div>
<div class="data-item">
<strong>Address:</strong> {{ address }}
</div>
</div>
<div class="section">
<h2>Installation Details</h2>
<div class="data-item">
<strong>Installation ID:</strong> {{ installation_identification }}
</div>
<div class="data-item">
<strong>Due Date:</strong> {{ due_date }}
</div>
</div>
<div class="section">
<h2>Consumption Details</h2>
<div class="data-item">
<strong>Consumption:</strong> {{ consumption }} kWh
</div>
</div>
<div class="footer">
<p>Thank you for your prompt payment.</p>
<p>Generated by Google Gemini for ElyssonMR's Blog</p>
</div>
</div>
</body>
</html>
# Install faker: pip install weasyprint Jinja2
from datetime import datetime, timezone
import time
import json
import os
import sys
from jinja2 import Environment, FileSystemLoader
from weasyprint import HTML
templates = Environment(loader=FileSystemLoader('.'))
def get_invoices(input_path):
invoices = []
for invoice in os.listdir(input_path)[:5]:
invoice_path = os.path.join(input_path, invoice)
if os.path.isfile(invoice_path):
invoices.append(invoice_path)
return invoices
def read_invoice_data(invoice_path):
with open(invoice_path, encoding='utf-8') as input_file:
return json.load(input_file)
def create_pdf(invoice_data):
template = templates.get_template('02_invoice_template.html')
html_content = template.render(invoice_data)
return HTML(string=html_content).write_pdf()
def save_pdf_file(output_path, data, client_email):
int_timestamp = str(
datetime.now(tz=timezone.utc).timestamp()
).replace('.', '')
file_name = os.path.join(
output_path,
f'invoice_{int_timestamp}_{client_email}.pdf'
)
with open(file_name, mode='wb') as output_file:
output_file.write(data)
return file_name
if __name__ == '__main__':
if len(sys.argv) < 3:
print('You should provide a path in order to read the invoice data and a path to save the generated PDF')
sys.exit()
input_path = os.path.abspath(sys.argv[1])
output_path = os.path.abspath(sys.argv[2])
if not os.path.exists(input_path):
print('Input path does not exist, make sure to run the generator application')
sys.exit()
if not os.path.exists(output_path):
print(f'Creating output folder: {output_path}')
os.mkdir(output_path)
print('Starting the PDF Generator.\nYou can pause it using: CTRL+C (break)')
try:
while True:
invoices = get_invoices(input_path)
if not invoices:
print(
'No invoices to process. Checking again in 5 seconds'
)
time.sleep(5)
continue
print(f'Found {len(invoices)} to process')
print('=' * 20)
for invoice in invoices:
print(f'Reading Invoice Data from: {invoice}')
invoice_data = read_invoice_data(
os.path.join(input_path,invoice)
)
print('Processing PDF')
pdf_content = create_pdf(invoice_data)
invoice_name = save_pdf_file(
output_path, pdf_content, invoice_data['email']
)
print(f'PDF: {invoice_name} generated')
os.remove(invoice)
print('Invoice Removed\n')
print('=' * 20)
except KeyboardInterrupt:
print('Stopping the generator')
import random
import time
import os
import sys
import re
def get_pdfs(input_path):
invoices = []
for invoice in os.listdir(input_path):
invoice_path = os.path.join(input_path, invoice)
if os.path.isfile(invoice_path):
invoices.append(invoice_path)
return invoices
def extract_email(pdf_path):
results = re.match(r'^.+invoice_\d+_(?P<email>.+)\.pdf$', pdf_path)
if results:
return results.group('email')
def send_pdf(pdf, email):
# Fake sending PDF trough email
time.sleep(random.random())
if __name__ == '__main__':
if len(sys.argv) == 1:
print(
'You should provide a path in order to '
'read the generated PDFs'
)
sys.exit()
input_path = os.path.abspath(sys.argv[1])
if not os.path.exists(input_path):
print(
'Input path does not exist, '
'make sure to run the PDF generator application'
)
sys.exit()
print(
'Starting the email sender.'
'\nYou can pause it using: CTRL+C (break)'
)
try:
while True:
pdfs = get_pdfs(input_path)
if len(pdfs) == 0:
print(
'There is no PDF to send. Checking again in 3 seconds'
)
time.sleep(3)
continue
print(f'Sending {len(pdfs)} emails with the PDF file')
print('=' * 20)
for pdf in pdfs:
email = extract_email(pdf)
print(f'Sending PDF to {email}')
send_pdf(pdf, email)
print('Email Sent!!')
os.remove(pdf)
print('=' * 20)
except KeyboardInterrupt:
print('Stopping the generator')
pip install Faker weasyprint Jinja2
python 01_data_producer.py invoice_data
python 02_pdf_writer.py invoice_data pdf_files
python 03_email_sender.py pdf_files
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment