4. Using capellambse with Jinja2

Welcome to the py-capellambse jinja2 templating showcase. When using capella for systems engineering you might want to generate documentation for your model you can use M2DOC, one of capella’s addons which we found too challenging or you can use Jinja2 a richful templating language with high degree of freedom.

This notebook will introduce you into writing jinja2 templates where you’ll plant model information and diagrams. Additonally we’ll give a side-note on how to handle unique identifiers professionally with PVMT and some hints onto how to use jinja in a professional manner which could give you the option onto developping an automated document generation system.

With Jinja2 you are able to generate any text-based format(HTML, XML, CSV, LaTex,…) but during this tutorial we will only generate .html files. The jinja2 syntax is inspired by python. Check out their docs!

Below code loads the needed libraries and instantiates a test model:

[1]:
import jinja2
import capellambse

from IPython.core.display import HTML

path_to_model = "../../../tests/data/melodymodel/5_0/Melody Model Test.aird"
model = capellambse.MelodyModel(path_to_model)
env = jinja2.Environment()
Cannot load PVMT extension: ValueError: Provided model does not have a PropertyValuePkg
Property values are not available in this model

In the following we want to make a template to document all modelled actors from the logical layer. Therefore we define a template string where we iterate over all actors and plant the name, uuid and description into it.

Hint: Make sure that you know of capella’smetamodelas we are implementing the capellambse.layers as close as possible to it while being as efficient and pythonic we can be currently. This knowledge can shorten used statements in the template immensely!

[2]:
templ = """
<h1>Actor definitions</h1>
{% for actor in model.la.all_components.by_is_actor(True) %}
    <h2>{{ actor.name }}</h2>
    <h3>Actor definition</h3>
    <p>UUID: {{ actor.uuid }}</p>
    <p>{{ actor.description }}</p>
{% endfor %}
"""

HTML(env.from_string(templ).render(model=model))
[2]:

Actor definitions

Prof. A. P. W. B. Dumbledore

Actor definition

UUID: 08e02248-504d-4ed8-a295-c7682a614f66

Principal of Hogwarts, wearer of the elder wand and greatest mage of all time.

Prof. S. Snape

Actor definition

UUID: 6f463eed-c77b-4568-8078-beec0536f243

Good guy and teacher of brewing arts.

Harry J. Potter

Actor definition

UUID: a8c46457-a702-41c4-a971-c815c4c5a674

R. Weasley

Actor definition

UUID: ff7b8672-84db-4b93-9fea-22a410907fb1

Voldemort

Actor definition

UUID: 3e0ee19f-0e3f-49d4-ae99-29bd4a3260c5

Multiport

Actor definition

UUID: b3888dad-a870-4b8b-97d4-0ddb83ef9251

As an extra: We don’t use the UUID from capella in our documents. If you still need an identifier, in the following it’s explained how we are doing it:

With the capella PVMT, one of the various capella addons, you can make property value groups and then set arbitrary values with these. Capellambse is able to recognize the pvmt extension and gives read and write access. In our workflows we are maintaining an ID database for all model elements. If that is done you can access pvmt attributes like:

{{ ... }}
<p>ID: {{ actor.pvmt["Group.Identification.MY MODEL ID"] }}</p>
{{ ... }}

For a PVMT showcase look into [TODO: pvmt-showcase notebook].

4.1. Filters and object manipulation

Now to step up our templating-game, we’ll bring in more complexity. We want a template that documents functional context of all actors. For that iff the actor has a non-empty functions attribute we make a table with columns for function’s uid, name, description and FunctionalExchanges.

We can define variables in the template via the set statement. Furthermore the builtin jinja filters already give much power for object manipulation during rendering. Here we use map(.) to create FunctionalExchange iterators and sum these up into one large list that stores all FunctionalExchanges that have an an actor as either source or target. In the table for-loop we then filter on this lookup container and set outgoing and incoming FunctionalExchanges that we need for the last column.

