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}