verifier.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import wrappers.modelverse as mv
  2. class Edge(object):
  3. """
  4. Small helper class for association validation.
  5. Stores an edge as a tuple of (source, dest)
  6. """
  7. def __init__(self, src, dest):
  8. self.src = src
  9. self.dest = dest
  10. def __eq__(self, other):
  11. if other.src == self.src and other.dest == self.dest:
  12. return True
  13. return False
  14. def __repr__(self):
  15. return "{}->{}".format(self.src, self.dest)
  16. class Attribute(object):
  17. """
  18. Helper class for attribute representation as key value pair
  19. """
  20. def __init__(self, key, val):
  21. self.key = key
  22. self.val = val
  23. def __eq__(self, other):
  24. if other.key == self.key and other.val == self.val:
  25. return True
  26. return False
  27. def __repr__(self):
  28. return "{}={}".format(self.key, self.val)
  29. # returns a list of attributes of a node with type node_type in a model
  30. def _get_attributes_of(node_type, model):
  31. ret = []
  32. node_attribute_links = mv.all_instances(model, "NodeAttribute")
  33. for link in node_attribute_links:
  34. src_node = mv.read_association_source(model, link)[0]
  35. src_typ = mv.read_attrs(model, src_node)["type"]
  36. if src_typ != node_type:
  37. continue
  38. attr_node_name = mv.read_association_destination(model, link)[0]
  39. attr_dict = mv.read_attrs(model, attr_node_name)
  40. attr = Attribute(attr_dict["key"], attr_dict["value"])
  41. ret.append(attr)
  42. return ret
  43. # returns the number of occurences of a node with type node_type in a model
  44. def _count_occurences(node_type, model):
  45. ctr = 0
  46. all_nodes = mv.all_instances(model, "Node")
  47. for node in all_nodes:
  48. typ = mv.read_attrs(model, node)["type"]
  49. if typ == node_type:
  50. ctr += 1
  51. return ctr
  52. def verify(instance_model_path):
  53. """
  54. Verify an instance model against all example models (mv folder currently hard-coded)
  55. Performs checks similar to the linguistic conformance check as seen in the MDE class:
  56. 1. Node typing
  57. 2. Node cardinality
  58. 3. Attribute validity
  59. 4. Associations
  60. """
  61. example_models = mv.model_list("models/example")
  62. example_models_full = ["models/example/"+exm for exm in example_models]
  63. # 1. Check if all nodes in instance model are typed by a node in the example models
  64. # For that, first get all available types from example models
  65. print("Verify: checking node typing ...")
  66. all_types = []
  67. for exm in example_models_full:
  68. all_nodes = mv.all_instances(exm, "Node")
  69. for node in all_nodes:
  70. typ = mv.read_attrs(exm, node)["type"]
  71. if not typ in all_types:
  72. all_types.append(typ)
  73. #print("Verify: types found in example models: {}".format(all_types))
  74. # Now, check for every element in instance model if it is a valid type
  75. all_nodes = mv.all_instances(instance_model_path, "Node")
  76. for node in all_nodes:
  77. node_typ = mv.read_attrs(instance_model_path, node)["type"]
  78. if node_typ not in all_types:
  79. return {"OK":False, "error":"Type {} from instance model not in example models".format(node_typ)}
  80. # 2. Check node cardinality: a node is mandatory in the instance model if it occurs in every
  81. # example model. Additionally, it needs to occur at least n times if it occurs at least n times in every example model.
  82. print("Verify: checking node multiplicity ...")
  83. total_occurences = {k:[] for k in all_types}
  84. for node, occ_list in total_occurences.iteritems():
  85. for exm in example_models_full:
  86. num_occ = _count_occurences(node, exm)
  87. occ_list.append(num_occ)
  88. elem_cardinality = {k:min(v) for k, v in total_occurences.iteritems()}
  89. for el, min_occ in elem_cardinality.iteritems():
  90. if not _count_occurences(el, instance_model_path) >= min_occ:
  91. return {"OK":False, "error":"Node with type {} needs to occur at least {} times".format(el, min_occ)}
  92. # 3. Check attribute types: Is every attribute in instance model present in some example model?
  93. print("Verify: checking attributes ...")
  94. all_nodes = mv.all_instances(instance_model_path, "Node")
  95. for node in all_nodes:
  96. node_typ = mv.read_attrs(instance_model_path, node)["type"]
  97. attrs = _get_attributes_of(node_typ, instance_model_path)
  98. if len(attrs) == 0:
  99. continue
  100. #print("Attributes of node {} of type {} are: {}".format(node, node_typ, attrs))
  101. # check example models for such an attribute key
  102. for im_attribute in attrs:
  103. found = False
  104. for exm in example_models_full:
  105. if found:
  106. break
  107. exm_attrs = _get_attributes_of(node_typ, exm)
  108. for exm_attr in exm_attrs:
  109. if exm_attr.key == im_attribute.key:
  110. #print("Found attribute {} in model {}".format(im_attribute, exm))
  111. found = True
  112. break
  113. if not found:
  114. return {"OK":False, "error":"Attribute {} not found in example models".format(im_attribute)}
  115. # TODO: Attribute mandatory if present in all example model nodes
  116. # 4. Associations
  117. # Check multiplicity: an association needs to occur n>0 times if it occurs n>0 times in every example model
  118. # Check assocation type validity as well
  119. print("Verify: checking associations ...")
  120. all_edges = {k:[] for k in example_models_full}
  121. for exm in example_models_full:
  122. all_nodes = mv.all_instances(exm, "Node")
  123. for node in all_nodes:
  124. node_typ = mv.read_attrs(exm, node)["type"]
  125. out_associations = mv.read_outgoing(exm, node, "Edge")
  126. for assoc in out_associations:
  127. if assoc.startswith("__"):
  128. continue
  129. target = mv.read_association_destination(exm, assoc)[0]
  130. target_typ = mv.read_attrs(exm, target)["type"]
  131. all_edges[exm].append(Edge(node_typ, target_typ))
  132. unique_edges = []
  133. for edge_lst in all_edges.values():
  134. for edge in edge_lst:
  135. if not edge in unique_edges:
  136. unique_edges.append(edge)
  137. edge_ctr = {k:[] for k in unique_edges}
  138. for edge, ctr_lst in edge_ctr.iteritems():
  139. for exm, edges in all_edges.iteritems():
  140. ctr_lst.append(edges.count(edge))
  141. for k, v in edge_ctr.iteritems():
  142. edge_ctr[k] = min(v)
  143. #print(edge_ctr) # {Edge("AP", "Tablet"): 0, Edge("Router", "PC"): 1, ...}
  144. # check if mandatory edges are present in instance model
  145. # first, get all edges in instance model
  146. all_edges = mv.all_instances(instance_model_path, "Edge")
  147. im_edges = []
  148. for edge in all_edges:
  149. if edge.startswith("__"):
  150. continue
  151. edge_src = mv.read_association_source(instance_model_path, edge)[0]
  152. edge_dest = mv.read_association_destination(instance_model_path, edge)[0]
  153. edge_src_typ = mv.read_attrs(instance_model_path, edge_src)["type"]
  154. edge_dest_typ = mv.read_attrs(instance_model_path, edge_dest)["type"]
  155. im_edges.append(Edge(edge_src_typ, edge_dest_typ))
  156. # Check if all edges in instance models are valid (connecting types correspond to example models)
  157. for edge in im_edges:
  158. if edge not in edge_ctr.keys():
  159. return {"OK":False, "error":"Edge {} not valid according to example models".format(edge)}
  160. # now, check if for every mandatory edge, it occurs sufficiently enough
  161. for edge, cnt in edge_ctr.iteritems():
  162. if im_edges.count(edge) < cnt:
  163. return {"OK":False, "error":"Edge {} needs to appear {} times in instance model".format(edge, cnt)}
  164. return {"OK":True, "error":None}