123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- import wrappers.modelverse as mv
- import commons
- class Edge(object):
- """
- Small helper class for association validation.
- Stores an edge as a tuple of (source, dest)
- """
- def __init__(self, src, dest):
- self.src = src
- self.dest = dest
- def __eq__(self, other):
- if other.src == self.src and other.dest == self.dest:
- return True
- return False
- def __repr__(self):
- return "{}->{}".format(self.src, self.dest)
- 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):
- """
- Verify an instance model against all example models (mv folder currently hard-coded)
- Performs checks similar to the linguistic conformance check as seen in the MDE class:
- 1. Node typing
- 2. Node cardinality
- 3. Attribute validity
- 4. Associations
- """
- 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:
- 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))
- # Now, check for every element in instance model if it is a valid type
- all_nodes = mv.all_instances(instance_model_path, "Node")
- for node in all_nodes:
- node_typ = mv.read_attrs(instance_model_path, node)["typeID"]
- if node_typ not in all_types:
- return {"OK":False, "error":"Type {} from instance model not in example models".format(node_typ)}
- # 2. Check node cardinality: a node is mandatory in the instance model if it occurs in every
- # example model. Additionally, it needs to occur at least n times if it occurs at least n times in every example model.
- 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:
- num_occ = _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 _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)}
- # 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)["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:
- if found:
- break
- 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))
- found = True
- break
- if not found:
- return {"OK":False, "error":"Attribute {} not found in example models".format(im_attribute)}
- # TODO: Attribute mandatory if present in all example model nodes
- # 4. Associations
- # 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}
- for exm in example_models:
- all_nodes = mv.all_instances(exm, "Node")
- for node in all_nodes:
- 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)["typeID"]
- all_edges[exm].append(Edge(node_typ, target_typ))
- unique_edges = []
- for edge_lst in all_edges.values():
- for edge in edge_lst:
- if not edge in unique_edges:
- unique_edges.append(edge)
- edge_ctr = {k:[] for k in unique_edges}
- for edge, ctr_lst in edge_ctr.iteritems():
- for exm, edges in all_edges.iteritems():
- ctr_lst.append(edges.count(edge))
- for k, v in edge_ctr.iteritems():
- edge_ctr[k] = min(v)
- 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
- all_edges = mv.all_instances(instance_model_path, "Edge")
- im_edges = []
- for edge in all_edges:
- if edge.startswith("__"):
- 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)["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)
- for edge in im_edges:
- if edge not in edge_ctr.keys():
- return {"OK":False, "error":"Edge {} not valid according to example models".format(edge)}
- # now, check if for every mandatory edge, it occurs sufficiently enough
- for edge, cnt in edge_ctr.iteritems():
- if im_edges.count(edge) < cnt:
- return {"OK":False, "error":"Edge {} needs to appear {} times in instance model".format(edge, cnt)}
- return {"OK":True, "error":None}
|