Skip to content

visualdynamics.io.report_template

report_template

A report saved on its own, as a template another project can use.

A report is a block model with symbolic bindings — @basis:Frf, @other:ShapeSet — so written without its project it is a template: the same figures and text, bound afresh to whatever project loads it (Brandon, 2026-09-08: "Is there a way for the user to save a report template or load a saved report template?"). The file is JSON with one marker key, the same four fields the project file keeps (native.py, save_report), under its own suffix so a folder of them reads as what it is. A block whose binding does not resolve in the new project is an unbound card there, to be repointed in the editor's pane, exactly as an unbound template block already is.

Templates saved into templates_folder() — the application's own folder under the user's application data — are offered by Generate Report beside the built-in ones, so a house template is one click away in every new project.

Functions:

Name Description
save

Write the report as a template. unit_system is taken for the

templates_folder

Where saved templates live: the application's folder under the

saved_templates

(name, path) for every template in the folder, by name.

Classes

Functions:

save

save(
    report: Report,
    path: str | PathLike,
    unit_system: Any = None,
    **_ignored: Any,
) -> None

Write the report as a template. unit_system is taken for the exporter's uniform signature and unused: a template carries no values, only what to draw and how to bind it.

Source code in src/visualdynamics/io/report_template.py
def save(report: Report, path: str | os.PathLike, unit_system: Any = None,
         **_ignored: Any) -> None:
    """Write the report as a template. `unit_system` is taken for the
    exporter's uniform signature and unused: a template carries no
    values, only what to draw and how to bind it."""
    out = {MARKER: 1, 'title': report.title, 'marking': report.marking,
           'marking_color': report.marking_color,
           'blocks': [dict(block) for block in report.blocks]}
    with open(path, 'w', encoding='utf-8') as handle:
        json.dump(out, handle, indent=1)
        handle.write('\n')

templates_folder

templates_folder() -> Path

Where saved templates live: the application's folder under the user's application data, as the platform lays it out. Created on demand by whoever writes there, not here.

Source code in src/visualdynamics/io/report_template.py
def templates_folder() -> Path:
    """Where saved templates live: the application's folder under the
    user's application data, as the platform lays it out. Created on
    demand by whoever writes there, not here."""
    override = os.environ.get('VISUALDYNAMICS_TEMPLATES')
    if override:
        return Path(override)
    if sys.platform == 'darwin':
        base = Path.home() / 'Library' / 'Application Support'
    elif sys.platform.startswith('win'):
        base = Path(os.environ.get('APPDATA', Path.home() / 'AppData' / 'Roaming'))
    else:
        base = Path(os.environ.get('XDG_DATA_HOME', Path.home() / '.local' / 'share'))
    return base / 'Visual Dynamics' / 'Report Templates'

saved_templates

saved_templates() -> list[tuple[str, Path]]

(name, path) for every template in the folder, by name.

Source code in src/visualdynamics/io/report_template.py
def saved_templates() -> list[tuple[str, Path]]:
    """(name, path) for every template in the folder, by name."""
    folder = templates_folder()
    if not folder.is_dir():
        return []
    return sorted((path.name[:-len(SUFFIX)], path)
                  for path in folder.iterdir()
                  if path.is_file() and path.name.endswith(SUFFIX))