Bläddra i källkod

restructured verify code: split up in multiple class methods

Lucas Heer 7 år sedan
förälder
incheckning
4ac56e435d
7 ändrade filer med 182 tillägg och 47 borttagningar
  1. 20 0
      commons.py
  2. 2 2
      sketchUI/exm_mainwindow.py
  3. 2 2
      sketchUI/exm_scene.py
  4. 53 4
      sketchUI/im_mainwindow.py
  5. 0 10
      sketchUI/mvops.py
  6. 104 28
      verifier.py
  7. 1 1
      wrappers/modelverse_SCCD.py

+ 20 - 0
commons.py

@@ -62,6 +62,26 @@ def get_node_type(model, node):
     """ Returns the type attribute of node in model as string"""
     """ Returns the type attribute of node in model as string"""
     return mv.read_attrs(model, node)["typeID"]
     return mv.read_attrs(model, node)["typeID"]
 
 
+def get_available_types():
+    """ Returns a list of all types of nodes in all example models """
+    types = []
+    for m in all_example_models():
+        nodes = mv.all_instances(m, "Node")
+        for node in nodes:
+            typ = get_node_type(m, node)
+            types.append(typ)
+    return list(set(types))
+
+def count_occurences(node_type, model):
+    """ Returns the number of occurences of a node with type node_type in a model """
+    ctr = 0
+    all_nodes = mv.all_instances(model, "Node")
+    for node in all_nodes:
+        typ = get_node_type(model, node)
+        if typ == node_type:
+            ctr += 1
+    return ctr
+
 def get_associations_between(model, node1, node2):
 def get_associations_between(model, node1, node2):
     """ Returns a list of association IDs between the nodes node1 and node2 """
     """ Returns a list of association IDs between the nodes node1 and node2 """
     edges_n1 = mv.read_outgoing(model, node1, "Edge")
     edges_n1 = mv.read_outgoing(model, node1, "Edge")

+ 2 - 2
sketchUI/exm_mainwindow.py

@@ -6,7 +6,7 @@ from sketchUI.exm_scene import SketchScene, Mode
 from sketchUI import mvops
 from sketchUI import mvops
 from sketchUI.graphics_node_item import GraphicsNodeItem
 from sketchUI.graphics_node_item import GraphicsNodeItem
 from wrappers.modelverse import element_list_nice
 from wrappers.modelverse import element_list_nice
-
+import commons
 
 
 
 
 class EXMMainWindow(QMainWindow, Ui_MainWindow):
 class EXMMainWindow(QMainWindow, Ui_MainWindow):
@@ -46,7 +46,7 @@ class EXMMainWindow(QMainWindow, Ui_MainWindow):
     def populate_types(self):
     def populate_types(self):
         # sync example model types to list widget
         # sync example model types to list widget
         self.listWidget.clear()
         self.listWidget.clear()
-        self.listWidget.addItems(mvops.get_available_types())
+        self.listWidget.addItems(commons.get_available_types())
 
 
     def _setup_toolbar(self):
     def _setup_toolbar(self):
         # create action buttons on toolbar
         # create action buttons on toolbar

+ 2 - 2
sketchUI/exm_scene.py

@@ -224,7 +224,7 @@ class SketchScene(QGraphicsScene):
             # user canceled or node type empty
             # user canceled or node type empty
             return
             return
 
 
-        if node_type in mvops.get_available_types():
+        if node_type in commons.get_available_types():
             self._parent.plainTextEdit.appendPlainText("Error: Already such a type: {}".format(node_type))
             self._parent.plainTextEdit.appendPlainText("Error: Already such a type: {}".format(node_type))
             return
             return
 
 
@@ -267,7 +267,7 @@ class SketchScene(QGraphicsScene):
             # user canceled or empty type string
             # user canceled or empty type string
             return
             return
 
 
-        if node_type in mvops.get_available_types():
+        if node_type in commons.get_available_types():
             self._parent.plainTextEdit.appendPlainText("Error: Already such a type: {}".format(node_type))
             self._parent.plainTextEdit.appendPlainText("Error: Already such a type: {}".format(node_type))
             return
             return
 
 

+ 53 - 4
sketchUI/im_mainwindow.py

