wg-backend-django/dell-env/lib/python3.11/site-packages/dash_cytoscape/utils/Tree.py
2023-10-30 14:40:43 +07:00

183 lines
5.2 KiB
Python

from collections import deque
class Tree(object):
def __init__(self,
node_id,
children=None,
data=None,
props=None,
edge_data=None,
edge_props=None):
"""
A class to facilitate tree manipulation in Cytoscape.
:param node_id: The ID of this tree, passed to the node data dict
:param children: The children of this tree, also Tree objects
:param data: Dictionary passed to this tree's node data dict
:param props: Dictionary passed to this tree's node dict, containing the node's props
:param edge_data: Dictionary passed to the data dict of the edge connecting this tree to its
parent
:param edge_props: Dictionary passed to the dict of the edge connecting this tree to its
parent
"""
if children is None:
children = []
if data is None:
data = {}
if props is None:
props = {}
if edge_data is None:
edge_data = {}
if edge_props is None:
edge_props = {}
self.node_id = node_id
self.children = children
self.data = data
self.props = props
self.edge_data = edge_data
self.edge_props = edge_props
self.index = {}
def _dfs(self, search_id):
if self.node_id == search_id:
return self
elif self.is_leaf():
return None
else:
for child in self.children:
result = child.dfs()
if result:
return result
return None
def _bfs(self, search_id):
stack = deque([self])
while stack:
tree = stack.popleft()
if tree.node_id == search_id:
return tree
if not tree.is_leaf():
for child in tree.children:
stack.append(child)
return None
def is_leaf(self):
"""
:return: If the Tree is a leaf or not
"""
return not self.children
def add_children(self, children):
"""
Add one or more children to the current children of a Tree.
:param children: List of Tree objects (one object or more)
"""
self.children.extend(children)
def get_edges(self):
"""
Get all the edges of the tree in Cytoscape JSON format.
:return: List of dictionaries, each specifying an edge
"""
edges = []
for child in self.children:
di = {
'data': {
'source': self.node_id,
'target': child.node_id
}
}
di['data'].update(child.edge_data)
di.update(child.edge_props)
edges.append(di)
for child in self.children:
edges.extend(child.get_edges())
return edges
def get_nodes(self):
"""
Get all the nodes of the tree in Cytoscape JSON format.
:return: List of dictionaries, each specifying a node
"""
di = {
'data': {
'id': self.node_id
}
}
di['data'].update(self.data)
di.update(self.props)
nodes = [di]
for child in self.children:
nodes.extend(child.get_nodes())
return nodes
def get_elements(self):
"""
Get all the elements of the tree in Cytoscape JSON format.
:return: List of dictionaries, each specifying an element
"""
return self.get_nodes() + self.get_edges()
def find_by_id(self, search_id, method='bfs'):
"""
Find a Tree object by its ID.
:param search_id: the queried ID
:param method: Which traversal method to use. Either "bfs" or "dfs"
:return: Tree object if found, None otherwise
"""
method = method.lower()
if method == 'bfs':
return self._bfs(search_id)
elif method == 'dfs':
return self._dfs(search_id)
else:
raise ValueError('Unknown traversal method')
def create_index(self):
"""
Generate the index of a Tree, and set it in place. If there was a previous index, it is
erased. This uses a BFS traversal. Please note that when a child is added to the tree,
the index is not regenerated. Furthermore, an index assigned to a parent cannot be
accessed by its children, and vice-versa.
:return: Dictionary mapping node_id to Tree object
"""
stack = deque([self])
self.index = {}
while stack:
tree = stack.popleft()
self.index[tree.node_id] = tree
if not tree.is_leaf():
for child in tree.children:
stack.append(child)
return self.index
if __name__ == '__main__':
import pprint
t1 = Tree('a', data={'hello': 'goodbye'}, children=[
Tree('b', edge_data={'foo': 'bar'}, edge_props={'classes': 'directed'}),
Tree('c', props={'selected': True})
])
print("Nodes:")
pprint.pprint(t1.get_nodes())
print('\nEdges:')
pprint.pprint(t1.get_edges())