Parcourir la source

restructured verify code: split up in multiple class methods

Lucas Heer il y a 7 ans
Parent
commit
4ac56e435d
7 fichiers modifiés avec 182 ajouts et 47 suppressions
  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"""
     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):
     """ Returns a list of association IDs between the nodes node1 and node2 """
     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.graphics_node_item import GraphicsNodeItem
 from wrappers.modelverse import element_list_nice
-
+import commons
 
 
 class EXMMainWindow(QMainWindow, Ui_MainWindow):
@@ -46,7 +46,7 @@ class EXMMainWindow(QMainWindow, Ui_MainWindow):
     def populate_types(self):
         # sync example model types to list widget
         self.listWidget.clear()
-        self.listWidget.addItems(mvops.get_available_types())
+        self.listWidget.addItems(commons.get_available_types())
 
     def _setup_toolbar(self):
         # create action buttons on toolbar

+ 2 - 2
sketchUI/exm_scene.py

@@ -224,7 +224,7 @@ class SketchScene(QGraphicsScene):
             # user canceled or node type empty
             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))
             return
 
@@ -267,7 +267,7 @@ class SketchScene(QGraphicsScene):
             # user canceled or empty type string
             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))
             return
 

+ 53 - 4
sketchUI/im_mainwindow.py

@@ -7,7 +7,8 @@ from sketchUI.im_scene import CustomScene, Mode
 from sketchUI import mvops
 from sketchUI.graphics_node_item import GraphicsNodeItem
 from wrappers.modelverse import element_list_nice
-from verifier import verify
+from verifier import Verifier
+import commons
 
 
 class IMMainWindow(QMainWindow, Ui_MainWindow):
@@ -22,7 +23,7 @@ class IMMainWindow(QMainWindow, Ui_MainWindow):
         self._scene.setSceneRect(0, 0, 200, 200)
         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.setup_toolbar()
@@ -170,9 +171,57 @@ class IMMainWindow(QMainWindow, Ui_MainWindow):
 
     def _on_verify_clicked(self, event):
         self.plainTextEdit.appendPlainText("Verifying instance model against example models ...")
+        self.plainTextEdit.repaint()
         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()
 
     def _on_attribute_edited(self, item):

+ 0 - 10
sketchUI/mvops.py

@@ -9,16 +9,6 @@ import commons
 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):
     """
     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
-from commons import get_all_attributes_of_type
+import commons
 
 
 class Edge(object):
@@ -20,16 +20,94 @@ class Edge(object):
         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):
     """
@@ -40,14 +118,13 @@ def verify(instance_model_path):
     3. Attribute validity
     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
     # For that, first get all available types from example models
     print("Verify: checking node typing ...")
     all_types = []
-    for exm in example_models_full:
+    for exm in example_models:
         all_nodes = mv.all_instances(exm, "Node")
         for node in all_nodes:
             typ = mv.read_attrs(exm, node)["typeID"]
@@ -67,7 +144,7 @@ def verify(instance_model_path):
     print("Verify: checking node multiplicity ...")
     total_occurences = {k:[] for k in all_types}
     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)
             occ_list.append(num_occ)
 
@@ -76,24 +153,22 @@ def verify(instance_model_path):
         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":True, "error":None}
-
     # 3. Check attribute types: Is every attribute in instance model present in some example model?
     print("Verify: checking attributes ...")
     all_nodes = mv.all_instances(instance_model_path, "Node")
     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:
             continue
         #print("Attributes of node {} of type {} are: {}".format(node, node_typ, attrs))
         # check example models for such an attribute key
         for im_attribute in attrs:
             found = False
-            for exm in example_models_full:
+            for exm in example_models:
                 if found:
                     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:
                     if exm_attr.key == im_attribute.key:
                         #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 assocation type validity as well
     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")
         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")
             for assoc in out_associations:
                 if assoc.startswith("__"):
                     continue
                 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))
 
     unique_edges = []
@@ -136,7 +211,8 @@ def verify(instance_model_path):
     for k, v in edge_ctr.iteritems():
         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
     # first, get all edges in instance model
@@ -147,8 +223,8 @@ def verify(instance_model_path):
             continue
         edge_src = mv.read_association_source(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))
 
     # 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)
 
-Date:   Wed Apr 25 15:33:59 2018
+Date:   Thu Apr 26 11:05:54 2018
 
 Model author: Yentl Van Tendeloo
 Model name:   MvK Server