@@ -7,7 +7,8 @@ from sketchUI.im_scene import CustomScene, Mode
 from sketchUI import mvops
 from sketchUI import mvops
 from sketchUI.graphics_node_item import GraphicsNodeItem
 from sketchUI.graphics_node_item import GraphicsNodeItem
 from wrappers.modelverse import element_list_nice
 from wrappers.modelverse import element_list_nice
-from verifier import verify
+from verifier import Verifier
+import commons
 
 
 
 
 class IMMainWindow(QMainWindow, Ui_MainWindow):
 class IMMainWindow(QMainWindow, Ui_MainWindow):
@@ -22,7 +23,7 @@ class IMMainWindow(QMainWindow, Ui_MainWindow):
         self._scene.setSceneRect(0, 0, 200, 200)
         self._scene.setSceneRect(0, 0, 200, 200)
         self.graphicsView.setScene(self._scene)
         self.graphicsView.setScene(self._scene)
 
 
-        self.listWidget.addItems(mvops.get_available_types())
+        self.listWidget.addItems(commons.get_available_types())
         self.listWidget.itemDoubleClicked.connect(self._on_list_item_clicked)
         self.listWidget.itemDoubleClicked.connect(self._on_list_item_clicked)
 
 
         self.setup_toolbar()
         self.setup_toolbar()
@@ -170,9 +171,57 @@ class IMMainWindow(QMainWindow, Ui_MainWindow):
 
 
     def _on_verify_clicked(self, event):
     def _on_verify_clicked(self, event):
         self.plainTextEdit.appendPlainText("Verifying instance model against example models ...")
         self.plainTextEdit.appendPlainText("Verifying instance model against example models ...")
+        self.plainTextEdit.repaint()
         QApplication.setOverrideCursor(Qt.WaitCursor)
         QApplication.setOverrideCursor(Qt.WaitCursor)
-        ret = verify(self._cur_model)
-        self.plainTextEdit.appendPlainText("Result: {}".format(str(ret)))
+
+        verify = Verifier(self._cur_model)
+        verify.init()
+
+        self.plainTextEdit.appendPlainText("Verify: Checking node typing ...")
+        self.plainTextEdit.repaint()
+        ret = verify.verify_node_typing()
+        if not ret["OK"]:
+            self.plainTextEdit.appendPlainText("Error: {}".format(str(ret["error"])))
+            QApplication.restoreOverrideCursor()
+            return
+        else:
+            self.plainTextEdit.appendPlainText("OK")
+            self.plainTextEdit.repaint()
+
+        self.plainTextEdit.appendPlainText("Verify: Checking node multiplicity ...")
+        self.plainTextEdit.repaint()
+        ret = verify.verify_node_multiplicity()
+        if not ret["OK"]:
+            self.plainTextEdit.appendPlainText("Error: {}".format(str(ret["error"])))
+            QApplication.restoreOverrideCursor()
+            return
+        else:
+            self.plainTextEdit.appendPlainText("OK")
+            self.plainTextEdit.repaint()
+
+        self.plainTextEdit.appendPlainText("Verify: Checking attributes ...")
+        self.plainTextEdit.repaint()
+        ret = verify.verify_attributes()
+        if not ret["OK"]:
+            self.plainTextEdit.appendPlainText("Error: {}".format(str(ret["error"])))
+            QApplication.restoreOverrideCursor()
+            return
+        else:
+            self.plainTextEdit.appendPlainText("OK")
+            self.plainTextEdit.repaint()
+
+        self.plainTextEdit.appendPlainText("Verify: Checking edges ...")
+        self.plainTextEdit.repaint()
+        ret = verify.verify_associations()
+        if not ret["OK"]:
+            self.plainTextEdit.appendPlainText("Error: {}".format(str(ret["error"])))
+            QApplication.restoreOverrideCursor()
+            return
+        else:
+            self.plainTextEdit.appendPlainText("OK")
+            self.plainTextEdit.repaint()
+
+        self.plainTextEdit.appendPlainText("Verify OK")
         QApplication.restoreOverrideCursor()
         QApplication.restoreOverrideCursor()
 
 
     def _on_attribute_edited(self, item):
     def _on_attribute_edited(self, item):

