Skip to content

realization_view

This submodule defines the collector for the RealizationView diagram.

COLLECTORS: dict[str, Collector] = {'ALL': collect_all, 'ABOVE': collect_realized, 'BELOW': collect_realizing} module-attribute 🔗

The functions to receive the diagram elements for every layer.

collect_all(start, depth) 🔗

Collect all elements in both ABOVE and BELOW directions.

Source code in capellambse_context_diagrams/collectors/realization_view.py
132
133
134
135
136
137
138
def collect_all(
    start: common.GenericElement, depth: int
) -> dict[LayerLiteral, list[dict[str, t.Any]]]:
    """Collect all elements in both ABOVE and BELOW directions."""
    above = collect_realized(start, depth)
    below = collect_realizing(start, depth)
    return above | below

collect_elements(start, depth, direction, attribute_prefix, origin=None) 🔗

Collect elements based on the specified direction and attribute name.

Source code in capellambse_context_diagrams/collectors/realization_view.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def collect_elements(
    start: common.GenericElement,
    depth: int,
    direction: str,
    attribute_prefix: str,
    origin: common.GenericElement | None = None,
) -> dict[LayerLiteral, list[dict[str, t.Any]]]:
    """Collect elements based on the specified direction and attribute name."""
    layer_obj, layer = find_layer(start)
    collected_elements: dict[LayerLiteral, list[dict[str, t.Any]]] = {}
    if direction == "ABOVE" or origin is None:
        collected_elements = {
            layer: [{"element": start, "origin": origin, "layer": layer_obj}]
        }
    elif direction == "BELOW" and origin is not None:
        collected_elements = {
            layer: [
                {
                    "element": origin,
                    "origin": start,
                    "layer": layer_obj,
                    "reverse": True,
                }
            ]
        }

    if (
        (direction == "ABOVE" and layer == "Operational")
        or (direction == "BELOW" and layer == "Physical")
        or depth == 0
    ):
        return collected_elements

    if isinstance(start, fa.Function):
        attribute_name = f"{attribute_prefix}_functions"
    elif isinstance(start, oa.OperationalActivity):
        attribute_name = f"{attribute_prefix}_system_functions"
    else:
        assert isinstance(start, cs.Component)
        attribute_name = f"{attribute_prefix}_components"

    for element in getattr(start, attribute_name, []):
        sub_collected = collect_elements(
            element, depth - 1, direction, attribute_prefix, origin=start
        )
        for sub_layer, sub_elements in sub_collected.items():
            collected_elements.setdefault(sub_layer, []).extend(sub_elements)
    return collected_elements

collect_realized(start, depth) 🔗

Collect all elements from realized_ attributes up to depth.

Source code in capellambse_context_diagrams/collectors/realization_view.py
118
119
120
121
122
def collect_realized(
    start: common.GenericElement, depth: int
) -> dict[LayerLiteral, list[dict[str, t.Any]]]:
    """Collect all elements from ``realized_`` attributes up to depth."""
    return collect_elements(start, depth, "ABOVE", "realized")

collect_realizing(start, depth) 🔗

Collect all elements from realizing_ attributes down to depth.

Source code in capellambse_context_diagrams/collectors/realization_view.py
125
126
127
128
129
def collect_realizing(
    start: common.GenericElement, depth: int
) -> dict[LayerLiteral, list[dict[str, t.Any]]]:
    """Collect all elements from ``realizing_`` attributes down to depth."""
    return collect_elements(start, depth, "BELOW", "realizing")

collector(diagram, params) 🔗

Return the class tree data for ELK.

