Calco

Python

The Calco client is a standard-library-only connector for Python 3.8+. It converts templates to PDF, HTML, CSV, ODS, and XLS with optional JSON data injection.

Add the connector

There's no PyPI package to install (yet) — the client is a small, standard-library-only package. Copy the calco/ connector directory (client.py and __init__.py) into your project, keeping the package name, so the import below resolves:

python
from calco import CalcoClient

Quick start

python
from calco import CalcoClient

client = CalcoClient("http://localhost:8080", api_key="dc_live_...")

# Convert a local XLSX file to PDF
pdf_bytes = client.convert("invoice_template.xlsx", format="pdf", data={"total": 100})
with open("invoice.pdf", "wb") as f:
    f.write(pdf_bytes)

The constructor is CalcoClient(base_url, timeout=120, api_key=None). The key is sent as Authorization: Bearer <key> on every request.

Converting a file

convert uploads a local file and returns the rendered bytes without storing anything. data accepts a dict or list and is serialized for you.

python
html = client.convert(
    "report.xlsx",
    format="html",  # "pdf" | "html" | "csv" | "ods" | "xls"
    data={"quarter": "Q1", "revenue": 50000},
    sheet="Summary",
    title="Q1 Report",
)

Extract the placeholder map with scaffold:

python
scaffold = client.scaffold("invoice_template.xlsx")

Managing templates

python
# Create
template = client.create_template(
    "invoice_v1.xlsx",
    name="Invoice",
    category="finance",
    tags="billing,invoice",
)

# List
templates = client.list_templates()

# Update metadata (only supplied fields change)
client.update_template(template_id=1, name="Invoice (updated)", tags="billing,v2")

# Add a new version
version = client.add_version(template_id=1, file_path="invoice_v2.xlsx")

# Scaffold for a specific version
scaffold = client.get_version_scaffold(template_id=1, version=2)

# Delete
client.delete_template(template_id=1)

Rendering a stored template

Render via POST with render_template, or fetch via GET with download_template:

python
pdf = client.render_template(
    template_id=1,
    version=2,
    data={"customer": "Acme Corp", "total": 1500},
    format="pdf",
    title="Invoice #1234",
)
with open("invoice_1234.pdf", "wb") as f:
    f.write(pdf)

Batch rendering

Render a list of records into a single ZIP archive:

python
records = [
    {"customer": "Acme Corp", "total": 1500},
    {"customer": "Globex Inc", "total": 2300},
]
zip_bytes = client.batch_render(
    template_id=1,
    records=records,
    format="pdf",
    title_template="Invoice-{customer}",
    version=2,
)
with open("invoices.zip", "wb") as f:
    f.write(zip_bytes)

By-hash workflow

Every stored version exposes a content hash (sha256 of the file). Address a version directly by hash:

python
meta = client.get_template_by_hash("3c9f...d1ab")
pdf = client.render_by_hash("3c9f...d1ab", data={"customer": "Jane"}, format="pdf")
xlsx = client.download_by_hash("3c9f...d1ab", format="xlsx")

Error handling

HTTP errors raise CalcoError, carrying the status code and message:

python
from calco import CalcoClient, CalcoError

client = CalcoClient("http://localhost:8080")

try:
    client.delete_template(999)
except CalcoError as e:
    print(e.status_code)  # 404
    print(e.message)      # "Template not found"

Framework integration

Django

python
# views.py
from django.http import HttpResponse
from calco import CalcoClient

client = CalcoClient("http://calco-service:8080")

def download_invoice(request, invoice_id):
    invoice = Invoice.objects.get(pk=invoice_id)
    pdf = client.render_template(
        template_id=invoice.template_id,
        version=invoice.template_version,
        data={
            "customer": invoice.customer_name,
            "items": [
                {"description": li.description, "amount": str(li.amount)}
                for li in invoice.lines.all()
            ],
            "total": str(invoice.total),
        },
        format="pdf",
        title=f"Invoice #{invoice.number}",
    )
    response = HttpResponse(pdf, content_type="application/pdf")
    response["Content-Disposition"] = f'attachment; filename="invoice_{invoice.number}.pdf"'
    return response

FastAPI

python
# main.py
from fastapi import FastAPI, HTTPException
from fastapi.responses import Response
from calco import CalcoClient, CalcoError

app = FastAPI()
client = CalcoClient("http://calco-service:8080")

@app.get("/invoices/{invoice_id}/pdf")
async def get_invoice_pdf(invoice_id: int):
    invoice_data = get_invoice_from_db(invoice_id)
    try:
        pdf = client.render_template(
            template_id=invoice_data["template_id"],
            version=invoice_data["template_version"],
            data=invoice_data["fields"],
            format="pdf",
            title=f"Invoice #{invoice_id}",
        )
    except CalcoError as e:
        raise HTTPException(status_code=e.status_code, detail=e.message)

    return Response(
        content=pdf,
        media_type="application/pdf",
        headers={"Content-Disposition": f'attachment; filename="invoice_{invoice_id}.pdf"'},
    )