Overview
The CalcTemplate Engine renders calculation reports from database-stored templates. Each template defines variables, sections, and equations. The engine evaluates SymPy expressions, substitutes values into LaTeX, and renders a formatted multi-section calculation document.
Rather than writing a separate management command per component, each
component opts in by adding a template_data.py file. A single
generic command (seed_calc_templates) discovers and seeds all
of them.
Quick Start
To add template data for a new component (e.g. c0042_my_calc):
-
Create
components/c0042_my_calc/template_data.pywith the four required exports:TEMPLATE_META,VARIABLES,SECTIONS,EQUATIONS. -
Run
python manage.py seed_calc_templates my_calc_slug --forceto seed (or re-seed) to the database. - Verify in the Engine Admin or by loading the calculation in the UI.
TEMPLATE_META = {
"name": "My Calculation",
"slug": "my_calc_slug",
"description": "Short description of what this calculates",
}
VARIABLES = [
{"ordering": 0, "name": "V", "symbol": "V", "label": "Voltage", "unit": "V", "is_input": True},
{"ordering": 1, "name": "I", "symbol": "I", "label": "Current", "unit": "A", "is_input": False},
]
SECTIONS = [
{"ordering": 0, "slug": "inputs_title", "title": "Input Parameters", "section_type": "title",
"text_content": "Design parameters:"},
{"ordering": 1, "slug": "inputs_table", "title": "Input Parameters", "section_type": "table",
"rows": [("V", "System voltage")]},
{"ordering": 2, "slug": "calc_section", "title": "Calculation", "section_type": "equations"},
]
EQUATIONS = [
(2, 0, "Current Calculation", "I", "P / V",
r"$$ I = \frac{P}{V} $$",
"$$ I = \\frac{P}{V} = \\frac{.P}{.V} = .I \\; \\text{A} $$",
"Ohm's law applied to find current."),
]
Data Format Reference
TEMPLATE_META
| Key | Type | Description |
|---|---|---|
name | str | Human-readable template name |
slug | str | Unique slug (used as DB key and CLI filter) |
description | str | Short description |
VARIABLES
List of dicts, one per variable. All variables must be defined before they can be referenced by sections or equations.
| Key | Type | Description |
|---|---|---|
ordering | int | Display order (0-based) |
name | str | Python-safe variable name (used in expressions) |
symbol | str | HTML symbol for display (e.g. V<sub>L-L</sub>) |
label | str | Human-readable label |
unit | str | Unit string (e.g. "V", "A", "kW"). Blank for dimensionless. |
is_input | bool | True for user inputs, False for calculated outputs |
SECTIONS
List of dicts defining the document structure. Sections are rendered in
ordering order.
| Key | Type | Description |
|---|---|---|
ordering | int | Render order (0-based). Also used to link equations. |
slug | str | Unique slug within template |
title | str | Section heading |
section_type | str | One of: title, table, equations, svg_display, text |
text_content | str | (Optional) Body text for title and text sections |
rows | list | (Optional) For table sections: list of (variable_name, notes) tuples |
EQUATIONS
List of tuples with 8 positional elements:
| # | Name | Type | Description |
|---|---|---|---|
| 0 | section_ordering | int | Links to the ordering of an equations-type section |
| 1 | equation_ordering | int | Order within the section (0-based) |
| 2 | title | str | Equation heading |
| 3 | output_variable | str | Name of the variable this equation solves for |
| 4 | expression | str | SymPy expression string |
| 5 | latex | str | LaTeX formula (static, no values substituted) |
| 6 | substitution_latex | str | LaTeX with .variable_name placeholders for substitution |
| 7 | notes | str | Explanatory text shown above/below the equation |
SymPy Expression Guide
The expression field is evaluated using SymPy's
sympify(). Use standard mathematical notation:
| Operation | Expression | Example |
|---|---|---|
| Square root | sqrt(x) | sqrt(3) * V * I |
| Power | x**n | I**2 * R |
| Pi | pi | acos(PF) * 180 / pi |
| Trig | sin(x), cos(x), tan(x) | cos(theta) |
| Inverse trig | asin(x), acos(x), atan(x) | acos(PF) |
| Logarithm | log(x) (natural), log(x, 10) | 20 * log(V1/V2, 10) |
| Absolute value | Abs(x) | Abs(V1 - V2) |
V_L_L not V(L-L).
The display symbol (symbol field) handles the pretty rendering.
LaTeX Guide
Static LaTeX (latex field)
Standard LaTeX wrapped in $$ ... $$ for display math. This is
shown as the "formula" line — no values, just the symbolic equation.
Substitution LaTeX (substitution_latex field)
Same structure but with .variable_name placeholders that get
replaced with computed values at render time.
.) marks a placeholder.
# Static:
r"$$ I = \frac{S}{\sqrt{3} \times V} $$"
# Substitution (note the dot-prefixed variable names):
"$$ I = \\frac{S}{\\sqrt{3} \\times V}"
"\n = \\frac{.S}{\\sqrt{3} \\times .V_L_L}"
"\n = .I \\; \\text{A} $$"
\n to break across lines. Each line shows one step of the
substitution (formula → values → result).
Section Types
| Type | Purpose | Required Fields |
|---|---|---|
title |
Section heading with optional body text | text_content (optional) |
table |
Variable table (shows symbol, value, unit, notes) | rows: list of (var_name, notes) tuples |
equations |
Equation block — renders all CalcEquations linked to this section's ordering | None (equations link via section_ordering) |
svg_display |
Embedded SVG diagram placeholder | SVG content provided at runtime |
text |
Freeform text block (supports HTML) | text_content |
ordering number. If section ordering 3 is an
equations section, then equation tuples with
section_ordering=3 will render inside it.
Management Command Reference
# List all discoverable template_data.py modules
python manage.py seed_calc_templates --list
# Seed all templates (skip existing)
python manage.py seed_calc_templates
# Seed all templates (delete and rebuild existing)
python manage.py seed_calc_templates --force
# Seed one specific template by slug
python manage.py seed_calc_templates three_phase_power
# Preview what would be seeded without DB changes
python manage.py seed_calc_templates --dry-run
# Combine: force re-seed a specific template
python manage.py seed_calc_templates three_phase_power --force
| Flag | Description |
|---|---|
slugs (positional) | One or more slugs to filter. Omit for all. |
--force | Delete existing templates before re-seeding |
--list | List discoverable modules and exit |
--dry-run | Print what would be seeded without writing to DB |
Worked Example
The c0001_vip_3_phase component is the reference implementation.
Here is a walkthrough of its template_data.py:
1. Template Meta
TEMPLATE_META = {
"name": "Three Phase Power",
"slug": "three_phase_power",
"description": "Three-phase V-I-P calculator (IEC)",
}
2. Variables
Define all inputs and outputs. The name field is what you use
in expressions and table row references.
VARIABLES = [
{"ordering": 0, "name": "V_L_L", "symbol": "V<sub>L-L</sub>",
"label": "Voltage (Phase to Phase)", "unit": "V", "is_input": True},
{"ordering": 1, "name": "I", "symbol": "I<sub>p</sub>",
"label": "Current (Phase)", "unit": "A", "is_input": True},
# ... more variables
]
3. Sections
Structure the document: title → input table → title → equations → summary table.
SECTIONS = [
{"ordering": 0, "slug": "input_parameters_title",
"title": "Input Parameters", "section_type": "title",
"text_content": "The following design parameters were considered:"},
{"ordering": 1, "slug": "input_parameters_table",
"title": "Input Parameters", "section_type": "table",
"rows": [("V_L_L", "3-Phase System"), ("I", "Phase Current")]},
{"ordering": 2, "slug": "calculations_title",
"title": "Calculations", "section_type": "title"},
{"ordering": 3, "slug": "current_equation",
"title": "Current Calculation", "section_type": "equations"},
# ... more equation sections (one per equation or group)
]
4. Equations
Each equation tuple links to a section by its ordering:
EQUATIONS = [
(3, 0, # section_ordering=3, equation_ordering=0
"Current Calculation", "I", # title, output variable
"S / (sqrt(3) * V_L_L)", # SymPy expression
r"$$ I = \frac{S}{\sqrt{3} \times V} $$", # static LaTeX
"$$ I = \\frac{.S}{\\sqrt{3} \\times .V_L_L} = .I \\; \\text{A} $$", # substitution
"Full-load current from apparent power"), # notes
]
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
Template 'slug' already exists |
Template was previously seeded | Use --force flag to re-seed |
row references unknown variable 'X' |
A table section row references a variable name not in VARIABLES | Check spelling of variable name in the rows list |
references section ordering N which does not exist |
An equation's section_ordering doesn't match any section |
Ensure the section with that ordering exists and is type equations |
missing required attributes: X, Y |
template_data.py is missing one of the four required exports |
Add the missing TEMPLATE_META, VARIABLES, SECTIONS, or EQUATIONS |
| SymPy evaluation error at runtime | Invalid expression string | Test with from sympy import sympify; sympify("your_expr") |
| LaTeX not rendering | Missing $$ delimiters or unescaped backslashes |
Use raw strings (r"...") for static LaTeX. For substitution LaTeX, double-escape: \\frac |
| Substitution placeholders not replaced | Missing dot prefix | Use .variable_name (with leading dot) in substitution_latex |