The jupyter environment is great for writing templates b/c you can investigate possible attributes of objects right away in another cell.

Hint: You can define custom filter-functions and add them to the Environment.filters.

[3]:
templ1 = """
<h1>Actor definitions</h1>
{% set fexs = model.la.actor_exchanges.map("func_exchanges") %}
{% for actor in model.la.all_actors %}
    <h2>{{ actor.name }}</h2>
    <h3>Actor definition</h3>
    <p>{{ actor.description }}</p>
    <h3>Actor functions</h3>
    {% if actor.functions %}
        <p>The table below identifies functions of {{ actor.name }}.</p>
        <table>
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Function</th>
                    <th>Description</th>
                    <th>Involved Subsystems</th>
                </tr>
            </thead>
            <tbody>
            {% for fnc in actor.functions %}
                {% set outs = fexs.by_source.owner(fnc) %}
                {% set ins = fexs.by_target.owner(fnc) %}
                {% set subs = (ins + outs) | map(attribute="owner.name") | unique | sort %}
                <tr>
                    <td>{{ fnc.uuid }}</td>
                    <td>{{ fnc.name }}</td>
                    <td>{{ fnc.description }}</td>
                    <td>{{ subs | join(', ') }}</td>
                </tr>
            {% endfor %}
            </tbody>
        </table>
        <p style="margin-left: 1cm; font-weight: bold; font-style: italic;">Functions of {{ actor.name }}</p>
    {% else %}
        <p style="text-align: left;">No actor functions were identified.</p>
    {% endif %}
{% endfor %}
"""
HTML(env.from_string(templ1).render(model=model))
[3]:

Actor definitions

Harry J. Potter

Actor definition

Actor functions

The table below identifies functions of Harry J. Potter.

ID Function Description Involved Subsystems
aa9931e3-116c-461e-8215-6b9fdbdd4a1b kill He Who Must Not Be Named Learning

Functions of Harry J. Potter

Prof. A. P. W. B. Dumbledore

Actor definition

Principal of Hogwarts, wearer of the elder wand and greatest mage of all time.

Actor functions

The table below identifies functions of Prof. A. P. W. B. Dumbledore.

ID Function Description Involved Subsystems
f708bc29-d69f-42a0-90cc-11fc01054cd0 manage the school
beaf5ba4-8fa9-4342-911f-0266bb29be45 advise Harry

Functions of Prof. A. P. W. B. Dumbledore

Prof. S. Snape

Actor definition

Good guy and teacher of brewing arts.

Actor functions

The table below identifies functions of Prof. S. Snape.

ID Function Description Involved Subsystems
a7acb298-d14b-4707-a419-fea272434541 Teaching Teacher Responsibilities
4a2a7f3c-d223-4d44-94a7-50dd2906a70c maintain a layer of defense for the Sorcerer's Stone

Functions of Prof. S. Snape

Multiport

Actor definition

Actor functions

The table below identifies functions of Multiport.

ID Function Description Involved Subsystems
9c1885f5-fac7-48fd-9d54-a11092508867 LAF 1 C 5

Functions of Multiport

Voldemort

Actor definition

Actor functions

No actor functions were identified.

R. Weasley

Actor definition

Actor functions

The table below identifies functions of R. Weasley.

ID Function Description Involved Subsystems
c1a42acc-1f53-42bb-8404-77a5c08c414b assist Harry
edbd1ad4-31c0-4d53-b856-3ffa60e0e99b break school rules Punishment

Functions of R. Weasley

4.2. Beautiful SVG diagrams

Finally we will render a template that displays a diagram. There are many ways to do this and with jinja2 you have full control. We like our figures inside tables such that a caption can be displayed

