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

On this page