From 1733ff1274596d3d30fcf206d904a9f52f037f96 Mon Sep 17 00:00:00 2001 From: ewuerger Date: Tue, 7 May 2024 16:40:56 +0200 Subject: [PATCH] feat: Add non-existent `ComponentPort`s --- .../collectors/default.py | 25 +++++++++++-------- .../collectors/makers.py | 18 ++++++++----- capellambse_context_diagrams/serializers.py | 21 ++++++---------- tests/test_context_diagrams.py | 5 ++-- 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/capellambse_context_diagrams/collectors/default.py b/capellambse_context_diagrams/collectors/default.py index e686baff..9994e2aa 100644 --- a/capellambse_context_diagrams/collectors/default.py +++ b/capellambse_context_diagrams/collectors/default.py @@ -12,7 +12,8 @@ from capellambse import helpers from capellambse.model import common from capellambse.model.crosslayer import cs, fa -from capellambse.model.layers import ctx, la +from capellambse.model.layers import ctx as sa +from capellambse.model.layers import la from capellambse.model.modeltypes import DiagramType as DT from .. import _elkjs, context @@ -23,6 +24,9 @@ def collector( diagram: context.ContextDiagram, params: dict[str, t.Any] | None = None ) -> _elkjs.ELKInputData: """Collect context data from ports of centric box.""" + diagram.display_derived_interfaces = (params or {}).pop( + "display_derived_interfaces", diagram.display_derived_interfaces + ) data = generic.collector(diagram, no_symbol=True) ports = port_collector(diagram.target, diagram.type) centerbox = data["children"][0] @@ -310,24 +314,25 @@ def _derive_from_functions( # TODO: Even out derived interfaces on each side - for i, (uuid, derived_component) in enumerate(components.items()): + centerbox = data["children"][0] + for i, (uuid, derived_component) in enumerate(components.items(), 1): box = makers.make_box( derived_component, no_symbol=diagram.display_symbols_as_boxes, ) class_ = type(derived_comp).__name__ - box["id"] = comp_uuid = f"__Derived-{class_}_{uuid}" + box["id"] = f"__Derived-{class_}:{uuid}" data["children"].append(box) + source_id = f"__Derived-CP_INOUT:{i}" + target_id = f"__Derived-CP_INOUT:{-i}" + box.setdefault("ports", []).append(makers.make_port(source_id)) + centerbox.setdefault("ports", []).append(makers.make_port(target_id)) if i % 2 == 0: - source_id = comp_uuid - target_id = diagram.target.uuid - else: - source_id = diagram.target.uuid - target_id = comp_uuid + source_id, target_id = target_id, source_id data["edges"].append( { - "id": f"__Derived-ComponentExchange_{i}", + "id": f"__Derived-ComponentExchange:{i}", "sources": [source_id], "targets": [target_id], } @@ -341,5 +346,5 @@ def _derive_from_functions( DERIVATORS = { la.LogicalComponent: _derive_from_functions, - ctx.SystemComponent: _derive_from_functions, + sa.SystemComponent: _derive_from_functions, } diff --git a/capellambse_context_diagrams/collectors/makers.py b/capellambse_context_diagrams/collectors/makers.py index 672c3869..fdea3a12 100644 --- a/capellambse_context_diagrams/collectors/makers.py +++ b/capellambse_context_diagrams/collectors/makers.py @@ -42,10 +42,12 @@ FAULT_PAD = 10 """Height adjustment for labels.""" BOX_TO_SYMBOL = ( - layers.ctx.Capability, - layers.oa.OperationalCapability, - layers.ctx.Mission, - layers.ctx.SystemComponent, + layers.ctx.Capability.__name__, + layers.oa.OperationalCapability.__name__, + layers.ctx.Mission.__name__, + layers.ctx.SystemComponent.__name__, + "SystemHumanActor", + "SystemActor", ) """ Types that need to be converted to symbols during serialization if @@ -189,9 +191,13 @@ def calculate_height_and_width( return width, max(height, _height) -def is_symbol(obj: common.GenericElement) -> bool: +def is_symbol(obj: str | common.GenericElement | None) -> bool: """Check if given `obj` is rendered as a Symbol instead of a Box.""" - return isinstance(obj, BOX_TO_SYMBOL) + if obj is None: + return False + elif isinstance(obj, str): + return obj in BOX_TO_SYMBOL + return type(obj).__name__ in BOX_TO_SYMBOL def make_port(uuid: str) -> _elkjs.ELKInputPort: diff --git a/capellambse_context_diagrams/serializers.py b/capellambse_context_diagrams/serializers.py index 69dfde3f..760a43b7 100644 --- a/capellambse_context_diagrams/serializers.py +++ b/capellambse_context_diagrams/serializers.py @@ -17,7 +17,8 @@ from capellambse import diagram from capellambse.svg import decorations -from . import _elkjs, collectors, context +from . import _elkjs, context +from .collectors import makers logger = logging.getLogger(__name__) @@ -116,7 +117,7 @@ class type that stores all previously named classes. styleclass: str | None derived = False if child["id"].startswith("__"): - styleclass, uuid = child["id"][2:].split("_", 1) + styleclass, uuid = child["id"][2:].split(":", 1) if styleclass.startswith("Derived-"): styleclass = styleclass.removeprefix("Derived-") derived = True @@ -128,18 +129,12 @@ class type that stores all previously named classes. element: diagram.Box | diagram.Edge | diagram.Circle if child["type"] in {"node", "port"}: assert parent is None or isinstance(parent, diagram.Box) - has_symbol_cls = False - try: - obj = self.model.by_uuid(uuid) - has_symbol_cls = collectors.makers.is_symbol(obj) - except KeyError: - logger.error("ModelObject could not be found: '%s'", uuid) - + has_symbol_cls = makers.is_symbol(styleclass) is_port = child["type"] == "port" box_type = ("box", "symbol")[ is_port or has_symbol_cls - and not self._diagram.target == obj + and not self._diagram.target.uuid == uuid and not self._diagram.display_symbols_as_boxes ] @@ -170,11 +165,11 @@ class type that stores all previously named classes. source_id = child["sourceId"] if source_id.startswith("__"): - source_id = source_id[2:].split("_", 1)[-1] + source_id = source_id[2:].split(":", 1)[-1] target_id = child["targetId"] if target_id.startswith("__"): - target_id = target_id[2:].split("_", 1)[-1] + target_id = target_id[2:].split(":", 1)[-1] if child["routingPoints"]: refpoints = [ @@ -275,7 +270,7 @@ def get_styleclass(self, uuid: str) -> str | None: except KeyError: if not uuid.startswith("__"): return None - return uuid[2:].split("_", 1)[0] + return uuid[2:].split(":", 1)[0] else: return diagram.get_styleclass(melodyobj) diff --git a/tests/test_context_diagrams.py b/tests/test_context_diagrams.py index 42f882df..da9121d0 100644 --- a/tests/test_context_diagrams.py +++ b/tests/test_context_diagrams.py @@ -158,7 +158,6 @@ def test_context_diagram_with_derived_interfaces( diag = obj.context_diagram diag.display_derived_interfaces = True + diagram = diag.render(None) - diag.render("svgdiagram").save(pretty=True) - - assert len(diag.nodes) > 5 + assert len(diagram) > 5