Skip to content

Commit

Permalink
[WIP] feat: Add implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Wuestengecko committed Sep 28, 2023
1 parent 0315c65 commit 57a1f0e
Show file tree
Hide file tree
Showing 4 changed files with 294 additions and 2 deletions.
134 changes: 132 additions & 2 deletions model_diffsummary/__main__.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,150 @@
# Copyright DB Netz AG and contributors
# SPDX-License-Identifier: Apache-2.0
"""Main entry point into model_diffsummary."""
from __future__ import annotations

import logging
import typing as t

import capellambse
import click
from capellambse import model as m
from capellambse.model import common as c

import model_diffsummary

logger = logging.getLogger(__name__)


@click.command()
@click.version_option(
version=model_diffsummary.__version__,
prog_name="Model-Diff Summary Tool",
message="%(prog)s %(version)s",
)
def main():
"""Console script for model_diffsummary."""
@click.argument("model", type=capellambse.cli_helpers.ModelInfoCLI())
@click.argument("old_version")
@click.argument("new_version")
def main(model: dict[str, t.Any], old_version: str, new_version: str):
"""Generate the diff summary between two model versions.
The comparison result will be printed to stdout in YAML format.
"""
logging.basicConfig(level="DEBUG")
old_model = capellambse.MelodyModel(**model, revision=old_version)
new_model = capellambse.MelodyModel(**model, revision=new_version)

result = _compare_all_objects(old_model, new_model)

print(result)


def _compare_all_objects(
old: capellambse.MelodyModel, new: capellambse.MelodyModel
) -> t.Any:
result: t.Any = {"oa": {}, "la": {}, "pa": {}, "sa": {}}
for layer, objtype in (
("oa", m.oa.OperationalActivity),
("oa", m.fa.FunctionalExchange),
("oa", m.oa.OperationalCapability),
("sa", m.ctx.SystemComponent),
("sa", m.ctx.SystemFunction),
("sa", m.ctx.Capability),
):
type_result = _compare_object_type(old, new, objtype)
result[layer][objtype.__name__] = type_result
return result


def _compare_object_type(
old: capellambse.MelodyModel,
new: capellambse.MelodyModel,
obj_type: type[c.GenericElement],
) -> t.Any:
logging.debug("Collecting objects of type %s", obj_type.__name__)
changes = {}

old_objs = old.search(obj_type)
new_objs = new.search(obj_type)

old_uuids = {i.uuid for i in old_objs}
new_uuids = {i.uuid for i in new_objs}

if created_uuids := new_uuids - old_uuids:
changes["created"] = [_obj2dict(new.by_uuid(i)) for i in created_uuids]
if deleted_uuids := old_uuids - new_uuids:
changes["deleted"] = [_obj2dict(old.by_uuid(i)) for i in deleted_uuids]

for i in old_uuids & new_uuids:
if diff := _obj2diff(old.by_uuid(i), new.by_uuid(i)):
changes.setdefault("modified", []).append(diff)


def _obj2dict(obj: c.GenericElement) -> dict[str, t.Any]:
"""Serialize a model object into a dict.
This function is used for objects that were either created or
deleted, in which case all available attributes are serialized.
"""
result = {}
for attr in dir(type(obj)):
acc = getattr(type(obj), attr, None)
if isinstance(acc, c.AttributeProperty):
result[attr] = getattr(obj, attr)
return result


def _obj2diff(
old: c.GenericElement, new: c.GenericElement
) -> dict[str, t.Any] | None:
"""Serialize the differences between the old and new object.
This function is used for objects that were modified. Only the
attributes that were changed are serialized.
The new (current) *name* is always serialized. If it didn't change,
it will not have the "previous" key.
"""
changes: t.Any = {}
for attr in dir(type(old)):
if not isinstance(
getattr(type(old), attr, None),
(c.AttributeProperty, c.AttrProxyAccessor, c.LinkAccessor),
):
continue

old_val = getattr(old, attr)
new_val = getattr(new, attr)
if isinstance(old_val, c.GenericElement):
if old_val.uuid != new_val.uuid:
changes[attr] = {
"previous": {
"uuid": old_val.uuid,
"display_name": old_val.name,
},
"current": {
"uuid": new_val.uuid,
"display_name": new_val.name,
},
}
elif isinstance(old_val, c.ElementList):
if [i.uuid for i in old_val] != [i.uuid for i in new_val]:
changes[attr] = {
"previous": [
{"uuid": i.uuid, "display_name": i.name}
for i in old_val
],
"current": [
{"uuid": i.uuid, "display_name": i.name}
for i in new_val
],
}
elif old_val != new_val:
changes[attr] = {"previous": old_val, "current": new_val}

