def single_layer(list_of_task_dicts: list, list_of_task_tuples: list, recursive_depth: int, parent: str, file_name: str) -> None: """ each SVG has nodes and hyperlinks; no subgraphs """ use_case = AGraph(directed=True) use_case.clear() use_case.graph_attr.update(compound="true") for task_tup in list_of_task_tuples: #print(task_tup) if task_has_children_in_list_of_task_dicts(task_tup, list_of_task_dicts): #print(" has child, so hyperlink") use_case.add_node( smush(task_tup), label=with_spaces(task_tup), color="blue", # to indicate hyperlink shape= "rectangle", # to distinguish from nodes that do not have children href="single_layer_" + str(recursive_depth + 1) + "_" + smush(task_tup) + ".svg") for task_dist in list_of_task_dicts: if task_tup in task_dist.keys(): #for task_tup in task_dist[task_tup]: single_layer(list_of_task_dicts, task_dist[task_tup], recursive_depth + 1, "single_layer_" + str(recursive_depth) + "_" + file_name, file_name=smush(task_tup)) else: #print(" no children, so create node") use_case.add_node(smush(task_tup), label=with_spaces(task_tup)) if recursive_depth > 0: use_case.add_node("zoom out", href=parent + ".svg", color="red", shape="triangle") for index, task_tup in enumerate(list_of_task_tuples[1:]): use_case.add_edge(smush(list_of_task_tuples[index]), smush(task_tup)) use_case.write("single_layer_" + str(recursive_depth) + "_" + file_name + ".dot") use_case.draw("single_layer_" + str(recursive_depth) + "_" + file_name + ".svg", format="svg", prog="dot") return
def generate_single_svg(list_of_task_dicts: list, list_of_toplevel_steps: list, file_name: str) -> None: """ make a single graph with a bunch of subgraphs in contrast to the layered SVG approach, here each node name must be unique """ use_case = AGraph(directed=True) use_case.clear() use_case.graph_attr.update(compound="true") add_subgraph(list_of_task_dicts, use_case, list_of_toplevel_steps, parent="") use_case.write(file_name + ".dot") use_case.draw(file_name + ".svg", format="svg", prog="dot") return
def create_allinone(edge_list:list, list_of_dicts:list, parent_name: str) -> None: """ """ use_case = AGraph(directed=True) use_case.clear() use_case.graph_attr.update(compound="true") for edge_pair in edge_list: #print(with_spaces(edge_pair[0]),"to", with_spaces(edge_pair[1])) allinone(graph=use_case, edge_pair=edge_pair, list_of_dicts=list_of_dicts, parent_name=parent_name) use_case.write("allinone.dot") use_case.draw("allinone.svg", format="svg", prog="dot") return
class Graph(object): def __init__(self, graph_dict={}): self.__graph_dict = graph_dict self.__G = AGraph() def add_vertex(self, v): if (v not in self.__graph_dict): self.__graph_dict[v] = [] self.__G.add_node(v) def add_edge(self, u, v): if (u not in self.__graph_dict): self.__graph_dict[u] = [v] else: self.__graph_dict[u].append(v) self.__G.add_edge(u, v) def draw(self, fn): print(self.__graph_dict) self.__G.draw(fn, prog="circo") Image.open(fn).show() def clear(self): self.__graph_dict = {} self.__G.clear() def isConnected(self, path=None, start_vertex=None): if path == None: path = set() vertices = list( self.__graph_dict.keys()) # "list" necessary in Python 3 if start_vertex == None: start_vertex = vertices[0] path.add(start_vertex) if len(path) == len(vertices): return True else: for v in self.__graph_dict[start_vertex]: if v not in path: if self.isConnected(path, v): return True return False
def linked_layers(edge_list:list,list_of_dicts:list,parent_name:str) -> None: """ """ use_case = AGraph(directed=True) use_case.clear() use_case.graph_attr.update(compound="true") # use_case.add_node("up", # label="up", # shape="rectangle", # color="blue", # href=parent_name+".svg") for edge_pair in edge_list: if task_has_children_in_list_of_dicts(edge_pair[0], list_of_dicts): sg = use_case.subgraph(name="cluster_"+parent_name+smush(edge_pair[0]), label=with_spaces(edge_pair[0])) sg.add_node(parent_name+smush(edge_pair[0]), style="invis") for sg_edge_pair in get_subgraph(edge_pair[0],list_of_dicts): if task_has_children_in_list_of_dicts(sg_edge_pair[0],list_of_dicts): sg.add_node(parent_name+smush(edge_pair[0])+smush(sg_edge_pair[0]), label=with_spaces(sg_edge_pair[0]), shape="rectangle", color="blue", # name length is limited to 10 characters to # avoid file names that exceed OS limits. href=parent_name+"_"+smush(edge_pair[0])[:10]+".svg") if not path.exists(parent_name+"_"+smush(edge_pair[0])[:10]+".svg"): linked_layers(get_subgraph(edge_pair[0],list_of_dicts), list_of_dicts, parent_name+"_"+smush(edge_pair[0])[:10]) else: sg.add_node(parent_name+smush(edge_pair[0])+smush(sg_edge_pair[0]), label=with_spaces(sg_edge_pair[0])) if sg_edge_pair[1] is not None: if task_has_children_in_list_of_dicts(sg_edge_pair[1],list_of_dicts): sg.add_node(parent_name+smush(edge_pair[0])+smush(sg_edge_pair[1]), label=with_spaces(sg_edge_pair[1]), shape="rectangle", color="blue", href=parent_name+"_"+smush(edge_pair[0])[:10]+".svg") if not path.exists(parent_name+"_"+smush(edge_pair[0])[:10]+".svg"): linked_layers(get_subgraph(edge_pair[0],list_of_dicts), list_of_dicts, parent_name+"_"+smush(edge_pair[0])[:10]) else: sg.add_node(parent_name+smush(edge_pair[0])+smush(sg_edge_pair[1]), label=with_spaces(sg_edge_pair[1])) sg.add_edge(parent_name+smush(edge_pair[0])+smush(sg_edge_pair[0]), parent_name+smush(edge_pair[0])+smush(sg_edge_pair[1]))#,constraint=False) else: # node does not have children use_case.add_node(parent_name+smush(edge_pair[0]), label=with_spaces(edge_pair[0])) if edge_pair[1] is not None: if task_has_children_in_list_of_dicts(edge_pair[1], list_of_dicts): sg = use_case.subgraph(name="cluster_"+parent_name+smush(edge_pair[1]), label=with_spaces(edge_pair[1])) sg.add_node(parent_name+smush(edge_pair[1]), style="invis") for sg_edge_pair in get_subgraph(edge_pair[1],list_of_dicts): if task_has_children_in_list_of_dicts(sg_edge_pair[0], list_of_dicts): sg.add_node(parent_name+smush(edge_pair[1])+smush(sg_edge_pair[0]), label=with_spaces(sg_edge_pair[0]), shape="rectangle", color="blue", href=parent_name+"_"+smush(edge_pair[1])[:10]+".svg") if not path.exists(parent_name+"_"+smush(edge_pair[1])[:10]+".svg"): linked_layers(get_subgraph(edge_pair[1],list_of_dicts), list_of_dicts, parent_name+"_"+smush(edge_pair[1])[:10]) else: sg.add_node(parent_name+smush(edge_pair[1])+smush(sg_edge_pair[0]), label=with_spaces(sg_edge_pair[0])) if sg_edge_pair[1] is not None: if task_has_children_in_list_of_dicts(sg_edge_pair[1], list_of_dicts): sg.add_node(parent_name+smush(edge_pair[1])+smush(sg_edge_pair[1]), label=with_spaces(sg_edge_pair[1]), shape="rectangle", color="blue", href=parent_name+"_"+smush(edge_pair[1])[:10]+".svg") if not path.exists(parent_name+"_"+smush(edge_pair[1])[:10]+".svg"): linked_layers(get_subgraph(edge_pair[1],list_of_dicts), list_of_dicts, parent_name+"_"+smush(edge_pair[1])[:10]) else: sg.add_node(parent_name+smush(edge_pair[1])+smush(sg_edge_pair[1]), label=with_spaces(sg_edge_pair[1])) sg.add_edge(parent_name+smush(edge_pair[1])+smush(sg_edge_pair[0]), parent_name+smush(edge_pair[1])+smush(sg_edge_pair[1]))#,constraint=False) else: # node does not have children use_case.add_node(parent_name+smush(edge_pair[1]), label=with_spaces(edge_pair[1])) use_case.add_edge(parent_name+smush(edge_pair[0]), parent_name+smush(edge_pair[1])) print("parent name =",parent_name) use_case.write(parent_name+".dot") use_case.draw(parent_name+".svg", format="svg", prog="dot") return
#!/usr/bin/env python3 # Useful introduction to Graphviz: # https://www.worthe-it.co.za/blog/2017-09-19-quick-introduction-to-graphviz.html # need "pip install pygraphviz" # which requires a local installation of graphviz from pygraphviz import AGraph # https://pygraphviz.github.io/documentation/latest/reference/agraph.html # import generate_graphviz as gg if __name__ == "__main__": use_case = AGraph(directed=True, comment="an example") #, ratio="0.5")#, compound=True) use_case.clear() width = 40 use_case.add_node("TOF repair with PR", shape="rectangle") use_case.add_node("lv_or_rv", label="severly decreased LV or RV systolic function") use_case.add_edge("TOF repair with PR", "lv_or_rv") use_case.add_node( "achd_class_1", label= "evaluation by an ACHD cardiologist and \nadvanced HF team (class I)", shape="rectangle", constraint=False) use_case.add_edge("lv_or_rv", "achd_class_1", label="yes", color="green") use_case.add_edge("achd_class_1", "symptoms?", style="invis") use_case.add_edge("lv_or_rv", "PR severity", label="no", color="red")
def this_layer_plus_one(list_of_task_dicts: list, list_of_subtasks: list, output_filename: str, recursive_depth: int, parent: str) -> None: """ recursively create hyperlinked SVGs using GraphViz input data structure is a list of dicts """ # need the filename prefix for both this file and the child files fnamel = "layer_plus_one_" + str(recursive_depth) + "_" fnamel1 = "layer_plus_one_" + str(recursive_depth + 1) + "_" # initialize a new graph for this layer use_case = AGraph(directed=True, comment=output_filename) #, compound=True) use_case.clear() use_case.graph_attr.update(compound="true") for task in list_of_subtasks: #print(task) for this_dict in list_of_task_dicts: if list(this_dict.keys())[0] == task: #unique_subgraph_name = task_without_spaces#+"_"+str(random.randint(1000,9999)) if (task_has_children_in_list_of_task_dicts( task, list_of_task_dicts)): sg = use_case.subgraph(name="cluster_" + smush(task), label=with_spaces(task), href=fnamel1 + smush(task) + ".svg") else: # no href to SVG because there are no child nodes sg = use_case.subgraph(name="cluster_" + smush(task), label=with_spaces(task)) sg.add_node(smush(task), style="invis") #print(task) #print(list(this_dict.keys())[0]) subitem_list = list(this_dict.values())[0] # print(subitem_list) if len(subitem_list) < 2: # no edges to connect if (task_has_children_in_list_of_task_dicts( subitem_list[0], list_of_task_dicts)): sg.add_node(smush(task) + smush(subitem_list[0]), label=with_spaces(subitem_list[0]), href=fnamel1 + smush(task) + ".svg", color="blue", shape="rectangle") else: sg.add_node(smush(task) + smush(subitem_list[0]), label=with_spaces(subitem_list[0]), shape="rectangle") sg.add_edge(smush(task) + smush(subitem_list[0]), smush(task), style="invis") else: for index, subitem in enumerate(subitem_list[1:]): #print(' ',subitem) if (task_has_children_in_list_of_task_dicts( subitem_list[index], list_of_task_dicts)): sg.add_node(smush(task) + smush(subitem_list[index]), label=with_spaces(subitem_list[index]), href=fnamel1 + smush(task) + ".svg", color="blue", shape="rectangle") else: # no children to link to sg.add_node(smush(task) + smush(subitem_list[index]), label=with_spaces(subitem_list[index]), shape="rectangle") if (task_has_children_in_list_of_task_dicts( subitem, list_of_task_dicts)): sg.add_node(smush(task) + smush(subitem), label=with_spaces(subitem), href=fnamel1 + smush(task) + ".svg", color="blue", shape="rectangle") else: sg.add_node(smush(task) + smush(subitem), label=with_spaces(subitem), shape="rectangle") # every sequence of tasks is ordered, so link task with prior task sg.add_edge( smush(task) + smush(subitem_list[index]), smush(task) + smush(subitem)) if index == len( subitem_list ): # last item links to invisible node in order to force invisible node to bottom of subgraph sg.add_edge(smush(task) + smush(subitem_list[index]), smush(task), style="invis") for index, task_tup in enumerate(list_of_subtasks[1:]): # use_case.add_node(smush(list_of_subtasks[index]),label=with_spaces(task_tup),shape="rectangle") # use_case.add_node(smush(task_tup),label=with_spaces(task_tup),shape="rectangle") use_case.add_edge(smush(list_of_subtasks[index]), smush(task_tup), ltail="cluster_" + smush(list_of_subtasks[index]), lhead="cluster_" + smush(task_tup)) if recursive_depth > 0: use_case.add_node("zoom out", href=parent + ".svg", color="red", shape="triangle") #use_case.write() use_case.draw(fnamel + output_filename + ".svg", format="svg", prog="dot") #print("drew SVG for ", output_filename) for task_tuple in list_of_subtasks: for index, this_dict in enumerate(list_of_task_dicts): if task_tuple in this_dict.keys(): this_layer_plus_one(list_of_task_dicts, list_of_task_dicts[index][task_tuple], smush(task_tuple), recursive_depth + 1, fnamel + output_filename) return