Browse Source

reworked edge mandatory constraint, some related bug fixes

Lucas Heer 7 years ago
parent
commit
4639a78203
7 changed files with 108 additions and 26 deletions
  1. 10 11
      commons.py
  2. 0 2
      main.py
  3. 59 0
      models/sketching/example_model_3.mvc
  4. 1 1
      sketchUI/exm_scene.py
  5. 2 2
      sketchUI/im_scene.py
  6. 35 9
      verifier.py
  7. 1 1
      wrappers/modelverse_SCCD.py

+ 10 - 11
commons.py

@@ -129,21 +129,20 @@ def model_contains_type(model, node_type):
         return False
         return False
     return True
     return True
 
 
-def has_edge_to_type(model, node_id, type_id):
+def has_edge_to_type(model, node_id, node_type):
     """
     """
     True if the node identified by node_id is connected to a node of type type_id, False otherwise.
     True if the node identified by node_id is connected to a node of type type_id, False otherwise.
     """
     """
     outgoings = mv.read_outgoing(model, node_id, "Edge")
     outgoings = mv.read_outgoing(model, node_id, "Edge")
-    dest_ids = []
-    for edge in outgoings:
-        dest_node = mv.read_association_destination(model, edge)[0]
-        dest_ids.append(dest_node)
-    dest_types = []
-    for node_id in dest_ids:
-        node_type = get_node_type(model, node_id)
-        dest_types.append(node_type)
-
-    return type_id in dest_types
+    dest_ids = [mv.read_association_destination(model, edge)[0] for edge in outgoings]
+    dest_types = [get_node_type(model, _node_id) for _node_id in dest_ids]
+
+    # also need to check incomings of node_id
+    incomings = mv.read_incoming(model, node_id, "Edge")
+    src_ids = [mv.read_association_source(model, edge)[0] for edge in incomings]
+    src_types = [get_node_type(model, _node_id) for _node_id in src_ids]
+
+    return node_type in dest_types + src_types
 
 
 def is_edge_supported(from_type, to_type):
 def is_edge_supported(from_type, to_type):
     """
     """

+ 0 - 2
main.py

@@ -128,7 +128,5 @@ if __name__ == "__main__":
     print("Starting UI ...")
     print("Starting UI ...")
     run_ui(mode, model)
     run_ui(mode, model)
 
 
-    """
     print("verify ... ")
     print("verify ... ")
     print(mv.verify(model, "formalisms/graphMM"))
     print(mv.verify(model, "formalisms/graphMM"))
-    """

+ 59 - 0
models/sketching/example_model_3.mvc

@@ -0,0 +1,59 @@
+Model m3 {
+    descr = "Device-User"
+    is_example = True
+}
+
+Node n1 {
+    typeID = "PC"
+}
+
+Node n2 {
+    typeID = "User"
+}
+
+Node n3 {
+    typeID = "Tablet"
+}
+
+Node n4 {
+    typeID = "User"
+}
+
+Node n5 {
+    typeID = "Phone"
+}
+
+Node n6 {
+    typeID = "User"
+}
+
+Edge e1(n1, n2) {
+    directed = False
+}
+
+Edge e2(n3, n4) {
+    directed = False
+}
+
+Edge e3(n5, n6) {
+    directed = False
+}
+
+Attribute a1 {
+    key = "name"
+    value = "Peter"
+}
+
+Attribute a2 {
+    key = "name"
+    value = "James"
+}
+
+Attribute a3 {
+    key = "name"
+    value = "Paul"
+}
+
+NodeAttribute na1(n1, a2) {}
+NodeAttribute na1(n3, a4) {}
+NodeAttribute na1(n5, a6) {}

+ 1 - 1
sketchUI/exm_scene.py

@@ -76,7 +76,7 @@ class SketchScene(QGraphicsScene):
                 return
                 return
 
 
             elif isinstance(item, GraphicsNodeItem):
             elif isinstance(item, GraphicsNodeItem):
-                self._parent.plainTextEdit.appendPlainText("Selected node of type {}".format(item.get_type()))
+                self._parent.plainTextEdit.appendPlainText("Selected node {}:{}".format(item.node_id, item.get_type()))
                 self.highlight_node(item.node_id, Qt.blue)
                 self.highlight_node(item.node_id, Qt.blue)
                 # load attributes for selected node
                 # load attributes for selected node
                 self._parent.tableWidget.setRowCount(0)
                 self._parent.tableWidget.setRowCount(0)

+ 2 - 2
sketchUI/im_scene.py

@@ -36,7 +36,7 @@ class CustomScene(QGraphicsScene):
             if not item:
             if not item:
                 return
                 return
             elif isinstance(item, GraphicsNodeItem):
             elif isinstance(item, GraphicsNodeItem):
-                self._parent.plainTextEdit.appendPlainText("Selected node of type {}".format(item.get_type()))
+                self._parent.plainTextEdit.appendPlainText("Selected node {}:{}".format(item.node_id, item.get_type()))
                 self.highlight_node(item.node_id, Qt.blue)
                 self.highlight_node(item.node_id, Qt.blue)
                 # load node attributes and display them in table
                 # load node attributes and display them in table
                 self._parent.tableWidget.setRowCount(0)
                 self._parent.tableWidget.setRowCount(0)
@@ -119,7 +119,7 @@ class CustomScene(QGraphicsScene):
             line.edge_id = edge_id
             line.edge_id = edge_id
             self._parent.plainTextEdit.appendPlainText("Added edge between {} and {} to model".format(from_type, to_type))
             self._parent.plainTextEdit.appendPlainText("Added edge between {} and {} to model".format(from_type, to_type))
 
 
