diff --git a/model_diffsummary/__main__.py b/model_diffsummary/__main__.py index 71b00d4..d2c8bc5 100644 --- a/model_diffsummary/__main__.py +++ b/model_diffsummary/__main__.py @@ -4,10 +4,12 @@ from __future__ import annotations import logging +import pathlib import typing as t import capellambse import click +import yaml from capellambse import model as m from capellambse.model import common as c @@ -34,9 +36,119 @@ def main(model: dict[str, t.Any], old_version: str, new_version: str): old_model = capellambse.MelodyModel(**model, revision=old_version) new_model = capellambse.MelodyModel(**model, revision=new_version) - result = _compare_all_objects(old_model, new_model) + metadata = { + "model": model["path"], + "old-revision": old_version, + "new-revision": new_version, + } + diagrams = _compare_all_diagrams(old_model, new_model) - print(result) + objects = _compare_all_objects(old_model, new_model) + + result = {"metadata": metadata, "diagrams": diagrams, "objects": objects} + + # pathlib.Path("test.yaml").write_text(yaml.dump(result), encoding="utf8") + + +def _compare_all_diagrams( + old: capellambse.MelodyModel, new: capellambse.MelodyModel +) -> t.Any: + result: t.Any = {"oa": {}, "la": {}, "pa": {}, "sa": {}} + for layer, diagtype in ( + ("oa", m.modeltypes.DiagramType.COC), + ("oa", m.modeltypes.DiagramType.OAIB), + ("oa", m.modeltypes.DiagramType.OPD), + ("oa", m.modeltypes.DiagramType.OAB), + ("sa", m.modeltypes.DiagramType.CC), + ("sa", m.modeltypes.DiagramType.SDFB), + ("sa", m.modeltypes.DiagramType.MCB), + ("sa", m.modeltypes.DiagramType.SAB), + ): + type_result = _compare_diagram_type(old, new, layer, diagtype) + result[layer][diagtype.name] = type_result + return result + + +def _compare_diagram_type( + old: capellambse.MelodyModel, + new: capellambse.MelodyModel, + layer: str, + diag_type: m.modeltypes.DiagramType, +) -> t.Any: + logging.debug("Collecting diagrams of type %s", diag_type.name) + changes = {} + + old_diags = getattr(old, layer).diagrams.by_type(diag_type) + new_diags = getattr(new, layer).diagrams.by_type(diag_type) + + old_uuids = {i.uuid for i in old_diags} + new_uuids = {i.uuid for i in new_diags} + + if created_uuids := new_uuids - old_uuids: + changes["created"] = [new_diags.by_uuid(i).name for i in created_uuids] + if deleted_uuids := old_uuids - new_uuids: + changes["deleted"] = [old_diags.by_uuid(i).name for i in deleted_uuids] + + # for i in old_uuids & new_uuids: + # if diff := _diag2diff(old_diags.by_uuid(i), new_diags.by_uuid(i)): + # changes.setdefault("modified", []).append(diff) + return changes + + +def _diag2diff( + old: m.diagram.Diagram, new: m.diagram.Diagram +) -> dict[str, t.Any] | None: + """Serialize the differences between the old and new diagram. + + This function is used for diagrams that were modified. Newly + introduced elements and removed elements are serialized. + + The new (current) *display-name* is always serialized. If it didn't + change, it will not have the "previous" key. + + The *layout_changes* flag indicates that the diagram has changed + positions, sizes or bendpoints for exchanges. + """ + changes: t.Any = {} + + old_uuids = set(old.nodes.by_uuid) + new_uuids = set(new.nodes.by_uuid) + + if created_uuids := new_uuids - old_uuids: + changes["introduced"] = [ + _diagelt2dict(new.nodes.by_uuid(i)) for i in created_uuids + ] + if deleted_uuids := old_uuids - new_uuids: + changes["removed"] = [ + _diagelt2dict(old.nodes.by_uuid(i)) for i in deleted_uuids + ] + + old_diag = old.render(None) + new_diag = new.render(None) + changes["layout_changes"] = False + for i in old_uuids & new_uuids: + if _diagelt2diff(old_diag[i], new_diag[i]): + changes["layout_changes"] = True + return changes + + +def _diagelt2dict(obj: c.GenericElement) -> dict[str, t.Any]: + """Serialize a diagram element into a dict. + + This function is used for diagram elements that were either + introduced or deleted, in which case the id and the name are + serialized. + """ + result = _obj2dict(obj) + return {"id": result["id"], "name": result.get("name")} + + +def _diagelt2diff( + old: capellambse.diagram.DiagramElement, + new: capellambse.diagram.DiagramElement, +) -> dict[str, t.Any]: + # TODO + return {} def _compare_all_objects( @@ -78,6 +190,7 @@ def _compare_object_type( for i in old_uuids & new_uuids: if diff := _obj2diff(old.by_uuid(i), new.by_uuid(i)): changes.setdefault("modified", []).append(diff) + return changes def _obj2dict(obj: c.GenericElement) -> dict[str, t.Any]: