backend_graphviz.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. from nodeset import *
  2. import graphviz as gv
  3. import codecs
  4. def nodePrintDot(node):
  5. cleanname = "node_" + str(node.id).replace(";", "").replace("=", "")
  6. dot = cleanname + " [label = \"{" + str(node.id) + "|" + str(node.browseName) + \
  7. "}\", shape=\"record\"]"
  8. for r in node.references:
  9. if isinstance(r.target, Node):
  10. tgtname = "node_" + str(r.target.id).replace(";", "").replace("=", "")
  11. dot = dot + "\n"
  12. if r.isForward == True:
  13. dot = dot + cleanname + " -> " + tgtname + " [label=\"" + \
  14. str(r.referenceType.browseName) + "\"]\n"
  15. else:
  16. if len(r.referenceType.inverseName) == 0:
  17. logger.warn("Inverse name of reference is null " + str(r.referenceType.id))
  18. dot = dot + cleanname + " -> " + tgtname + \
  19. " [label=\"" + str(r.referenceType.inverseName) + "\"]\n"
  20. return dot
  21. def printDotGraphWalk(nodeset, depth=1, filename="out.dot", rootNode=None,
  22. followInverse=False, excludeNodeIds=[]):
  23. """ Outputs a graphiz/dot description the nodes centered around rootNode.
  24. References beginning from rootNode will be followed for depth steps. If
  25. "followInverse = True" is passed, then inverse (not Forward) references
  26. will also be followed.
  27. Nodes can be excluded from the graph by passing a list of NodeIds as
  28. string representation using excludeNodeIds (ex ["i=53", "ns=2;i=453"]).
  29. Output is written into filename to be parsed by dot/neato/srfp...
  30. """
  31. iter = depth
  32. processed = []
  33. if rootNode is None or not isinstance(rootNode, Node) or not rootNode in nodeset.nodes:
  34. root = nodeset.getRoot()
  35. else:
  36. root = rootNode
  37. file = codecs.open(filename, 'w+', encoding='utf-8')
  38. if root is None:
  39. return
  40. file.write("digraph ns {\n")
  41. file.write(nodePrintDot(root))
  42. refs = []
  43. if followInverse == True:
  44. refs = root.references # + root.getInverseReferences()
  45. else:
  46. for ref in root.references:
  47. if ref.isForward:
  48. refs.append(ref)
  49. while iter > 0:
  50. tmp = []
  51. for ref in refs:
  52. if isinstance(ref.target, NodeId):
  53. tgt = nodeset.nodes[ref.target]
  54. if not str(tgt.id) in excludeNodeIds:
  55. if not tgt in processed:
  56. file.write(nodePrintDot(tgt))
  57. processed.append(tgt)
  58. if ref.isForward is False and followInverse is True:
  59. for inverseRef in tgt.inverseReferences:
  60. refs.append(inverseRef)
  61. tmp = tmp + tgt.references # + tgt.getInverseReferences()
  62. elif ref.isForward:
  63. for targetRef in tgt.references:
  64. refs.append(targetRef)
  65. refs = tmp
  66. iter = iter - 1
  67. file.write("}\n")
  68. file.close()
  69. def getNodeString(node):
  70. return node.browseName.name + " (" + str(node.id) + ")"
  71. def getReferenceString(nodeset, ref):
  72. refNode = nodeset.nodes[ref.referenceType]
  73. return refNode.browseName.name
  74. def getNodeStyle(node):
  75. if isinstance(node, ReferenceTypeNode):
  76. return {'shape': 'box', 'style': 'filled', 'fillcolor': '1', 'colorscheme': "pastel19"}
  77. if isinstance(node, VariableTypeNode):
  78. return {'shape': 'box', 'style': 'filled', 'fillcolor': '2', 'colorscheme': "pastel19"}
  79. if isinstance(node, ObjectTypeNode):
  80. return {'shape': 'box', 'style': 'filled', 'fillcolor': '3', 'colorscheme': "pastel19"}
  81. if isinstance(node, DataTypeNode):
  82. return {'shape': 'box', 'style': 'filled', 'fillcolor': '4', 'colorscheme': "pastel19"}
  83. if isinstance(node, VariableNode):
  84. return {'shape': 'ellipse', 'style': 'rounded,filled', 'fillcolor': '5', 'colorscheme': "pastel19"}
  85. if isinstance(node, ObjectNode):
  86. return {'shape': 'box', 'style': 'rounded,filled', 'fillcolor': '6', 'colorscheme': "pastel19"}
  87. if isinstance(node, MethodNode):
  88. return {'shape': 'box', 'style': 'rounded,filled', 'fillcolor': '7', 'colorscheme': "pastel19"}
  89. if isinstance(node, ViewNode):
  90. return {'shape': 'box', 'style': 'rounded,filled', 'fillcolor': '8', 'colorscheme': "pastel19"}
  91. def add_edges(graph, edges):
  92. for e in edges:
  93. if isinstance(e[0], tuple):
  94. graph.edge(*e[0], **e[1])
  95. else:
  96. graph.edge(*e)
  97. return graph
  98. def add_nodes(graph, nodes):
  99. for n in nodes:
  100. if isinstance(n, tuple):
  101. graph.node(n[0], **n[1])
  102. else:
  103. graph.node(n)
  104. return graph
  105. def addReferenceToGraph(nodeset, nodeFrom, nodeTo, reference, graph):
  106. add_edges(graph, [((getNodeString(nodeFrom), getNodeString(nodeTo)), {'label': getReferenceString(nodeset, reference)})])
  107. def addNodeToGraph(nodeset, node, graph, alreadyAdded=set(), relevantReferences=set(), ignoreNodes=set(), isRoot=False, depth = 0):
  108. if node.id in alreadyAdded or node.id in ignoreNodes:
  109. return
  110. alreadyAdded.add(node.id)
  111. add_nodes(graph, [(getNodeString(node), getNodeStyle(node))])
  112. for ref in node.references:
  113. if ref.referenceType in relevantReferences and ref.isForward:
  114. targetNode = nodeset.nodes[ref.target]
  115. if targetNode.id in ignoreNodes:
  116. continue
  117. addNodeToGraph(nodeset, targetNode, graph, alreadyAdded, depth=depth+1, relevantReferences=relevantReferences,
  118. ignoreNodes = ignoreNodes)
  119. addReferenceToGraph(nodeset, node, targetNode, ref, graph)
  120. def generateGraphvizCode(nodeset, filename="dependencies", rootNode=None, excludeNodeIds=[]):
  121. if rootNode is None or not isinstance(rootNode, Node) or not rootNode in nodeset.nodes:
  122. root = nodeset.getRoot()
  123. else:
  124. root = rootNode
  125. if root is None:
  126. return
  127. g = gv.dot.Digraph(name="NodeSet Dependency", format='pdf', )
  128. alreadyAdded = set()
  129. ignoreNodes = set()
  130. # Ignore some nodes since almost all nodes will point to that which messes up the graph
  131. ignoreNodes.add(NodeId("i=68")) # PropertyType
  132. ignoreNodes.add(NodeId("i=63")) # BaseDataVariableType
  133. ignoreNodes.add(NodeId("i=61")) # FolderType
  134. addNodeToGraph(nodeset, root, g, alreadyAdded, isRoot=True,
  135. relevantReferences=nodeset.getRelevantOrderingReferences(),
  136. ignoreNodes=ignoreNodes)
  137. g.render(filename)