-    QApplication.restoreOverrideCursor()
+        QApplication.restoreOverrideCursor()
 
 
 
 
     def _handle_keypress_delete(self, selected):
     def _handle_keypress_delete(self, selected):

+ 35 - 9
verifier.py

@@ -176,16 +176,16 @@ class Verifier(object):
 
 
     def verify_associations(self):
     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).
+        1. An edge between two types is mandatory if in every example model that contains nodes of both types, every
+        node is connected with an edge to the other type.
         2. For every association in the instance model, the types of the source and target must correspond
         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
         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
         is already enforced while instance modeling (see im_scene.py, draw_edge() which checks this when trying
         to connect two nodes).
         to connect two nodes).
         """
         """
 
 
-        # get a set of mandatory edges (= edges that occur in every example model)
-        all_edges = {model: set() for model in self._example_models}
+        # construct the set of mandatory edges
+        all_edges = set()
         for exm in self._example_models:
         for exm in self._example_models:
             all_links = mv.all_instances(exm, "Edge")
             all_links = mv.all_instances(exm, "Edge")
             for link in all_links:
             for link in all_links:
@@ -193,8 +193,29 @@ class Verifier(object):
                 dest_id = mv.read_association_destination(exm, link)[0]
                 dest_id = mv.read_association_destination(exm, link)[0]
                 src_type = commons.get_node_type(exm, src_id)
                 src_type = commons.get_node_type(exm, src_id)
                 dest_type = commons.get_node_type(exm, dest_id)
                 dest_type = commons.get_node_type(exm, dest_id)
-                all_edges[exm].add(Edge(src_type, dest_type))
-        mandatory_edges = set.intersection(*all_edges.values())
+                all_edges.add(Edge(src_type, dest_type))
+        edge_mandatory = {e:True for e in all_edges}
+
+        for cand_edge, _ in edge_mandatory.iteritems():
+            # check every example model if it contains the required types
+            # and if there are two noes of the type which are not connected -> not mandatory
+            found = False
+            for exm in self._example_models:
+                if found:
+                    break
+                typed_nodes_a = commons.all_nodes_with_type(exm, cand_edge.n1)
+                typed_nodes_b = commons.all_nodes_with_type(exm, cand_edge.n2)
+                if not typed_nodes_a or not typed_nodes_b:
+                    # example model does not contain the two types
+                    continue
+                for src_node in typed_nodes_a:
+                    # if this node is not connected to a node typed by the required type, the edge is not mandatory
+                    if not commons.has_edge_to_type(exm, src_node, cand_edge.n2):
+                        edge_mandatory[cand_edge] = False
+                        found = True
+
+        mandatory_edges = [edge for edge,mandatory in edge_mandatory.iteritems() if mandatory]
+        print(mandatory_edges)
 
 
         # check if instance model contains the types and the edge
         # check if instance model contains the types and the edge
         for edge in mandatory_edges:
         for edge in mandatory_edges:
@@ -202,13 +223,18 @@ class Verifier(object):
                 continue
                 continue
             if not commons.model_contains_type(self._instance_model, edge.n2):
             if not commons.model_contains_type(self._instance_model, edge.n2):
                 continue
                 continue
-            # instance model contains both types
+            # instance model contains both types -> are they connected?
             for node in commons.all_nodes_with_type(self._instance_model, edge.n1):
             for node in commons.all_nodes_with_type(self._instance_model, edge.n1):
                 if not commons.has_edge_to_type(self._instance_model, node, edge.n2):
                 if not commons.has_edge_to_type(self._instance_model, node, edge.n2):
                     return {"OK":False, "error": "Edge between {} and {} mandatory".format(edge.n1, edge.n2),
                     return {"OK":False, "error": "Edge between {} and {} mandatory".format(edge.n1, edge.n2),
                             "affected":[]}
                             "affected":[]}
+            # other way round
+            for node in commons.all_nodes_with_type(self._instance_model, edge.n2):
+                if not commons.has_edge_to_type(self._instance_model, node, edge.n1):
+                    return {"OK":False, "error": "Edge between {} and {} mandatory".format(edge.n2, edge.n1),
+                            "affected":[]}
 
 
-        # lastly, check if all edges in the instance model are actually supported
+        # lastly, check if all edges in the instance model are actually valid
         all_edges = mv.all_instances(self._instance_model, "Edge")
         all_edges = mv.all_instances(self._instance_model, "Edge")
         for edge in all_edges:
         for edge in all_edges:
             src_id = mv.read_association_source(self._instance_model, edge)[0]
             src_id = mv.read_association_source(self._instance_model, edge)[0]
@@ -216,7 +242,7 @@ class Verifier(object):
             src_type = commons.get_node_type(self._instance_model, src_id)
             src_type = commons.get_node_type(self._instance_model, src_id)
             dest_type = commons.get_node_type(self._instance_model, dest_id)
             dest_type = commons.get_node_type(self._instance_model, dest_id)
             if not commons.is_edge_supported(src_type, dest_type):
             if not commons.is_edge_supported(src_type, dest_type):
-                return {"OK":False, "error": "Edge between {} and {} not supported".format(edge.n1, edge.n2),
+                return {"OK":False, "error": "Edge between {} and {} not valid".format(edge.n1, edge.n2),
                         "affected":[src_id, dest_id]}
                         "affected":[src_id, dest_id]}
 
 
         return {"OK": True, "error": None, "affected":[]}
         return {"OK": True, "error": None, "affected":[]}

+ 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 May  2 18:41:25 2018
+Date:   Fri May  4 09:34:46 2018
 
 
 Model author: Yentl Van Tendeloo
 Model author: Yentl Van Tendeloo
 Model name:   MvK Server
 Model name:   MvK Server