| from nodeset import * |
| import graphviz as gv |
| import codecs |
| |
| def nodePrintDot(node): |
| cleanname = "node_" + str(node.id).replace(";", "").replace("=", "") |
| dot = cleanname + " [label = \"{" + str(node.id) + "|" + str(node.browseName) + \ |
| "}\", shape=\"record\"]" |
| for r in node.references: |
| if isinstance(r.target, Node): |
| tgtname = "node_" + str(r.target.id).replace(";", "").replace("=", "") |
| dot = dot + "\n" |
| if r.isForward == True: |
| dot = dot + cleanname + " -> " + tgtname + " [label=\"" + \ |
| str(r.referenceType.browseName) + "\"]\n" |
| else: |
| if len(r.referenceType.inverseName) == 0: |
| logger.warn("Inverse name of reference is null " + str(r.referenceType.id)) |
| dot = dot + cleanname + " -> " + tgtname + \ |
| " [label=\"" + str(r.referenceType.inverseName) + "\"]\n" |
| return dot |
| |
| def printDotGraphWalk(nodeset, depth=1, filename="out.dot", rootNode=None, |
| followInverse=False, excludeNodeIds=[]): |
| """ Outputs a graphiz/dot description the nodes centered around rootNode. |
| |
| References beginning from rootNode will be followed for depth steps. If |
| "followInverse = True" is passed, then inverse (not Forward) references |
| will also be followed. |
| |
| Nodes can be excluded from the graph by passing a list of NodeIds as |
| string representation using excludeNodeIds (ex ["i=53", "ns=2;i=453"]). |
| |
| Output is written into filename to be parsed by dot/neato/srfp... |
| """ |
| iter = depth |
| processed = [] |
| if rootNode is None or not isinstance(rootNode, Node) or not rootNode in nodeset.nodes: |
| root = nodeset.getRoot() |
| else: |
| root = rootNode |
| |
| file = codecs.open(filename, 'w+', encoding='utf-8') |
| |
| if root is None: |
| return |
| |
| file.write("digraph ns {\n") |
| file.write(nodePrintDot(root)) |
| refs = [] |
| if followInverse == True: |
| refs = root.references # + root.getInverseReferences() |
| else: |
| for ref in root.references: |
| if ref.isForward: |
| refs.append(ref) |
| while iter > 0: |
| tmp = [] |
| for ref in refs: |
| if isinstance(ref.target, NodeId): |
| tgt = nodeset.nodes[ref.target] |
| if not str(tgt.id) in excludeNodeIds: |
| if not tgt in processed: |
| file.write(nodePrintDot(tgt)) |
| processed.append(tgt) |
| if ref.isForward is False and followInverse is True: |
| for inverseRef in tgt.inverseReferences: |
| refs.append(inverseRef) |
| tmp = tmp + tgt.references # + tgt.getInverseReferences() |
| elif ref.isForward: |
| for targetRef in tgt.references: |
| refs.append(targetRef) |
| refs = tmp |
| iter = iter - 1 |
| |
| file.write("}\n") |
| file.close() |
| |
| def getNodeString(node): |
| return node.browseName.name + " (" + str(node.id) + ")" |
| |
| def getReferenceString(nodeset, ref): |
| refNode = nodeset.nodes[ref.referenceType] |
| return refNode.browseName.name |
| |
| def getNodeStyle(node): |
| if isinstance(node, ReferenceTypeNode): |
| return {'shape': 'box', 'style': 'filled', 'fillcolor': '1', 'colorscheme': "pastel19"} |
| if isinstance(node, VariableTypeNode): |
| return {'shape': 'box', 'style': 'filled', 'fillcolor': '2', 'colorscheme': "pastel19"} |
| if isinstance(node, ObjectTypeNode): |
| return {'shape': 'box', 'style': 'filled', 'fillcolor': '3', 'colorscheme': "pastel19"} |
| if isinstance(node, DataTypeNode): |
| return {'shape': 'box', 'style': 'filled', 'fillcolor': '4', 'colorscheme': "pastel19"} |
| if isinstance(node, VariableNode): |
| return {'shape': 'ellipse', 'style': 'rounded,filled', 'fillcolor': '5', 'colorscheme': "pastel19"} |
| if isinstance(node, ObjectNode): |
| return {'shape': 'box', 'style': 'rounded,filled', 'fillcolor': '6', 'colorscheme': "pastel19"} |
| if isinstance(node, MethodNode): |
| return {'shape': 'box', 'style': 'rounded,filled', 'fillcolor': '7', 'colorscheme': "pastel19"} |
| if isinstance(node, ViewNode): |
| return {'shape': 'box', 'style': 'rounded,filled', 'fillcolor': '8', 'colorscheme': "pastel19"} |
| |
| def add_edges(graph, edges): |
| for e in edges: |
| if isinstance(e[0], tuple): |
| graph.edge(*e[0], **e[1]) |
| else: |
| graph.edge(*e) |
| return graph |
| |
| def add_nodes(graph, nodes): |
| for n in nodes: |
| if isinstance(n, tuple): |
| graph.node(n[0], **n[1]) |
| else: |
| graph.node(n) |
| return graph |
| |
| def addReferenceToGraph(nodeset, nodeFrom, nodeTo, reference, graph): |
| add_edges(graph, [((getNodeString(nodeFrom), getNodeString(nodeTo)), {'label': getReferenceString(nodeset, reference)})]) |
| |
| |
| def addNodeToGraph(nodeset, node, graph, alreadyAdded=set(), relevantReferences=set(), ignoreNodes=set(), isRoot=False, depth = 0): |
| if node.id in alreadyAdded or node.id in ignoreNodes: |
| return |
| alreadyAdded.add(node.id) |
| add_nodes(graph, [(getNodeString(node), getNodeStyle(node))]) |
| for ref in node.references: |
| if ref.referenceType in relevantReferences and ref.isForward: |
| targetNode = nodeset.nodes[ref.target] |
| if targetNode.id in ignoreNodes: |
| continue |
| addNodeToGraph(nodeset, targetNode, graph, alreadyAdded, depth=depth+1, relevantReferences=relevantReferences, |
| ignoreNodes = ignoreNodes) |
| addReferenceToGraph(nodeset, node, targetNode, ref, graph) |
| |
| |
| def generateGraphvizCode(nodeset, filename="dependencies", rootNode=None, excludeNodeIds=[]): |
| if rootNode is None or not isinstance(rootNode, Node) or not rootNode in nodeset.nodes: |
| root = nodeset.getRoot() |
| else: |
| root = rootNode |
| |
| if root is None: |
| return |
| |
| g = gv.dot.Digraph(name="NodeSet Dependency", format='pdf', ) |
| |
| alreadyAdded = set() |
| ignoreNodes = set() |
| # Ignore some nodes since almost all nodes will point to that which messes up the graph |
| ignoreNodes.add(NodeId("i=68")) # PropertyType |
| ignoreNodes.add(NodeId("i=63")) # BaseDataVariableType |
| ignoreNodes.add(NodeId("i=61")) # FolderType |
| addNodeToGraph(nodeset, root, g, alreadyAdded, isRoot=True, |
| relevantReferences=nodeset.getRelevantOrderingReferences(), |
| ignoreNodes=ignoreNodes) |
| |
| g.render(filename) |