+ 0 - 10
sketchUI/mvops.py

@@ -9,16 +9,6 @@ import commons
 from sketchUI.graphics_node_item import IconType
 from sketchUI.graphics_node_item import IconType
 
 
 
 
-def get_available_types():
-    """ Returns a list of all types of nodes in all example models """
-    types = []
-    for m in commons.all_example_models():
-        nodes = mv.all_instances(m, "Node")
-        for node in nodes:
-            typ = commons.get_node_type(m, node)
-            types.append(typ)
-    return list(set(types))
-
 def is_edge_supported(from_type, to_type):
 def is_edge_supported(from_type, to_type):
     """
     """
     True if an association between from_type and to_type exists in any example model
     True if an association between from_type and to_type exists in any example model

+ 104 - 28
verifier.py

@@ -1,5 +1,5 @@
 import wrappers.modelverse as mv
 import wrappers.modelverse as mv
-from commons import get_all_attributes_of_type
+import commons
 
 
 
 
 class Edge(object):
 class Edge(object):
@@ -20,16 +20,94 @@ class Edge(object):
         return "{}->{}".format(self.src, self.dest)
         return "{}->{}".format(self.src, self.dest)
 
 
 
 
-def _count_occurences(node_type, model):
-    """ returns the number of occurences of a node with type node_type in a model """
-    ctr = 0
-    all_nodes = mv.all_instances(model, "Node")
-    for node in all_nodes:
-        typ = mv.read_attrs(model, node)["typeID"]
-        if typ == node_type:
-            ctr += 1
-    return ctr
-
+class Verifier(object):
+    def __init__(self, instance_model):
+        self._instance_model = instance_model
+        self._example_models = None
+        self._available_types = None
+
+    def set_instance_model(self, model):
+        self._instance_model = model
+
+    def get_instance_model(self):
+        return self._instance_model
+
+    def init(self):
+        """
+        Inits the verifier. Loads required data from the modelverse
+        to avoid reloading it for every step.
+        """
+        self._example_models = commons.all_example_models()
+        self._available_types = commons.get_available_types()
+
+    def verify_node_typing(self):
+        """
+        Checks if every node in instance model is typed by a node in any example model.
+        Should actually not be neccessary since instance modeling only allows types from
+        example models.
+        """
+        # First, get a list of all types found in all example models.
+        all_types = []
+        for exm in self._example_models:
+            all_nodes = mv.all_instances(exm, "Node")
+            for node in all_nodes:
+                typ = mv.read_attrs(exm, node)["typeID"]
+                if not typ in all_types:
+                    all_types.append(typ)
+        # print("Verify: types found in example models: {}".format(all_types))
+
+        # Check for every node in instance model if it has a valid type
+        all_nodes = mv.all_instances(self._instance_model, "Node")
+        for node in all_nodes:
+            node_typ = commons.get_node_type(self._instance_model, node)
+            if node_typ not in all_types:
+                return {"OK": False, "error": "Type {} from instance model not in example models".format(node_typ)}
+
+        return {"OK":True, "error":None}
+
+    def verify_node_multiplicity(self):
+        """
+        A node is mandatory in the instance model if it occurs in every example model.
+        More specifically, if a node type appears occ = [n0, n1, ... n_i] times in example model i,
+        it needs to occur min(occ) in the instance model.
+        """
+        total_occurences = {k: [] for k in self._available_types}
+        for node, occ_list in total_occurences.iteritems():
+            for exm in self._example_models:
+                num_occ = commons.count_occurences(node, exm)
+                occ_list.append(num_occ)
+
+        elem_cardinality = {k: min(v) for k, v in total_occurences.iteritems()}
+        for el, min_occ in elem_cardinality.iteritems():
+            if not commons.count_occurences(el, self._instance_model) >= min_occ:
+                return {"OK": False, "error": "Node with type {} needs to occur at least {} times".format(el, min_occ)}
+
+        return {"OK": True, "error": None}
+
+    def verify_attributes(self):
+        """
+        1. For every attribute key of a typed node in the instance model, there must be a corresponding
+        attribute key in some example model.
+        2. An attribute for a type is mandatory if it occurs in every example model.
+        """
+
+        # TODO: implement
+
+        return {"OK": True, "error": None}
+
+    def verify_associations(self):
+        """
+        1. If an association between two types is present in all example models, it is mandatory and must
+        therefore be present in the instance model (if it contains the same two types).
+        2. For every association in the instance model, the types of the source and target must correspond
+        to the types of an association in some example model. This check should not be necessary since this
+        is already enforced while instance modeling (see im_scene.py, draw_edge() which checks this when trying
+        to connect two nodes).
+        """
+
+        # TODO: implement
+
+        return {"OK": True, "error": None}
 
 
 def verify(instance_model_path):
 def verify(instance_model_path):
     """
     """