if not changes:
return None
return {"uuid": old.uuid, "display_name": new.name, "changes": changes}


if __name__ == "__main__":
Expand Down
91 changes: 91 additions & 0 deletions notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<!--
~ Copyright DB Netz AG and contributors
~ SPDX-License-Identifier: Apache-2.0
-->

End goal:

- Commit-based "news feed" in collab manager
- Also viewable as standalone HTML report
- Run in pipeline for newest changes of each commit
- Run for any arbitrary two commits (e.g. tag BL_10 vs. tag BL_11)

- Something like: python -m whatevermod --previous COMMITISH --current COMMITISH

- Produces JSON or YAML report, which can be displayed interactively

- Some tree structure, commit as tree root

drill down into changed objects:

Commit ABCDEF0 by Guy 1 on <date/time>

Changed diagrams:
> [SAB] System: 20 changes (click to expand...)
> ...
v [CDC] Mission
Introduced:
- Capability 1
Removed:
- Capability 7
> ...

Changes on OA:
> OperationalActivity: 3 created, 5 deleted, 1 modified (click to expand...)
> ...
v OperationalCapability:
| Created:
| > New thing A (click to show details)
| v New thing B
| uuid: 13579BDF-...
| name: New thing B
| description: <p>...</p>
| ...
|
| Deleted:
| - Old Name 1 (12345678-...)
| - Old Name 2 (9ABCDEF0-...)
| ...
|
| Modified:
| - Capsoup (2468ACE0-...)
| Name changed from X to Capsoup

Changes on SA:
...

Changes on LA:
...

------------------------------------

Changed objects:
- Function ABC
> new input: ...
> input deleted: ...
> name changed: ...

> child change:
- ...
> ...

Changed diagrams:
- Diagram XYZ
> Box "Function ABC" added
(only semantic changes, layout is ignored / summarized as "layout changes")
-> link to diagram cache, or left / right display, visual diff tool, w/e

... and other changes (which we don't know how to show yet) :) // (XML tree diff?)

Commit 1234567 by Guy 2 on <date/time>: Changed 200 objects and 20 diagrams (click to expand...)

Objects of interest:

For OA: OperationalActivity, FunctionalExchange, OperationalCapability
Diagrams (OA): COC, OAIB, OPD, OAB, O.MSM, O.STM(?), O.CDB (low-prio)

For SA: SystemComponent (+ Parts) -> distinguish between actor/non-actor; + allocated SystemFunctions
Capabilities + Involvements
Diagrams (SA): CC, SDFB, MCB, SAB

Start with OperationalActivity
66 changes: 66 additions & 0 deletions result-sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Copyright DB Netz AG and contributors
# SPDX-License-Identifier: Apache-2.0

metadata:
model:
path: git+https://git.tech.rz.db.de/god/automated-train
old_revision:
hash: commit_A
author: Person A
date: 2023-09-01 09:30:00
description: |-
Do work
In this commit, work was done.
Co-authored-by: Arufua <[email protected]>
Signed-off-by: Executive C <[email protected]>
new_revision:
hash: commit_B
author: Person B
date: 2023-09-28 14:00:00
description: A couple minor changes

diagrams:
oa:
created:
- Diagram 1
- Diagram 2
deleted:
- Diagram 3
- Diagram 4
changed:
- id: _ABCDEFGHI
name: Diagram 5
layout_changes: true
introduced:
- id: 12345678-...
name: Operational Activity 1
- id: 12345678-...
name: Operational Activity 2
removed:
- id: 12345678-...
name: Operational Activity 3

objects:
oa:
OperationalActivity:
created:
- uuid: 13579BDF-...
name: New thing B
description: <p>...</p> # maybe use '!html' ?
deleted:
- uuid: 12345678-...
name: Old Name 1
description: <p>...</p>
modified:
- uuid: 2468ACE0-...
display_name: Capsoup # always the current name, for showing in the frontend
changes:
name:
previous: null
current: Capsoup
OperationalCapability:
...: ...

sa:
5 changes: 5 additions & 0 deletions testrun.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh -e
# Copyright DB Netz AG and contributors
# SPDX-License-Identifier: Apache-2.0

exec python -m model_diffsummary autotrain 2ffa0e6e828378550cabec5ff32cf5b4a834d1fc HEAD

0 comments on commit 57a1f0e

Please sign in to comment.