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
Example #2
0
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