@@ -40,14 +118,13 @@ def verify(instance_model_path):
     3. Attribute validity
     3. Attribute validity
     4. Associations
     4. Associations
     """
     """
-    example_models = mv.model_list("models/example")
-    example_models_full = ["models/example/"+exm for exm in example_models]
+    example_models = commons.all_example_models()
 
 
     # 1. Check if all nodes in instance model are typed by a node in the example models
     # 1. Check if all nodes in instance model are typed by a node in the example models
     # For that, first get all available types from example models
     # For that, first get all available types from example models
     print("Verify: checking node typing ...")
     print("Verify: checking node typing ...")
     all_types = []
     all_types = []
-    for exm in example_models_full:
+    for exm in example_models:
         all_nodes = mv.all_instances(exm, "Node")
         all_nodes = mv.all_instances(exm, "Node")
         for node in all_nodes:
         for node in all_nodes:
             typ = mv.read_attrs(exm, node)["typeID"]
             typ = mv.read_attrs(exm, node)["typeID"]
@@ -67,7 +144,7 @@ def verify(instance_model_path):
     print("Verify: checking node multiplicity ...")
     print("Verify: checking node multiplicity ...")
     total_occurences = {k:[] for k in all_types}
     total_occurences = {k:[] for k in all_types}
     for node, occ_list in total_occurences.iteritems():
     for node, occ_list in total_occurences.iteritems():
-        for exm in example_models_full:
+        for exm in example_models:
             num_occ = _count_occurences(node, exm)
             num_occ = _count_occurences(node, exm)
             occ_list.append(num_occ)
             occ_list.append(num_occ)
 
 
@@ -76,24 +153,22 @@ def verify(instance_model_path):
         if not _count_occurences(el, instance_model_path) >= min_occ:
         if not _count_occurences(el, instance_model_path) >= min_occ:
             return {"OK":False, "error":"Node with type {} needs to occur at least {} times".format(el, min_occ)}
             return {"OK":False, "error":"Node with type {} needs to occur at least {} times".format(el, min_occ)}
 
 
-    return {"OK":True, "error":None}
-
     # 3. Check attribute types: Is every attribute in instance model present in some example model?
     # 3. Check attribute types: Is every attribute in instance model present in some example model?
     print("Verify: checking attributes ...")
     print("Verify: checking attributes ...")
     all_nodes = mv.all_instances(instance_model_path, "Node")
     all_nodes = mv.all_instances(instance_model_path, "Node")
     for node in all_nodes:
     for node in all_nodes:
-        node_typ = mv.read_attrs(instance_model_path, node)["type"]
-        attrs = get_all_attributes_of_type(instance_model_path, node_typ)
+        node_typ = mv.read_attrs(instance_model_path, node)["typeID"]
+        attrs = commons.get_all_attributes_of_type(instance_model_path, node_typ)
         if len(attrs) == 0:
         if len(attrs) == 0:
             continue
             continue
         #print("Attributes of node {} of type {} are: {}".format(node, node_typ, attrs))
         #print("Attributes of node {} of type {} are: {}".format(node, node_typ, attrs))
         # check example models for such an attribute key
         # check example models for such an attribute key
         for im_attribute in attrs:
         for im_attribute in attrs:
             found = False
             found = False
-            for exm in example_models_full:
+            for exm in example_models:
                 if found:
                 if found:
                     break
                     break
