spdx_tools.spdx.graph_generation
1# SPDX-FileCopyrightText: 2023 spdx contributors 2# 3# SPDX-License-Identifier: Apache-2.0 4from beartype.typing import Dict, List, Union 5 6from spdx_tools.spdx.model import File, Package, Snippet 7 8try: 9 from networkx import DiGraph 10except ImportError: 11 DiGraph = None 12from spdx_tools.spdx.document_utils import get_contained_spdx_elements 13from spdx_tools.spdx.model import Document, Relationship 14 15 16def export_graph_from_document(document: Document, file_name: str) -> None: 17 from networkx.drawing import nx_agraph 18 19 graph = generate_relationship_graph_from_spdx(document) 20 _color_nodes(graph) 21 attributes_graph = nx_agraph.to_agraph(graph) # convert to a pygraphviz graph 22 attributes_graph.draw(file_name, prog="dot") 23 24 25def generate_relationship_graph_from_spdx(document: Document) -> DiGraph: 26 from networkx import DiGraph 27 28 graph = DiGraph() 29 graph.add_node(document.creation_info.spdx_id, element=document.creation_info) 30 31 contained_elements: Dict[str, Union[Package, File, Snippet]] = get_contained_spdx_elements(document) 32 contained_element_nodes = [(spdx_id, {"element": element}) for spdx_id, element in contained_elements.items()] 33 graph.add_nodes_from(contained_element_nodes) 34 35 relationships_by_spdx_id: Dict[str, List[Relationship]] = dict() 36 for relationship in document.relationships: 37 relationships_by_spdx_id.setdefault(relationship.spdx_element_id, []).append(relationship) 38 39 for spdx_id, relationships in relationships_by_spdx_id.items(): 40 if spdx_id not in graph.nodes(): 41 # this will add any external spdx_id to the graph where we have no further information about the element, 42 # to indicate that this node represents an element we add the attribute "element" 43 graph.add_node(spdx_id, element=None) 44 for relationship in relationships: 45 relationship_node_key = relationship.spdx_element_id + "_" + relationship.relationship_type.name 46 graph.add_node(relationship_node_key, comment=relationship.comment) 47 graph.add_edge(relationship.spdx_element_id, relationship_node_key) 48 # if the related spdx element is SpdxNone or SpdxNoAssertion we need a type conversion 49 related_spdx_element_id = str(relationship.related_spdx_element_id) 50 51 if related_spdx_element_id not in graph.nodes(): 52 # this will add any external spdx_id to the graph where we have no further information about 53 # the element, to indicate that this node represents an element we add the attribute "element" 54 graph.add_node( 55 related_spdx_element_id, 56 element=None, 57 ) 58 graph.add_edge(relationship_node_key, related_spdx_element_id) 59 60 return graph 61 62 63def _color_nodes(graph: DiGraph) -> None: 64 for node in graph.nodes(): 65 if "_" in node: 66 # nodes representing a RelationshipType are concatenated with the spdx_element_id, 67 # to only see the RelationshipType when rendering the graph to a picture we add 68 # a label to these nodes 69 graph.add_node(node, color="lightgreen", label=node.split("_", 1)[-1]) 70 elif node == "SPDXRef-DOCUMENT": 71 graph.add_node(node, color="indianred2") 72 else: 73 graph.add_node(node, color="lightskyblue")
def
export_graph_from_document( document: spdx_tools.spdx.model.document.Document, file_name: str) -> None:
17def export_graph_from_document(document: Document, file_name: str) -> None: 18 from networkx.drawing import nx_agraph 19 20 graph = generate_relationship_graph_from_spdx(document) 21 _color_nodes(graph) 22 attributes_graph = nx_agraph.to_agraph(graph) # convert to a pygraphviz graph 23 attributes_graph.draw(file_name, prog="dot")
def
generate_relationship_graph_from_spdx( document: spdx_tools.spdx.model.document.Document) -> networkx.classes.digraph.DiGraph:
26def generate_relationship_graph_from_spdx(document: Document) -> DiGraph: 27 from networkx import DiGraph 28 29 graph = DiGraph() 30 graph.add_node(document.creation_info.spdx_id, element=document.creation_info) 31 32 contained_elements: Dict[str, Union[Package, File, Snippet]] = get_contained_spdx_elements(document) 33 contained_element_nodes = [(spdx_id, {"element": element}) for spdx_id, element in contained_elements.items()] 34 graph.add_nodes_from(contained_element_nodes) 35 36 relationships_by_spdx_id: Dict[str, List[Relationship]] = dict() 37 for relationship in document.relationships: 38 relationships_by_spdx_id.setdefault(relationship.spdx_element_id, []).append(relationship) 39 40 for spdx_id, relationships in relationships_by_spdx_id.items(): 41 if spdx_id not in graph.nodes(): 42 # this will add any external spdx_id to the graph where we have no further information about the element, 43 # to indicate that this node represents an element we add the attribute "element" 44 graph.add_node(spdx_id, element=None) 45 for relationship in relationships: 46 relationship_node_key = relationship.spdx_element_id + "_" + relationship.relationship_type.name 47 graph.add_node(relationship_node_key, comment=relationship.comment) 48 graph.add_edge(relationship.spdx_element_id, relationship_node_key) 49 # if the related spdx element is SpdxNone or SpdxNoAssertion we need a type conversion 50 related_spdx_element_id = str(relationship.related_spdx_element_id) 51 52 if related_spdx_element_id not in graph.nodes(): 53 # this will add any external spdx_id to the graph where we have no further information about 54 # the element, to indicate that this node represents an element we add the attribute "element" 55 graph.add_node( 56 related_spdx_element_id, 57 element=None, 58 ) 59 graph.add_edge(relationship_node_key, related_spdx_element_id) 60 61 return graph