Examples
Exemplos Python
Exemplos completos de integração com Python, Flask, Django, FastAPI e mais
Exemplos Python
Exemplos práticos e completos de como integrar o RenderHub em aplicações Python.
Instalação
pip install requests python-dotenv
# ou
poetry add requests python-dotenv
Configuração Básica
Variáveis de Ambiente
Crie um arquivo .env:
RENDERHUB_API_KEY=rh_live_sua_chave_aqui
RENDERHUB_BASE_URL=https://renderhub.com/api/v1
Cliente RenderHub
# lib/renderhub.py
import os
import base64
import requests
from typing import Dict, Any, Optional
from dotenv import load_dotenv
load_dotenv()
class RenderHubClient:
def __init__(self, api_key: Optional[str] = None):
self.api_key = api_key or os.getenv('RENDERHUB_API_KEY')
self.base_url = os.getenv('RENDERHUB_BASE_URL', 'https://renderhub.com/api/v1')
if not self.api_key:
raise ValueError('RENDERHUB_API_KEY não configurada')
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {self.api_key}',
'Content-Type': 'application/json'
})
def render(self, options: Dict[str, Any]) -> Dict[str, Any]:
"""Gera PDF a partir de HTML ou template"""
try:
response = self.session.post(
f'{self.base_url}/render',
json=options,
timeout=30
)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
self._handle_error(e.response)
except requests.exceptions.RequestException as e:
raise Exception(f'Erro na requisição: {str(e)}')
def create_template(self, template: Dict[str, Any]) -> Dict[str, Any]:
"""Cria um novo template"""
try:
response = self.session.post(
f'{self.base_url}/templates',
json=template,
timeout=30
)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
self._handle_error(e.response)
def get_template(self, template_id: str) -> Dict[str, Any]:
"""Busca template por ID"""
try:
response = self.session.get(
f'{self.base_url}/templates/{template_id}',
timeout=30
)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
self._handle_error(e.response)
def save_pdf(self, pdf_base64: str, filename: str):
"""Salva PDF base64 em arquivo"""
pdf_bytes = base64.b64decode(pdf_base64)
with open(filename, 'wb') as f:
f.write(pdf_bytes)
def _handle_error(self, response):
"""Trata erros da API"""
status = response.status_code
data = response.json() if response.content else {}
if status == 401:
raise Exception(f"Erro de autenticação: {data.get('message', 'Chave inválida')}")
elif status == 403:
raise Exception(f"Quota excedida: {data.get('message', '')}")
elif status == 429:
retry_after = data.get('retry_after', 60)
raise Exception(f"Rate limit excedido. Tente novamente em {retry_after}s")
else:
raise Exception(f"Erro {status}: {data.get('message', 'Erro desconhecido')}")
Exemplo 1: Converter HTML para PDF
# examples/convert_html.py
from lib.renderhub import RenderHubClient
def convert_html_to_pdf():
client = RenderHubClient()
html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body {
font-family: Arial, sans-serif;
padding: 40px;
max-width: 800px;
margin: 0 auto;
}
h1 {
color: #2563eb;
border-bottom: 3px solid #2563eb;
padding-bottom: 10px;
}
.highlight {
background: #fef3c7;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
</style>
</head>
<body>
<h1>Relatório de Vendas - Janeiro 2024</h1>
<div class="highlight">
<h2>Resumo</h2>
<p>Total de vendas: R$ 150.000,00</p>
<p>Novos clientes: 42</p>
<p>Taxa de conversão: 15.8%</p>
</div>
<h2>Detalhes</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
</body>
</html>
"""
result = client.render({
'mode': 'CONVERT',
'input_type': 'html',
'data': html,
'page_size': 'A4',
'orientation': 'portrait'
})
# Salvar PDF
client.save_pdf(result['pdf_base64'], 'relatorio.pdf')
print('✅ PDF gerado com sucesso!')
print(f"Páginas: {result['pages']}")
print(f"Tamanho: {result['file_size_kb']} KB")
print(f"Duração: {result['duration_ms']}ms")
if __name__ == '__main__':
convert_html_to_pdf()
Exemplo 2: Fatura com Template
# examples/generate_invoice.py
from lib.renderhub import RenderHubClient
from datetime import datetime
from typing import List, Dict
def create_invoice_template():
"""Cria template (executar apenas uma vez)"""
client = RenderHubClient()
with open('templates/invoice.html', 'r', encoding='utf-8') as f:
template_html = f.read()
template = client.create_template({
'name': 'invoice-template-v1',
'description': 'Template de fatura padrão',
'category': 'invoice',
'content': template_html,
'variables': [
{'name': 'company_name', 'type': 'string', 'required': True},
{'name': 'invoice_number', 'type': 'string', 'required': True},
{'name': 'customer_name', 'type': 'string', 'required': True},
{'name': 'items', 'type': 'array', 'required': True},
{'name': 'total', 'type': 'string', 'required': True}
]
})
print(f"Template criado: {template['id']}")
return template['id']
def generate_invoice(customer_id: int, template_id: str):
"""Gera fatura a partir do template"""
client = RenderHubClient()
# Buscar dados do cliente (simulado)
customer = {
'id': customer_id,
'name': 'João Silva Comércio LTDA',
'email': 'joao@example.com',
'address': 'Rua das Flores, 123',
'city': 'São Paulo',
'state': 'SP'
}
orders = [
{'product': 'Notebook Dell', 'quantity': 2, 'price': 3500.00},
{'product': 'Mouse Logitech', 'quantity': 5, 'price': 150.00},
{'product': 'Teclado Mecânico', 'quantity': 3, 'price': 450.00}
]
# Mapear dados para o template
invoice_data = {
'company_name': 'Minha Empresa LTDA',
'company_cnpj': '12.345.678/0001-99',
'invoice_number': f"2024-{customer_id:05d}",
'invoice_date': datetime.now().strftime('%d/%m/%Y'),
'customer_name': customer['name'],
'customer_email': customer['email'],
'customer_address': f"{customer['address']}, {customer['city']} - {customer['state']}",
'items': [
{
'description': order['product'],
'quantity': order['quantity'],
'unit_price': f"{order['price']:.2f}",
'total': f"{order['quantity'] * order['price']:.2f}"
}
for order in orders
],
'subtotal': f"{sum(o['quantity'] * o['price'] for o in orders):.2f}",
'tax': f"{sum(o['quantity'] * o['price'] for o in orders) * 0.15:.2f}",
'total': f"{sum(o['quantity'] * o['price'] for o in orders) * 1.15:.2f}"
}
# Renderizar PDF
result = client.render({
'mode': 'RENDER',
'template_id': template_id,
'data': invoice_data
})
# Salvar PDF
filename = f"invoice-{invoice_data['invoice_number']}.pdf"
client.save_pdf(result['pdf_base64'], filename)
print(f"✅ Fatura {invoice_data['invoice_number']} gerada!")
return filename
if __name__ == '__main__':
template_id = 'tpl_abc123' # Ou criar com create_invoice_template()
generate_invoice(1001, template_id)
Exemplo 3: Integração com Flask
# app.py
from flask import Flask, request, jsonify, send_file
from lib.renderhub import RenderHubClient
import io
import base64
app = Flask(__name__)
renderhub = RenderHubClient()
@app.route('/api/generate-pdf', methods=['POST'])
def generate_pdf():
"""Endpoint para gerar PDF"""
try:
data = request.get_json()
# Validar dados
if not data.get('template_id') or not data.get('data'):
return jsonify({
'error': 'template_id e data são obrigatórios'
}), 400
# Gerar PDF
result = renderhub.render({
'mode': 'RENDER',
'template_id': data['template_id'],
'data': data['data']
})
return jsonify({
'success': True,
'pdf_base64': result['pdf_base64'],
'pages': result['pages'],
'file_size_kb': result['file_size_kb']
})
except Exception as e:
return jsonify({
'error': 'Erro ao gerar PDF',
'message': str(e)
}), 500
@app.route('/api/download-pdf', methods=['POST'])
def download_pdf():
"""Endpoint para download direto"""
try:
data = request.get_json()
result = renderhub.render({
'mode': 'RENDER',
'template_id': data['template_id'],
'data': data['data']
})
# Converter base64 para bytes
pdf_bytes = base64.b64decode(result['pdf_base64'])
# Enviar como download
return send_file(
io.BytesIO(pdf_bytes),
mimetype='application/pdf',
as_attachment=True,
download_name='document.pdf'
)
except Exception as e:
return jsonify({'error': str(e)}), 500
if __name__ == '__main__':
app.run(debug=True, port=5000)
Exemplo 4: Integração com Django
# views.py
from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from lib.renderhub import RenderHubClient
import json
import base64
renderhub = RenderHubClient()
@csrf_exempt
@require_http_methods(["POST"])
def generate_pdf(request):
"""View para gerar PDF"""
try:
data = json.loads(request.body)
# Validar dados
if not data.get('template_id') or not data.get('data'):
return JsonResponse({
'error': 'template_id e data são obrigatórios'
}, status=400)
# Gerar PDF
result = renderhub.render({
'mode': 'RENDER',
'template_id': data['template_id'],
'data': data['data']
})
return JsonResponse({
'success': True,
'pdf_base64': result['pdf_base64'],
'pages': result['pages'],
'file_size_kb': result['file_size_kb']
})
except Exception as e:
return JsonResponse({
'error': 'Erro ao gerar PDF',
'message': str(e)
}, status=500)
@csrf_exempt
@require_http_methods(["POST"])
def download_pdf(request):
"""View para download direto"""
try:
data = json.loads(request.body)
result = renderhub.render({
'mode': 'RENDER',
'template_id': data['template_id'],
'data': data['data']
})
# Converter base64 para bytes
pdf_bytes = base64.b64decode(result['pdf_base64'])
# Retornar PDF
response = HttpResponse(pdf_bytes, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="document.pdf"'
return response
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
# urls.py
from django.urls import path
from . import views
urlpatterns = [
path('api/generate-pdf', views.generate_pdf, name='generate_pdf'),
path('api/download-pdf', views.download_pdf, name='download_pdf'),
]
Exemplo 5: Processamento em Lote com Celery
# tasks.py
from celery import Celery
from lib.renderhub import RenderHubClient
app = Celery('tasks', broker='redis://localhost:6379/0')
@app.task(bind=True, max_retries=3)
def generate_invoice_task(self, customer_id, template_id):
"""Task para gerar fatura"""
try:
print(f"Processando fatura para cliente {customer_id}...")
client = RenderHubClient()
# Buscar dados do cliente
customer_data = fetch_customer_data(customer_id)
# Gerar PDF
result = client.render({
'mode': 'RENDER',
'template_id': template_id,
'data': customer_data
})
# Salvar
filename = f"invoices/invoice-{customer_id}.pdf"
client.save_pdf(result['pdf_base64'], filename)
return {'filename': filename, 'customer_id': customer_id}
except Exception as e:
# Retry com backoff exponencial
raise self.retry(exc=e, countdown=2 ** self.request.retries)
def generate_invoices_for_all_customers(customer_ids, template_id):
"""Adiciona tasks à fila"""
for customer_id in customer_ids:
generate_invoice_task.delay(customer_id, template_id)
print(f"{len(customer_ids)} tasks adicionadas à fila")
def fetch_customer_data(customer_id):
"""Buscar dados do cliente"""
return {
'customer_name': f'Cliente {customer_id}',
'invoice_number': f'2024-{customer_id}',
'total': '1500.00'
}
if __name__ == '__main__':
customer_ids = [1001, 1002, 1003, 1004, 1005]
generate_invoices_for_all_customers(customer_ids, 'tpl_invoice_v1')
Exemplo 6: Webhooks com FastAPI
# webhook_server.py
from fastapi import FastAPI, HTTPException, Request, Header
from lib.renderhub import RenderHubClient
import hmac
import hashlib
import os
app = FastAPI()
renderhub = RenderHubClient()
WEBHOOK_SECRET = os.getenv('WEBHOOK_SECRET')
@app.post('/api/generate-async')
async def generate_async(request: Request):
"""Gera PDF com webhook"""
try:
data = await request.json()
result = renderhub.render({
'mode': 'RENDER',
'template_id': data['template_id'],
'data': data['data'],
'webhook_url': 'https://meuapp.com/webhooks/pdf-ready',
'webhook_headers': {
'X-Custom-Token': 'meu-token-secreto'
}
})
return {
'success': True,
'job_id': result['job_id'],
'status': result['status']
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post('/webhooks/pdf-ready')
async def pdf_ready_webhook(
request: Request,
x_renderhub_signature: str = Header(None)
):
"""Recebe callback do RenderHub"""
try:
# Validar assinatura HMAC
body = await request.body()
expected_signature = hmac.new(
WEBHOOK_SECRET.encode(),
body,
hashlib.sha256
).hexdigest()
if x_renderhub_signature != expected_signature:
raise HTTPException(status_code=401, detail='Invalid signature')
# Processar webhook
data = await request.json()
if data['status'] == 'DONE':
print(f"✅ PDF {data['id']} pronto!")
# Baixar PDF da URL temporária
import requests
pdf_response = requests.get(data['pdf_url'])
# Salvar em storage
with open(f"pdfs/{data['id']}.pdf", 'wb') as f:
f.write(pdf_response.content)
# Atualizar status no banco
# await db.jobs.update(data['job_id'], status='COMPLETED')
# Notificar usuário
# await notify_user(data['job_id'])
elif data['status'] == 'FAILED':
print(f"❌ Erro ao gerar PDF: {data['error']}")
# await db.jobs.update(data['job_id'], status='FAILED')
return {'received': True}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host='0.0.0.0', port=8000)
Exemplo 7: Retry com Backoff Exponencial
# lib/retry.py
import time
from typing import Callable, Any
def generate_pdf_with_retry(render_fn: Callable[[], Any], max_retries: int = 3):
"""Gera PDF com retry automático"""
for attempt in range(max_retries):
try:
return render_fn()
except Exception as e:
is_last_attempt = attempt == max_retries - 1
# Se rate limited, aguardar
if 'Rate limit' in str(e):
if is_last_attempt:
raise e
delay = min(2 ** attempt, 30) # Max 30s
print(f"Rate limited. Aguardando {delay}s...")
time.sleep(delay)
continue
# Outros erros, não fazer retry
raise e
# Uso
client = RenderHubClient()
result = generate_pdf_with_retry(
lambda: client.render({
'mode': 'CONVERT',
'input_type': 'html',
'data': '<h1>Test</h1>'
})
)
Exemplo 8: Geração em Paralelo com ThreadPoolExecutor
# examples/parallel_generation.py
from concurrent.futures import ThreadPoolExecutor, as_completed
from lib.renderhub import RenderHubClient
def generate_certificate(student_id, template_id):
"""Gera certificado para um aluno"""
client = RenderHubClient()
# Buscar dados do aluno
student_data = {
'student_name': f'Aluno {student_id}',
'course_name': 'Python Avançado',
'course_hours': 40,
'completion_date': '15/01/2024',
'has_distinction': student_id % 2 == 0
}
result = client.render({
'mode': 'RENDER',
'template_id': template_id,
'data': student_data
})
filename = f"certificates/certificate-{student_id}.pdf"
client.save_pdf(result['pdf_base64'], filename)
return student_id, filename
def generate_certificates_parallel(student_ids, template_id, max_workers=5):
"""Gera certificados em paralelo"""
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# Submeter todas as tasks
futures = {
executor.submit(generate_certificate, student_id, template_id): student_id
for student_id in student_ids
}
# Processar conforme completam
for future in as_completed(futures):
student_id = futures[future]
try:
student_id, filename = future.result()
print(f"✅ Certificado gerado para aluno {student_id}: {filename}")
except Exception as e:
print(f"❌ Erro ao gerar certificado para aluno {student_id}: {e}")
if __name__ == '__main__':
student_ids = range(1, 101) # 100 alunos
generate_certificates_parallel(student_ids, 'tpl_certificate_v1', max_workers=10)
Próximos Passos
- Exemplos Node.js - Integração com Node.js/Express
- Exemplos PHP - Integração com PHP/Laravel
- API Reference - Documentação completa da API
- Guia de Templates - Templates e variáveis