-                exm_attrs = get_all_attributes_of_type(exm, node_typ)
+                exm_attrs = commons.get_all_attributes_of_type(exm, node_typ)
                 for exm_attr in exm_attrs:
                 for exm_attr in exm_attrs:
                     if exm_attr.key == im_attribute.key:
                     if exm_attr.key == im_attribute.key:
                         #print("Found attribute {} in model {}".format(im_attribute, exm))
                         #print("Found attribute {} in model {}".format(im_attribute, exm))
@@ -109,17 +184,17 @@ def verify(instance_model_path):
     # Check multiplicity: an association needs to occur n>0 times if it occurs n>0 times in every example model
     # Check multiplicity: an association needs to occur n>0 times if it occurs n>0 times in every example model
     # Check assocation type validity as well
     # Check assocation type validity as well
     print("Verify: checking associations ...")
     print("Verify: checking associations ...")
-    all_edges = {k:[] for k in example_models_full}
-    for exm in example_models_full:
+    all_edges = {k:[] for k in example_models}
+    for exm in example_models:
         all_nodes = mv.all_instances(exm, "Node")
         all_nodes = mv.all_instances(exm, "Node")
         for node in all_nodes:
         for node in all_nodes:
-            node_typ = mv.read_attrs(exm, node)["type"]
+            node_typ = mv.read_attrs(exm, node)["typeID"]
             out_associations = mv.read_outgoing(exm, node, "Edge")
             out_associations = mv.read_outgoing(exm, node, "Edge")
             for assoc in out_associations:
             for assoc in out_associations:
                 if assoc.startswith("__"):
                 if assoc.startswith("__"):
                     continue
                     continue
                 target = mv.read_association_destination(exm, assoc)[0]
                 target = mv.read_association_destination(exm, assoc)[0]
-                target_typ = mv.read_attrs(exm, target)["type"]
+                target_typ = mv.read_attrs(exm, target)["typeID"]
                 all_edges[exm].append(Edge(node_typ, target_typ))
                 all_edges[exm].append(Edge(node_typ, target_typ))
 
 
     unique_edges = []
     unique_edges = []
@@ -136,7 +211,8 @@ def verify(instance_model_path):
     for k, v in edge_ctr.iteritems():
     for k, v in edge_ctr.iteritems():
         edge_ctr[k] = min(v)
         edge_ctr[k] = min(v)
 
 
-    #print(edge_ctr)  # {Edge("AP", "Tablet"): 0, Edge("Router", "PC"): 1, ...}
+    print(edge_ctr)  # {Edge("AP", "Tablet"): 0, Edge("Router", "PC"): 1, ...}
+    return {"OK": True, "error": None}
 
 
     # check if mandatory edges are present in instance model
     # check if mandatory edges are present in instance model
     # first, get all edges in instance model
     # first, get all edges in instance model
@@ -147,8 +223,8 @@ def verify(instance_model_path):
             continue
             continue
         edge_src = mv.read_association_source(instance_model_path, edge)[0]
         edge_src = mv.read_association_source(instance_model_path, edge)[0]
         edge_dest = mv.read_association_destination(instance_model_path, edge)[0]
         edge_dest = mv.read_association_destination(instance_model_path, edge)[0]
-        edge_src_typ = mv.read_attrs(instance_model_path, edge_src)["type"]
-        edge_dest_typ = mv.read_attrs(instance_model_path, edge_dest)["type"]
+        edge_src_typ = mv.read_attrs(instance_model_path, edge_src)["typeID"]
+        edge_dest_typ = mv.read_attrs(instance_model_path, edge_dest)["typeID"]
         im_edges.append(Edge(edge_src_typ, edge_dest_typ))
         im_edges.append(Edge(edge_src_typ, edge_dest_typ))
 
 
     # Check if all edges in instance models are valid (connecting types correspond to example models)
     # Check if all edges in instance models are valid (connecting types correspond to example models)

+ 1 - 1
wrappers/modelverse_SCCD.py

@@ -1,7 +1,7 @@
 """
 """
 Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
 Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
 
 
-Date:   Wed Apr 25 15:33:59 2018
+Date:   Thu Apr 26 11:05:54 2018
 
 
 Model author: Yentl Van Tendeloo
 Model author: Yentl Van Tendeloo
 Model name:   MvK Server
 Model name:   MvK Server