Report Generation
This example shows how to generate professional documents directly from SysML v2 models. It takes a set of SysML models and a markdown template as input, and produces publication-quality documents in a variety of formats (e.g. PDF, HTML, and DOCX).
The template system is general-purpose. You can produce any kind of document from your models. Here we walk through a requirements report as a concrete example, which includes traceability matrix, dependency graphs, and revision history.
What You Get
The generated document contains:
A title page with project metadata and revision history
Requirement tables with all attributes (status, description, justification, etc.)
Parent/child traceability between requirements
Auto-generated dependency graphs (SVG)
A traceability matrix linking requirements to system components
A changelog comparing the current version against the last tagged release
Note
The generated documents are also included in the downloadable example zip.
The input is a SysML v2 model containing requirements with attributes, documentation, and relationships. For example:
requirement 'STKR-EXEC-001' : ReqDefs::'Trading Firm Requirement' {
doc Description
/* System shall generate consistent risk-adjusted returns
* while maintaining regulatory compliance.
*/
doc Justification
/* Primary business objective to deliver value to investors
* while managing operational and regulatory risk.
*/
doc SuccessCriteria
/* Achieve minimum Sharpe ratio of 2.0 over rolling 12-month
* periods with zero regulatory violations.
*/
allocate 'Algorithmic Trading System Design'::'System Instances'
::'Algorithmic Trading Platform' to self;
}
Child requirements are linked to their parents through derivation connections:
#derivation connection 'STKR-EXEC-002 Derivation' {
end #original original_req references
'Stakeholder Requirements'::'Executive Stakeholder Requirements'
::'STKR-EXEC-002';
end #derive derived_req_1 references
'Stakeholder Requirements'::'User Stakeholder Requirements'
::'Compliance Requirements'::'STKR-COMP-001';
}
Setup
Download
Download and extract the example model and script into a folder.
External Dependencies
This example requires three external tools:
WeasyPrint (PDF generation)
Graphviz (dependency graph rendering)
Pandoc (DOCX conversion)
Install Graphviz and Pandoc using their official installers, or via Chocolatey:
choco install graphviz pandoc
WeasyPrint requires GTK3 runtime libraries (Pango, Cairo, etc.) to be installed.
Download and install the GTK3 Runtime
installer. After installation, ensure the GTK3 bin directory is on your system
PATH.
brew install weasyprint graphviz pandoc
sudo apt install weasyprint graphviz pandoc
Package names may differ on non-Debian distributions. See the official documentation for each tool linked above.
Python Environment
Create a Python virtual environment:
python -m venv .venv
Activate the virtual environment (see How to manually activate virtual environment)
Install the Python dependencies:
pip install -r requirements.txt
This installs Syside Automator and the libraries used for template processing and document conversion (Jinja2, WeasyPrint, pypandoc, Graphviz, etc.).
Running the Generator
Basic generation, producing PDF, HTML, and DOCX in the output directory:
python scripts/generate_docs.py --output ./output
With version tracking. This compares the current model against the latest git tag and updates the revision history and changelog:
python scripts/generate_docs.py --output ./output --update-version
CLI options:
Option |
Description |
|---|---|
|
Output directory for generated documents (required). |
|
Path to the directory containing SysML model files. Defaults to |
|
Compare requirements against the latest git tag, update
|
Project Structure
report_generation/
├── models/
│ ├── syside_demo_requirements.md # Jinja2 template (document structure)
│ ├── syside_demo_requirements.sysml # Requirements model
│ ├── syside_demo_common_defs.sysml # Requirement type definitions
│ ├── syside_demo_design.sysml # System design and components
│ ├── syside_demo_stakeholders.sysml # Stakeholder definitions
│ ├── syside_demo_verification.sysml # Verification test definitions
│ └── syside_demo_vvr.sysml # V&V requirements
├── scripts/
│ └── generate_docs.py # Generation script
├── assets/
│ ├── styles.css # PDF/HTML styling
│ ├── template.docx # DOCX reference template
│ └── logo_placeholder.png # Company logo
├── metadata/
│ └── versions.json # Revision history
├── output/ # Generated documents
└── requirements.txt # Python dependencies
The template file (syside_demo_requirements.md) lives alongside the SysML models in
models/ because it references model elements directly.
How It Works
The generation pipeline has four steps:
Model loading. The script loads all
*.sysmlfiles from the project directory using Syside Automator and builds an in-memory model.Template processing. The Jinja2 template (
syside_demo_requirements.md) is rendered. Template expressions call functions exposed by the script to extract data from the loaded model: requirements, attributes, relationships, documentation.Document assembly. The rendered markdown is converted to HTML. A table of contents is generated from the headings. CSS styling is applied, including headers/footers with project metadata.
Output conversion. The styled HTML is converted to three formats: HTML (direct), PDF (via WeasyPrint), and DOCX (via Pandoc).
Tip
The template is the main thing you will work with. The script provides the functions that the template calls. You should not need to modify the script unless you are adding entirely new data extraction logic.
The Template
The template file (models/syside_demo_requirements.md) is a markdown file with
three kinds of content: YAML frontmatter, static markdown, and Jinja2 expressions that
pull data from the model.
Frontmatter
The top of the template contains YAML metadata between --- delimiters. These fields
are used to populate the title page, headers, footers, and revision tracking:
project_title: "Algorithmic Trading System"
document_title: "Software Requirements Specification"
author: "Sensmetry"
project_reference: "DEMO-2025-001"
document_version: "1.1"
document_version_description: "Non-functional SysML changes."
company_website: "www.example.com"
company_email: "info@example.com"
company_phone: "XXX XXX XXXX"
These values are passed to template functions like title() and are also used by the
script to generate PDF headers and footers. To customize the output for your project,
update these fields.
Static Content
Regular markdown that appears in the output as-is. For example, the Introduction section of the demo template is plain markdown:
## Introduction
### Purpose
This document serves as the Software Requirements Specification (SRS) for the
Algorithmic Trading System. It outlines the comprehensive set of requirements
that must be met by the system...
You can add, remove, or edit static sections freely. They are standard markdown, no special syntax needed.
Dynamic Content
Jinja2 expressions ({{ }}) call template functions to extract and format data from
the loaded SysML model. Here is how the Requirements section of the demo template works:
## Requirements
{{
repeat_for_each_item(
data_source=get_children_with_attributes(
root_package="Algorithmic Trading Requirements",
metatype="RequirementUsage",
ignore_abstract_metatypes=True,
attributes={
"Name": "ElementName",
"Description": "Documentation",
"SuccessCriteria": "Documentation",
"Justification": "Documentation",
"Parents": "Req_Parents",
"Derivations": "Req_Derivations",
"Implemented": "Req_Implemented",
"verifyRequirement": "Req_Verified",
"Graph": "Req_DependencyGraph",
"Package": "OwningNamespace",
"Status": "AttributeUsage"
},
),
sections=[
{"type": "h3", "value": "unique_headers_from_qualified_name(row[9])"},
{"type": "h4", "value": "row[0]"},
{"type": "h5", "value": "Requirement details"},
{"type": "table", "value": "generic_table(
columns=[...],
rows=[row[10:11] + row[0:6] + row[8:9]],
flipRowsAndCols=True,
...
)"},
],
page_break_between_rows=True
)
}}
This breaks down as:
get_children_with_attributes()extracts allRequirementUsageelements from theAlgorithmic Trading Requirementspackage, along with their attributes. Each element becomes a row (a list of attribute values in the order defined by theattributesdictionary).repeat_for_each_item()loops over those rows. For each requirement, it renders thesectionslist: headings, tables, and text. Inside section values,row[0]refers to the first attribute (Name),row[1]to the second (Description), and so on.generic_table()formats a subset of each row’s attributes into an HTML table. TheflipRowsAndCols=Trueflag transposes it so attribute names are row headers (useful for single-item detail views).
Note
Row indices always correspond to the order of keys in the attributes dictionary.
If you add, remove, or reorder attributes, the indices in row[N] references and
row slicing (e.g., row[10:11] + row[0:6]) must be updated to match.
The template also uses simpler expressions for standalone elements:
{{ title() }} {# Title page from frontmatter #}
{{ toc() }} {# Table of contents #}
{{ page_break() }} {# Page break for PDF/DOCX #}
{{ revision_history() }} {# Version table from versions.json #}
{{ changelog() }} {# Diff against last git tag #}
Customization
Branding
Company information: update the frontmatter fields at the top of the template:
author: "Your Company Name" company_website: "www.yourcompany.com" company_email: "info@yourcompany.com" company_phone: "+1 234 567 8900"
Logo: replace
assets/logo_placeholder.pngwith your company logo.PDF/HTML styling: modify
assets/styles.cssto change fonts, colors, spacing, and table styles.DOCX styling: modify
assets/template.docxto set Word document styles (paragraph formats, heading styles, page layout).
Adding Attributes
To add a new attribute (e.g., Priority) to the generated requirements:
Add the attribute to your SysML model:
requirement 'STKR-RISK-001' : ReqDefs::'Risk Management Requirement' { attribute Priority = "High"; ... }
Add it to the
attributesdictionary in the template’s Requirements section:attributes={ "Name": "ElementName", "Description": "Documentation", ... "Status": "AttributeUsage", "Priority": "AttributeUsage" },Include it in the table columns and row slicing. The new attribute’s index corresponds to its position in the
attributesdictionary (in this case,row[11]):columns=["Status", "Identifier", "Description", ..., "Priority"], rows=[row[10:11] + row[0:6] + row[8:9] + row[11:12]],
Regenerate the document.
Changing Model References
If your SysML model uses different package names, update the root_package parameter
in all template function calls. For example:
root_package="'Algorithmic Trading Requirements'"
becomes:
root_package="'Your Package Name'"
The metatype parameter controls which kind of elements are extracted. Common values:
Metatype |
Extracts |
|---|---|
|
Requirements |
|
Generic items (used for reference documents in the demo) |
|
System components (used in the traceability matrix) |
|
Requirement type definitions |
Tables and Layout
The generic_table() function accepts several layout parameters:
Parameter |
Description |
|---|---|
|
Column width percentages, e.g., |
|
CSS class for custom styling (e.g., |
|
Text alignment per column: |
|
Transpose the table so columns become rows. Useful for single-item detail views where attribute names are row headers. |
Requirement Relationships
The template can extract several types of requirement relationships. These are specified
as attribute types in the attributes dictionary:
Attribute type |
Description |
|---|---|
|
Parent requirements (requirements that derive this one) |
|
Derived child requirements |
|
Components allocated to implement this requirement |
|
Verification elements linked to this requirement |
|
Auto-generated SVG showing the derivation tree |
These relationships are extracted from the SysML model structure: derivation connections, allocation usages, and verification memberships respectively.
Adding and Removing Sections
Tip
The template is a plain markdown file. You can freely add, remove, or reorder sections.
To add a new section, insert markdown and template expressions at the desired position. To remove a section, delete it. The script processes whatever is in the template top-to-bottom.
For example, to add a summary table of all requirements (without per-requirement detail pages), you could add:
## Requirements Summary
{{
generic_table(
columns=["Status", "Name", "Description"],
rows=get_children_with_attributes(
root_package="Algorithmic Trading Requirements",
metatype="RequirementUsage",
ignore_abstract_metatypes=True,
attributes={
"Name": "ElementName",
"": "Documentation",
"Status": "AttributeUsage"
}
),
widths=["15%", "25%", "60%"]
)
}}
Template Function Reference
All functions below are available inside {{ }} expressions in the template.
Document Structure Functions
title()Generates the title page HTML using frontmatter metadata (project title, document title, author, reference number, version, company info, and logo).
toc()Generates a table of contents from document headings (up to 4 levels deep). The placeholder is replaced after the full document is rendered, so it reflects all headings.
page_break()Inserts a page break. Rendered as a
<div>with CSSpage-break-after: always, which applies to PDF and DOCX output.revision_history()Generates a revision history table from
metadata/versions.json. Each entry shows version, date, description, and a summary of changes.changelog()Compares the current model against the latest git tag and generates a detailed changelog showing added, modified, and deleted requirements with their attribute changes. Requires the
--update-versionCLI flag to be active.
Data Extraction Functions
get_children_with_attributes(...)Extracts elements from the SysML model and returns their attributes. Each element becomes a row (a list of strings), with values in the same order as the
attributesdictionary.Parameters:
Parameter
Description
root_packageQualified name of the package to search, e.g.,
"Algorithmic Trading Requirements"metatypeElement type to extract (
"RequirementUsage","ItemUsage","PartUsage","RequirementDefinition", etc.)ignore_abstract_metatypesIf
True, skip abstract type definitionsattributesDictionary mapping display names to extraction types (see table below)
Extraction types for the
attributesdictionary:Type
Description
"ElementName"The element’s name (dictionary key is ignored)
"Documentation"Documentation body. The key selects which
docblock: use""for the default (unnamed) doc, or a name like"Description"to matchdoc Description /* ... */"AttributeUsage"Value of a named attribute. The key specifies the attribute name (e.g.,
"Status","Prefix")"OwningNamespace"Qualified name of the parent namespace
"Req_Parents"Parent requirements (via derivation connections)
"Req_Derivations"Derived child requirements
"Req_Implemented"Allocated components (via allocation usages)
"Req_Verified"Verification elements (via verification memberships)
"Req_DependencyGraph"SVG dependency graph of derivation relationships
get_docs(...)Extracts documentation elements from a package. Returns a list of
[doc_name, doc_body]pairs. Ifdoc_nameis provided, only matching documentation blocks are returned.
Formatting Functions
generic_table(...)Generates an HTML table.
Parameter
Description
columnsColumn header labels
rowsList of rows, each a list of cell values
flipRowsAndColsTranspose the table (columns become rows). Default
FalsewidthsColumn width percentages, e.g.,
["25%", "75%"]css_classCSS class to apply (e.g.,
"table-2d","matrix-table")col_alignmentPer-column alignment:
"left","right", or"center"headless_table(...)Like
generic_tablebut uses the first row ofdataas column headers.list_items(...)Formats rows as a markdown list. Use
{0},{1}, etc. to reference columns by index (matching the order in theattributesdictionary).Example format string:
"- **[{0}]({2})** - {1}"This produces a list of linked items.
text(...)Extracts specific rows from data and formats them. The
rowsparameter selects which rows to include by index. Useful for extracting a single value.
Iteration Functions
repeat_for_each_item(...)Loops over
data_sourceand renderssectionsfor each row. Each section is a dictionary with two keys:Key
Description
"type""h1"through"h6"for headings,"table"for tables,"body"for text"value"A string that can reference
row[0],row[1], etc. For tables, the value is ageneric_table(...)call as a string, which is evaluated with the current row in scope.Set
page_break_between_rows=Trueto insert page breaks between items.
Specialized Functions
traceability_matrix(...)Builds a traceability matrix. Rows are elements from
row_package(e.g., requirements), columns are elements fromcol_package(e.g., system components). Cells are marked withXwhere an allocation relationship exists. The result is passed toheadless_table()for rendering.unique_headers_from_qualified_name(...)Extracts the last segment of a qualified namespace name and deduplicates it across calls. Useful for generating package-level section headings when iterating over requirements. A new heading is only emitted when the package changes.