verifier.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import wrappers.modelverse as mv
  2. import commons
  3. class Edge(object):
  4. """
  5. Small helper class for association validation.
  6. Stores an edge as a tuple of (source, dest)
  7. """
  8. def __init__(self, src, dest):
  9. self.src = src
  10. self.dest = dest
  11. def __eq__(self, other):
  12. if other.src == self.src and other.dest == self.dest:
  13. return True
  14. return False
  15. def __repr__(self):
  16. return "{}->{}".format(self.src, self.dest)
  17. class Verifier(object):
  18. def __init__(self, instance_model):
  19. self._instance_model = instance_model
  20. self._example_models = None
  21. self._available_types = None
  22. def set_instance_model(self, model):
  23. self._instance_model = model
  24. def get_instance_model(self):
  25. return self._instance_model
  26. def init(self):
  27. """
  28. Inits the verifier. Loads required data from the modelverse
  29. to avoid reloading it for every step.
  30. """
  31. self._example_models = commons.all_example_models()
  32. self._available_types = commons.get_available_types()
  33. def verify_node_typing(self):
  34. """
  35. Checks if every node in instance model is typed by a node in any example model.
  36. Should actually not be neccessary since instance modeling only allows types from
  37. example models.
  38. """
  39. # First, get a list of all types found in all example models.
  40. all_types = []
  41. for exm in self._example_models:
  42. all_nodes = mv.all_instances(exm, "Node")
  43. for node in all_nodes:
  44. typ = mv.read_attrs(exm, node)["typeID"]
  45. if not typ in all_types:
  46. all_types.append(typ)
  47. # print("Verify: types found in example models: {}".format(all_types))
  48. # Check for every node in instance model if it has a valid type
  49. all_nodes = mv.all_instances(self._instance_model, "Node")
  50. for node in all_nodes:
  51. node_typ = commons.get_node_type(self._instance_model, node)
  52. if node_typ not in all_types:
  53. return {"OK": False, "error": "Type {} from instance model not in example models".format(node_typ)}
  54. return {"OK":True, "error":None}
  55. def verify_node_multiplicity(self):
  56. """
  57. A node is mandatory in the instance model if it occurs in every example model.
  58. More specifically, if a node type appears occ = [n0, n1, ... n_i] times in example model i,
  59. it needs to occur min(occ) in the instance model.
  60. """
  61. total_occurences = {k: [] for k in self._available_types}
  62. for node, occ_list in total_occurences.iteritems():
  63. for exm in self._example_models:
  64. num_occ = commons.count_occurences(node, exm)
  65. occ_list.append(num_occ)
  66. elem_cardinality = {k: min(v) for k, v in total_occurences.iteritems()}
  67. for el, min_occ in elem_cardinality.iteritems():
  68. if not commons.count_occurences(el, self._instance_model) >= min_occ:
  69. return {"OK": False, "error": "Node with type {} needs to occur at least {} times".format(el, min_occ)}
  70. return {"OK": True, "error": None}
  71. def verify_attributes(self):
  72. """
  73. 1. For every attribute key of a typed node in the instance model, there must be a corresponding
  74. attribute key in some example model.
  75. 2. An attribute for a type is mandatory if it occurs in every example model.
  76. """
  77. # TODO: implement
  78. return {"OK": True, "error": None}
  79. def verify_associations(self):
  80. """
  81. 1. If an association between two types is present in all example models, it is mandatory and must
  82. therefore be present in the instance model (if it contains the same two types).
  83. 2. For every association in the instance model, the types of the source and target must correspond
  84. to the types of an association in some example model. This check should not be necessary since this
  85. is already enforced while instance modeling (see im_scene.py, draw_edge() which checks this when trying
  86. to connect two nodes).
  87. """
  88. # TODO: implement
  89. return {"OK": True, "error": None}
  90. def verify(instance_model_path):
  91. """
  92. Verify an instance model against all example models (mv folder currently hard-coded)
  93. Performs checks similar to the linguistic conformance check as seen in the MDE class:
  94. 1. Node typing
  95. 2. Node cardinality
  96. 3. Attribute validity
  97. 4. Associations
  98. """
  99. example_models = commons.all_example_models()
  100. # 1. Check if all nodes in instance model are typed by a node in the example models
  101. # For that, first get all available types from example models
  102. print("Verify: checking node typing ...")
  103. all_types = []
  104. for exm in example_models:
  105. all_nodes = mv.all_instances(exm, "Node")
  106. for node in all_nodes:
  107. typ = mv.read_attrs(exm, node)["typeID"]
  108. if not typ in all_types:
  109. all_types.append(typ)
  110. #print("Verify: types found in example models: {}".format(all_types))
  111. # Now, check for every element in instance model if it is a valid type
  112. all_nodes = mv.all_instances(instance_model_path, "Node")
  113. for node in all_nodes:
  114. node_typ = mv.read_attrs(instance_model_path, node)["typeID"]
  115. if node_typ not in all_types:
  116. return {"OK":False, "error":"Type {} from instance model not in example models".format(node_typ)}
  117. # 2. Check node cardinality: a node is mandatory in the instance model if it occurs in every
  118. # example model. Additionally, it needs to occur at least n times if it occurs at least n times in every example model.
  119. print("Verify: checking node multiplicity ...")
  120. total_occurences = {k:[] for k in all_types}
  121. for node, occ_list in total_occurences.iteritems():
  122. for exm in example_models:
  123. num_occ = _count_occurences(node, exm)
  124. occ_list.append(num_occ)
  125. elem_cardinality = {k:min(v) for k, v in total_occurences.iteritems()}
  126. for el, min_occ in elem_cardinality.iteritems():
  127. if not _count_occurences(el, instance_model_path) >= min_occ:
  128. return {"OK":False, "error":"Node with type {} needs to occur at least {} times".format(el, min_occ)}
  129. # 3. Check attribute types: Is every attribute in instance model present in some example model?
  130. print("Verify: checking attributes ...")
  131. all_nodes = mv.all_instances(instance_model_path, "Node")
  132. for node in all_nodes:
  133. node_typ = mv.read_attrs(instance_model_path, node)["typeID"]
  134. attrs = commons.get_all_attributes_of_type(instance_model_path, node_typ)
  135. if len(attrs) == 0:
  136. continue
  137. #print("Attributes of node {} of type {} are: {}".format(node, node_typ, attrs))
  138. # check example models for such an attribute key
  139. for im_attribute in attrs:
  140. found = False
  141. for exm in example_models:
  142. if found:
  143. break
  144. exm_attrs = commons.get_all_attributes_of_type(exm, node_typ)
  145. for exm_attr in exm_attrs:
  146. if exm_attr.key == im_attribute.key:
  147. #print("Found attribute {} in model {}".format(im_attribute, exm))
  148. found = True
  149. break
  150. if not found:
  151. return {"OK":False, "error":"Attribute {} not found in example models".format(im_attribute)}
  152. # TODO: Attribute mandatory if present in all example model nodes
  153. # 4. Associations
  154. # Check multiplicity: an association needs to occur n>0 times if it occurs n>0 times in every example model
  155. # Check assocation type validity as well
  156. print("Verify: checking associations ...")
  157. all_edges = {k:[] for k in example_models}
  158. for exm in example_models:
  159. all_nodes = mv.all_instances(exm, "Node")
  160. for node in all_nodes:
  161. node_typ = mv.read_attrs(exm, node)["typeID"]
  162. out_associations = mv.read_outgoing(exm, node, "Edge")
  163. for assoc in out_associations:
  164. if assoc.startswith("__"):
  165. continue
  166. target = mv.read_association_destination(exm, assoc)[0]
  167. target_typ = mv.read_attrs(exm, target)["typeID"]
  168. all_edges[exm].append(Edge(node_typ, target_typ))
  169. unique_edges = []
  170. for edge_lst in all_edges.values():
  171. for edge in edge_lst:
  172. if not edge in unique_edges:
  173. unique_edges.append(edge)
  174. edge_ctr = {k:[] for k in unique_edges}
  175. for edge, ctr_lst in edge_ctr.iteritems():
  176. for exm, edges in all_edges.iteritems():
  177. ctr_lst.append(edges.count(edge))
  178. for k, v in edge_ctr.iteritems():
  179. edge_ctr[k] = min(v)
  180. print(edge_ctr) # {Edge("AP", "Tablet"): 0, Edge("Router", "PC"): 1, ...}
  181. return {"OK": True, "error": None}
  182. # check if mandatory edges are present in instance model
  183. # first, get all edges in instance model
  184. all_edges = mv.all_instances(instance_model_path, "Edge")
  185. im_edges = []
  186. for edge in all_edges:
  187. if edge.startswith("__"):
  188. continue
  189. edge_src = mv.read_association_source(instance_model_path, edge)[0]
  190. edge_dest = mv.read_association_destination(instance_model_path, edge)[0]
  191. edge_src_typ = mv.read_attrs(instance_model_path, edge_src)["typeID"]
  192. edge_dest_typ = mv.read_attrs(instance_model_path, edge_dest)["typeID"]
  193. im_edges.append(Edge(edge_src_typ, edge_dest_typ))
  194. # Check if all edges in instance models are valid (connecting types correspond to example models)
  195. for edge in im_edges:
  196. if edge not in edge_ctr.keys():
  197. return {"OK":False, "error":"Edge {} not valid according to example models".format(edge)}
  198. # now, check if for every mandatory edge, it occurs sufficiently enough
  199. for edge, cnt in edge_ctr.iteritems():
  200. if im_edges.count(edge) < cnt:
  201. return {"OK":False, "error":"Edge {} needs to appear {} times in instance model".format(edge, cnt)}
  202. return {"OK":True, "error":None}