verifier.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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. # Check for every node in instance model if it has a valid type
  40. all_nodes = mv.all_instances(self._instance_model, "Node")
  41. for node in all_nodes:
  42. node_typ = commons.get_node_type(self._instance_model, node)
  43. if node_typ not in self._available_types:
  44. return {"OK": False, "error": "Type {} from instance model not in example models".format(node_typ)}
  45. return {"OK":True, "error":None}
  46. def verify_node_multiplicity(self):
  47. """
  48. A node is mandatory in the instance model if it occurs in every example model.
  49. More specifically, if a node type appears occ = [n0, n1, ... n_i] times in example model i,
  50. it needs to occur min(occ) in the instance model.
  51. """
  52. total_occurences = {k: [] for k in self._available_types}
  53. for node, occ_list in total_occurences.iteritems():
  54. for exm in self._example_models:
  55. num_occ = commons.count_occurences(node, exm)
  56. occ_list.append(num_occ)
  57. elem_cardinality = {k: min(v) for k, v in total_occurences.iteritems()}
  58. for el, min_occ in elem_cardinality.iteritems():
  59. if not commons.count_occurences(el, self._instance_model) >= min_occ:
  60. return {"OK": False, "error": "Node with type {} needs to occur at least {} times".format(el, min_occ)}
  61. return {"OK": True, "error": None}
  62. def _get_attributes_of_all_types(self):
  63. """
  64. Helper for attribute check that returns a dictionary of the form {type:[attr_key_1, attr_key_2, ...], ...},
  65. listing all attributes of every type from example models
  66. """
  67. attrs_of_types = {node_type:[] for node_type in self._available_types}
  68. for exm in self._example_models:
  69. for node_type, attrs in attrs_of_types.iteritems():
  70. attrs_of_type_in_exm = [x.key for x in commons.get_all_attributes_of_type(exm, node_type)]
  71. for attr in attrs_of_type_in_exm:
  72. if not attr in attrs:
  73. attrs.append(attr)
  74. return attrs_of_types
  75. def _is_attribute_mandatory_in(self, model, node_type, attr_key):
  76. """
  77. Helper for attribute check that returns True if the attribute attr_key of type node_type is mandatory (i.e
  78. occurs in every node of the type) in model.
  79. """
  80. nodes_of_type = commons.all_nodes_with_type(model, node_type)
  81. attrs_of_type = commons.get_all_attributes_of_type(model, node_type)
  82. ctr = 0
  83. for attr in attrs_of_type:
  84. if attr.key == attr_key:
  85. ctr += 1
  86. return len(nodes_of_type) == ctr
  87. def _is_attribute_mandatory(self, node_type, attr_key):
  88. """
  89. Helper for attribute check that returns True if the attribute attr_key of type node_type is mandatory by
  90. looking at all example models.
  91. """
  92. mand_list = []
  93. for exm in self._example_models:
  94. is_mandatory = self._is_attribute_mandatory_in(exm, node_type, attr_key)
  95. mand_list.append(is_mandatory)
  96. return all(mand_list)
  97. def verify_attributes(self):
  98. """
  99. 1. For every attribute key of a typed node in the instance model, there must be a corresponding
  100. attribute key in some example model.
  101. 2. An attribute for a type is mandatory if it occurs in every example model.
  102. """
  103. # Check attribute keys for every node in instance model
  104. all_nodes = mv.all_instances(self._instance_model, "Node")
  105. for node in all_nodes:
  106. attrs = commons.get_attributes_of_node(self._instance_model, node)
  107. if not attrs:
  108. continue
  109. node_typ = commons.get_node_type(self._instance_model, node)
  110. for attr in attrs:
  111. # check every example model if such an attribute key is present for the specific type
  112. for exm in self._example_models:
  113. exm_attrs = commons.get_all_attributes_of_type(exm, node_typ)
  114. if attr.key in [x.key for x in exm_attrs]:
  115. break
  116. else:
  117. # loop completed without break, so we did not find any key in example models
  118. return {"OK": False, "error": "No key {} for type {} in example models".format(attr.key, node_typ)}
  119. # Check if mandatory attributes are present in instance model
  120. attr_mandatory = {node_type:{} for node_type in self._available_types}
  121. all_attrs = self._get_attributes_of_all_types()
  122. for typ_i, attr_list in all_attrs.iteritems():
  123. for typ_j, dic in attr_mandatory.iteritems():
  124. if typ_j == typ_i:
  125. for attr in attr_list:
  126. dic[attr] = False
  127. # have dict like {"PC": {"owner":False}, "Router": {"IP":False}, ...}
  128. # now need to check which one is mandatory and set the boolean accordingly
  129. for node_type, attr_dict in attr_mandatory.iteritems():
  130. for attr, _ in attr_dict.iteritems():
  131. if self._is_attribute_mandatory(node_type, attr):
  132. attr_dict[attr] = True
  133. # for every node in instance model, check if it has the mandatory attributes
  134. for node in mv.all_instances(self._instance_model, "Node"):
  135. node_type = commons.get_node_type(self._instance_model, node)
  136. node_attrs = commons.get_attributes_of_node(self._instance_model, node)
  137. attr_mand = attr_mandatory[node_type]
  138. for attr, mand in attr_mand.iteritems():
  139. if mand:
  140. if not attr in [x.key for x in node_attrs]:
  141. return {"OK": False, "error":"Attribute {} for type {} mandatory".format(attr, node_type)}
  142. return {"OK": True, "error": None}
  143. def verify_associations(self):
  144. """
  145. 1. If an association between two types is present in all example models, it is mandatory and must
  146. therefore be present in the instance model (if it contains the same two types).
  147. 2. For every association in the instance model, the types of the source and target must correspond
  148. to the types of an association in some example model. This check should not be necessary since this
  149. is already enforced while instance modeling (see im_scene.py, draw_edge() which checks this when trying
  150. to connect two nodes).
  151. """
  152. return {"OK": True, "error": None}
  153. def verify(instance_model_path):
  154. """
  155. Verify an instance model against all example models (mv folder currently hard-coded)
  156. Performs checks similar to the linguistic conformance check as seen in the MDE class:
  157. 1. Node typing
  158. 2. Node cardinality
  159. 3. Attribute validity
  160. 4. Associations
  161. """
  162. example_models = commons.all_example_models()
  163. # 1. Check if all nodes in instance model are typed by a node in the example models
  164. # For that, first get all available types from example models
  165. print("Verify: checking node typing ...")
  166. all_types = []
  167. for exm in example_models:
  168. all_nodes = mv.all_instances(exm, "Node")
  169. for node in all_nodes:
  170. typ = mv.read_attrs(exm, node)["typeID"]
  171. if not typ in all_types:
  172. all_types.append(typ)
  173. #print("Verify: types found in example models: {}".format(all_types))
  174. # Now, check for every element in instance model if it is a valid type
  175. all_nodes = mv.all_instances(instance_model_path, "Node")
  176. for node in all_nodes:
  177. node_typ = mv.read_attrs(instance_model_path, node)["typeID"]
  178. if node_typ not in all_types:
  179. return {"OK":False, "error":"Type {} from instance model not in example models".format(node_typ)}
  180. # 2. Check node cardinality: a node is mandatory in the instance model if it occurs in every
  181. # example model. Additionally, it needs to occur at least n times if it occurs at least n times in every example model.
  182. print("Verify: checking node multiplicity ...")
  183. total_occurences = {k:[] for k in all_types}
  184. for node, occ_list in total_occurences.iteritems():
  185. for exm in example_models:
  186. num_occ = _count_occurences(node, exm)
  187. occ_list.append(num_occ)
  188. elem_cardinality = {k:min(v) for k, v in total_occurences.iteritems()}
  189. for el, min_occ in elem_cardinality.iteritems():
  190. if not _count_occurences(el, instance_model_path) >= min_occ:
  191. return {"OK":False, "error":"Node with type {} needs to occur at least {} times".format(el, min_occ)}
  192. # 3. Check attribute types: Is every attribute in instance model present in some example model?
  193. print("Verify: checking attributes ...")
  194. all_nodes = mv.all_instances(instance_model_path, "Node")
  195. for node in all_nodes:
  196. node_typ = mv.read_attrs(instance_model_path, node)["typeID"]
  197. attrs = commons.get_all_attributes_of_type(instance_model_path, node_typ)
  198. if len(attrs) == 0:
  199. continue
  200. #print("Attributes of node {} of type {} are: {}".format(node, node_typ, attrs))
  201. # check example models for such an attribute key
  202. for im_attribute in attrs:
  203. found = False
  204. for exm in example_models:
  205. if found:
  206. break
  207. exm_attrs = commons.get_all_attributes_of_type(exm, node_typ)
  208. for exm_attr in exm_attrs:
  209. if exm_attr.key == im_attribute.key:
  210. #print("Found attribute {} in model {}".format(im_attribute, exm))
  211. found = True
  212. break
  213. if not found:
  214. return {"OK":False, "error":"Attribute {} not found in example models".format(im_attribute)}
  215. # TODO: Attribute mandatory if present in all example model nodes
  216. # 4. Associations
  217. # Check multiplicity: an association needs to occur n>0 times if it occurs n>0 times in every example model
  218. # Check assocation type validity as well
  219. print("Verify: checking associations ...")
  220. all_edges = {k:[] for k in example_models}
  221. for exm in example_models:
  222. all_nodes = mv.all_instances(exm, "Node")
  223. for node in all_nodes:
  224. node_typ = mv.read_attrs(exm, node)["typeID"]
  225. out_associations = mv.read_outgoing(exm, node, "Edge")
  226. for assoc in out_associations:
  227. if assoc.startswith("__"):
  228. continue
  229. target = mv.read_association_destination(exm, assoc)[0]
  230. target_typ = mv.read_attrs(exm, target)["typeID"]
  231. all_edges[exm].append(Edge(node_typ, target_typ))
  232. unique_edges = []
  233. for edge_lst in all_edges.values():
  234. for edge in edge_lst:
  235. if not edge in unique_edges:
  236. unique_edges.append(edge)
  237. edge_ctr = {k:[] for k in unique_edges}
  238. for edge, ctr_lst in edge_ctr.iteritems():
  239. for exm, edges in all_edges.iteritems():
  240. ctr_lst.append(edges.count(edge))
  241. for k, v in edge_ctr.iteritems():
  242. edge_ctr[k] = min(v)
  243. print(edge_ctr) # {Edge("AP", "Tablet"): 0, Edge("Router", "PC"): 1, ...}
  244. return {"OK": True, "error": None}
  245. # check if mandatory edges are present in instance model
  246. # first, get all edges in instance model
  247. all_edges = mv.all_instances(instance_model_path, "Edge")
  248. im_edges = []
  249. for edge in all_edges:
  250. if edge.startswith("__"):
  251. continue
  252. edge_src = mv.read_association_source(instance_model_path, edge)[0]
  253. edge_dest = mv.read_association_destination(instance_model_path, edge)[0]
  254. edge_src_typ = mv.read_attrs(instance_model_path, edge_src)["typeID"]
  255. edge_dest_typ = mv.read_attrs(instance_model_path, edge_dest)["typeID"]
  256. im_edges.append(Edge(edge_src_typ, edge_dest_typ))
  257. # Check if all edges in instance models are valid (connecting types correspond to example models)
  258. for edge in im_edges:
  259. if edge not in edge_ctr.keys():
  260. return {"OK":False, "error":"Edge {} not valid according to example models".format(edge)}
  261. # now, check if for every mandatory edge, it occurs sufficiently enough
  262. for edge, cnt in edge_ctr.iteritems():
  263. if im_edges.count(edge) < cnt:
  264. return {"OK":False, "error":"Edge {} needs to appear {} times in instance model".format(edge, cnt)}
  265. return {"OK":True, "error":None}