Quickstart¶
This page builds a small sales report, checks it, and writes it to disk. Every
snippet uses only the public caxton facade.
1. Describe the data¶
Caxton reads rows through a lazy data source. Mappings, dataclasses,
NamedTuple values and plain attribute objects all work without registration.
from decimal import Decimal
SALES = (
{"product": "Coffee", "revenue": Decimal(1250), "cost": Decimal(700)},
{"product": "Tea", "revenue": Decimal(920), "cost": Decimal(510)},
)
2. Declare columns¶
A column has an id, a semantic type and a value source. By default the source
is the row field named after the column id.
from caxton import money, ref, text
columns = (
text("product").titled("Product").width(18),
money("revenue", currency="RUB").titled("Revenue"),
money("cost", currency="RUB").titled("Cost"),
money("profit", source=ref("revenue") - ref("cost"), currency="RUB").titled("Profit"),
)
ref() reads the evaluated value of another semantic column, so profit is
computed by Caxton before rendering. If you want the artifact to keep a live
formula instead, use col().
3. Compose the document¶
from caxton import sheet, spreadsheet, table
report = spreadsheet(
sheet(
"Sales",
table(SALES, *columns, name="sales", anchor="A3", freeze_header=True),
),
metadata={"example": "quickstart"},
)
table() coerces the row source once and stores it — it does not read any
row yet.
4. Validate before rendering¶
validate() checks structure — duplicate column ids, unknown references,
overlapping blocks — without consuming data.
Note
A clean validate() cannot guarantee a clean render for blocks whose shape
depends on the data (grouped tables and matrices). Those are re-checked
after their single preparation pass.
5. Inspect what the compiler decided¶
from caxton.testing import Rows, inspect_layout, inspect_spec
spec = inspect_spec(report)
assert spec.worksheet("Sales").table("sales").column_ids == (
"product",
"revenue",
"cost",
"profit",
)
layout = inspect_layout(report, rows=Rows.sample(1))
sales = layout.worksheet("Sales").table("sales")
assert sales.anchor == "A3"
assert sales.row(0)["profit"] == Decimal(550)
inspect_spec() never reads rows. inspect_layout() reads them only when you
ask for a scope with Rows.sample(n) or Rows.all().
6. Render or write¶
from io import BytesIO
from caxton import render, write
# In memory: the artifact bytes come back on the result.
result = render(report)
assert result.renderer == "xlsxwriter"
assert result.data is not None
# To a path: written atomically through a staging file.
write(report, "sales.xlsx")
# To a binary buffer.
buffer = BytesIO()
written = write(report, buffer, format="xlsx")
assert written.data == buffer.getvalue()
7. Check the finished file¶
from caxton.testing import inspect_artifact
artifact = inspect_artifact(result)
worksheet = artifact.worksheet("Sales")
assert worksheet.table("sales").column_titles == ("Product", "Revenue", "Cost", "Profit")
assert worksheet.cell("D4").value == 550
Next steps¶
- Core concepts — why the model looks like this.
- Tables and columns — sources, ids, titles.
- Rendering and output — backends, modes, sinks.