Source code in capellambse_context_diagrams/collectors/realization_view.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def collector(
    diagram: context.ContextDiagram, params: dict[str, t.Any]
) -> tuple[_elkjs.ELKInputData, list[_elkjs.ELKInputEdge]]:
    """Return the class tree data for ELK."""
    data = makers.make_diagram(diagram)
    layout_options: _elkjs.LayoutOptions = copy.deepcopy(
        _elkjs.RECT_PACKING_LAYOUT_OPTIONS  # type:ignore[arg-type]
    )
    layout_options["elk.contentAlignment"] = "V_CENTER H_CENTER"
    del layout_options["widthApproximation.targetWidth"]
    data["layoutOptions"] = layout_options
    _collector = COLLECTORS[params.get("search_direction", "ALL")]
    lay_to_els = _collector(diagram.target, params.get("depth", 1))
    layer_layout_options: _elkjs.LayoutOptions = layout_options | {  # type: ignore[operator]
        "nodeSize.constraints": "[NODE_LABELS,MINIMUM_SIZE]",
    }  # type: ignore[assignment]
    edges: list[_elkjs.ELKInputEdge] = []
    for layer in ("Operational", "System", "Logical", "Physical"):
        if not (elements := lay_to_els.get(layer)):  # type: ignore[call-overload]
            continue

        labels = makers.make_label(layer)
        width, height = makers.calculate_height_and_width(labels)
        layer_box: _elkjs.ELKInputChild = {
            "id": elements[0]["layer"].uuid,
            "children": [],
            "height": width,
            "width": height,
            "layoutOptions": layer_layout_options,
        }
        children: dict[str, _elkjs.ELKInputChild] = {}
        for elt in elements:
            assert elt["element"] is not None
            if elt["origin"] is not None:
                edges.append(
                    {
                        "id": f'{elt["origin"].uuid}_{elt["element"].uuid}',
                        "sources": [elt["origin"].uuid],
                        "targets": [elt["element"].uuid],
                    }
                )

            if elt.get("reverse", False):
                source = elt["element"]
                target = elt["origin"]
            else:
                source = elt["origin"]
                target = elt["element"]

            if not (element_box := children.get(target.uuid)):
                element_box = makers.make_box(target, no_symbol=True)
                children[target.uuid] = element_box
                layer_box["children"].append(element_box)
                index = len(layer_box["children"]) - 1

                if params.get("show_owners"):
                    owner = target.owner
                    if not isinstance(owner, (fa.Function, cs.Component)):
                        continue

                    if not (owner_box := children.get(owner.uuid)):
                        owner_box = makers.make_box(
                            owner,
                            no_symbol=True,
                            layout_options=makers.DEFAULT_LABEL_LAYOUT_OPTIONS,
                        )
                        owner_box["height"] += element_box["height"]
                        children[owner.uuid] = owner_box
                        layer_box["children"].append(owner_box)

                    del layer_box["children"][index]
                    owner_box.setdefault("children", []).append(element_box)
                    owner_box["width"] += element_box["width"]
                    for label in owner_box["labels"]:
                        label["layoutOptions"].update(
                            makers.DEFAULT_LABEL_LAYOUT_OPTIONS
                        )

                    if (
                        source is not None
                        and source.owner.uuid in children
                        and owner.uuid in children
                    ):
                        eid = f"{source.owner.uuid}_{owner.uuid}"
                        edges.append(
                            {
                                "id": eid,
                                "sources": [source.owner.uuid],
                                "targets": [owner.uuid],
                            }
                        )

        data["children"].append(layer_box)
    return data, edges

find_layer(obj) 🔗

Return the layer object and its literal.

Return either one of the following
  • Operational
  • System
  • Logical
  • Physical
Source code in capellambse_context_diagrams/collectors/realization_view.py
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
def find_layer(
    obj: common.GenericElement,
) -> tuple[crosslayer.BaseArchitectureLayer, LayerLiteral]:
    """Return the layer object and its literal.

    Return either one of the following:
      * ``Operational``
      * ``System``
      * ``Logical``
      * ``Physical``
    """
    parent = obj
    while not isinstance(parent, crosslayer.BaseArchitectureLayer):
        parent = parent.parent
    if not (match := RE_LAYER_PTRN.match(type(parent).__name__)):
        raise ValueError("No layer was found.")
    return parent, match.group(1)  # type:ignore[return-value]