[4]:
templ = """\
<h2>{{ component.name }}</h2>
{{ component.description }}
<table>
    <caption>Figure {{ fig_id }}: {{ fig_caption | e }}</caption>
    <tr><td>{{ figure.as_svg | safe }}</td></tr>
</table>
"""
diagram = model.diagrams.by_name("[LAB] Wizzard Education")
rendered = env.from_string(templ).render(
    component=model.search("LogicalComponent").by_name("Hogwarts"),
    fig_id=1,
    fig_caption=diagram.name,
    figure=diagram,
)
HTML(rendered)
Unknown global filter 'hide.sequencing.information.filter'
Unknown global filter 'ModelExtensionFilter'
Unknown global filter 'hide.simplified.diagram.based.component.exchanges.filter'
Unknown global filter 'hide.simplified.oriented.grouped.component.exchanges.filter'
Unknown global filter 'hide.simplified.group.of.component.exchanges.filter'
[4]:

Hogwarts

This instance is the mighty Hogwarts. Do you really need a description? Then maybe read the books or watch atleast the epic movies.

Figure 1: [LAB] Wizzard Education
LFHogwartsproduce GreatWizardsprotectStudentsagainst theDeath EatersCampusSchooleducate WizardsWhomping Willowdefend thesurrounding areaagainst IntrudersProf. A. P. W. B.Dumbledoremanage theschooladviseHarryProf. S. SnapeTeachingmaintain alayer ofdefense fortheSorcerer'sStoneHarry J. Potterkill He WhoMust Not BeNamedR. Weasleyassist Harrybreak schoolruleswizardryHeadmasterResponsibilitiesTeacherResponsibilitiesHelp for HarryKnowledgePunishmentLearningeducate & maturefriendshipassistanceCarepunisheducate

Hint: Take notice of thejinja.Environment. Instead of handing the figure_table-markup over in the rendering call you could also set an insert_figure_as_table function, which you ideally defined before, in the environment globals or you can definemacrosright in the template. These tools can automate repetitive content placement.

4.3. Template inheritance

The last cell will present a routine where a full .html document is generated. Earlier rendered content were HTML-fragments to be precise. On top you can see a showcase on jinja’s template-inheritance functionality. The special DictLoader gives support for finding the base template called “template.html”. This was just needed because we are dealing with content in memory and didn’t create template.html in our FileSystem before. Per default jinja is using the FileSystemLoader when creating an Environment. It’s not a bad idea to check the Loaders they have, if you want to understand how template loading is working and/or plan on developing a pipeline system for document distribution.

[5]:
fig_templ = "".join(
    (
        '{% extends "template.html" %}',
        "{% block content %}",
        templ,
        "{% endblock %}",
    )
)
final_templ = """
<!DOCTYPE html>
<html>

<head>
    <style>
        caption {
            caption-side: bottom;
            border-top: 1px solid silver;
        }
    </style>
</head>

<body>
    {% block content %}
    {% endblock %}
</body>
</html>
"""

env = jinja2.Environment(loader=jinja2.DictLoader({"template.html": final_templ}))
rendered = env.from_string(fig_templ).render(
    component=model.search("LogicalComponent").by_name("Hogwarts"),
    fig_id=1,
    fig_caption=diagram.name,
    figure=diagram,
)
HTML(rendered)
[5]:

Hogwarts

This instance is the mighty Hogwarts. Do you really need a description? Then maybe read the books or watch atleast the epic movies.

Figure 1: [LAB] Wizzard Education
LFHogwartsproduce GreatWizardsprotectStudentsagainst theDeath EatersCampusSchooleducate WizardsWhomping Willowdefend thesurrounding areaagainst IntrudersProf. A. P. W. B.Dumbledoremanage theschooladviseHarryProf. S. SnapeTeachingmaintain alayer ofdefense fortheSorcerer'sStoneHarry J. Potterkill He WhoMust Not BeNamedR. Weasleyassist Harrybreak schoolruleswizardryHeadmasterResponsibilitiesTeacherResponsibilitiesHelp for HarryKnowledgePunishmentLearningeducate & maturefriendshipassistanceCarepunisheducate