def test_find_common_reachable_vertex(): g = DirectedGraph() v0 = g.add_vertex(vertex_props={'license': 'L0', 'type': 'P'}) v1 = g.add_vertex(vertex_props={'license': 'L1', 'type': 'WP'}) g.add_edge(from_id=v0.id, to_id=v1.id) list_vertices = DirectedGraph.find_common_reachable_vertices( input_vertices=None) assert list_vertices is None list_vertices = DirectedGraph.find_common_reachable_vertices( input_vertices=[]) assert list_vertices is None list_vertices = DirectedGraph.find_common_reachable_vertices( input_vertices=[v0]) assert list_vertices is not None assert len(list_vertices) == 2 assert list_vertices[0] == v0 assert list_vertices[1] == v1 list_vertices = DirectedGraph.find_common_reachable_vertices( input_vertices=[v0, v1]) assert list_vertices is not None assert len(list_vertices) == 1 assert list_vertices[0] == v1 v2 = g.add_vertex(vertex_props={'license': 'L2', 'type': 'SP'}) g.add_edge(from_id=v2.id, to_id=v1.id) list_vertices = DirectedGraph.find_common_reachable_vertices( input_vertices=[v0, v2]) assert list_vertices is not None assert len(list_vertices) == 1 assert list_vertices[0] == v1
def test_find_common_reachable_vertex_empty_graph(): """Test if method DirectedGraph.find_common_reachable_vertices() works correctly.""" list_vertices = DirectedGraph.find_common_reachable_vertices(input_vertices=None) assert list_vertices is None list_vertices = DirectedGraph.find_common_reachable_vertices(input_vertices=[]) assert list_vertices is None v0 = Vertex(0, {}) list_vertices = DirectedGraph.find_common_reachable_vertices(input_vertices=[v0]) assert list_vertices is not None assert list_vertices[0] == v0
def compute_representative_license(self, input_licenses): """Compute representative license for given list of licenses. First, it tries to identify the input licenses by using known synonyms. If there exists at least one unknown license, this method gives up. It makes use of a very popular license graph available in [1]. After identifying license vertices in the graph, it tries to find the common reachable vertex from the input license vertices. If a common reachable vertex is not possible then there is a conflict and all pairs of conflicting licenses are identified by using the concept of compatibility classes. If a common reachable vertex is available, then its license becomes representative license. Note that we also try to find outlier licenses when representative license is available. [1] https://www.dwheeler.com/essays/floss-license-slide.html :param input_licenses: list of input licenses :return: representative license with supporting information """ output = { 'status': 'Failure', 'reason': 'Input is invalid', 'representative_license': None, 'unknown_licenses': [], 'conflict_licenses': [], 'outlier_licenses': [], 'synonyms': {} } if input_licenses is None: return output if len(input_licenses) == 0: return output # Find synonyms input_lic_synonyms = [self.find_synonym(y) for y in input_licenses] output['synonyms'] = dict(list(zip(input_licenses, input_lic_synonyms))) # Check if all input licenses are known if len(set(input_lic_synonyms) - set(self.known_licenses)) > 0: output['status'] = 'Unknown' output['reason'] = 'Some unknown licenses found' output['unknown_licenses'] = list( set(input_lic_synonyms) - set(self.known_licenses)) output['representative_license'] = None return output # Let's try to find a representative license # First, we need to find vertices for input licenses license_vertices = [] for lic in input_lic_synonyms: v = self.g.find_vertex(prop_name='license', prop_value=lic) assert v is not None license_vertices.append(v) assert len(license_vertices) == len(input_lic_synonyms) # Find common reachable vertices from input license vertices reachable_vertices = DirectedGraph.find_common_reachable_vertices(license_vertices) if len(reachable_vertices) == 0: # i.e. conflict output['status'] = 'Conflict' output['reason'] = 'Some licenses are in conflict' output['conflict_licenses'] = self._find_conflict_licenses(license_vertices) output['representative_license'] = None return output # Some representative license is possible :) # Check if one of the input licenses is the representative one common_destination = None license_vertex_ids = [x.id for x in license_vertices] for v in reachable_vertices: if v.id in license_vertex_ids: common_destination = v # TODO: should we break after this ? if common_destination is not None: output['status'] = 'Successful' output['reason'] = 'Representative license found' output['representative_license'] = \ common_destination.get_prop_value(prop_name='license') rep_lic_vertex = self.g.find_vertex('license', output['representative_license']) rep_lic_type = rep_lic_vertex.get_prop_value('type') output['outlier_licenses'] = self._find_outlier_licenses(license_vertices, rep_lic_type) return output # If one of the input licenses is NOT the representative one, then # let us pick up the least restrictive one for license_type in self.license_type_tuple: list_common_destinations = [ x for x in reachable_vertices if x.get_prop_value(prop_name='type') == license_type ] if len(list_common_destinations) > 0: output['status'] = 'Successful' output['reason'] = 'Representative license found' output['representative_license'] = \ list_common_destinations[0].get_prop_value(prop_name='license') rep_lic_vertex = self.g.find_vertex('license', output['representative_license']) rep_lic_type = rep_lic_vertex.get_prop_value('type') output['outlier_licenses'] = self._find_outlier_licenses(license_vertices, rep_lic_type) return output # We should have returned by now ! Returning from here is unexpected ! output['status'] = 'Failure' output['reason'] = 'Something unexpected happened!' output['representative_license'] = None return output