comparison env/lib/python3.9/site-packages/cwltool/cwlviewer.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 """Visualize a CWL workflow."""
2 import os
3 from urllib.parse import urlparse
4
5 import pydot # type: ignore
6 import rdflib
7
8
9 class CWLViewer:
10 """Produce similar images with the https://github.com/common-workflow-language/cwlviewer."""
11
12 _queries_dir = os.path.join(
13 os.path.abspath(os.path.dirname(__file__)), "rdfqueries"
14 )
15 _get_inner_edges_query_path = os.path.join(_queries_dir, "get_inner_edges.sparql")
16 _get_input_edges_query_path = os.path.join(_queries_dir, "get_input_edges.sparql")
17 _get_output_edges_query_path = os.path.join(_queries_dir, "get_output_edges.sparql")
18 _get_root_query_path = os.path.join(_queries_dir, "get_root.sparql")
19
20 def __init__(
21 self, rdf_description # type: str
22 ):
23 """Create a viewer object based on the rdf description of the workflow."""
24 self._dot_graph = CWLViewer._init_dot_graph() # type: pydot.Graph
25 self._rdf_graph = self._load_cwl_graph(
26 rdf_description
27 ) # type: rdflib.graph.Graph
28 self._root_graph_uri = self._get_root_graph_uri() # type: str
29 self._set_inner_edges()
30 self._set_input_edges()
31 self._set_output_edges()
32
33 def _load_cwl_graph(
34 self, rdf_description # type: str
35 ) -> rdflib.graph.Graph:
36 rdf_graph = rdflib.Graph()
37 rdf_graph.parse(data=rdf_description, format="n3")
38 return rdf_graph
39
40 def _set_inner_edges(self) -> None:
41 with open(self._get_inner_edges_query_path) as f:
42 get_inner_edges_query = f.read()
43 inner_edges = self._rdf_graph.query(
44 get_inner_edges_query, initBindings={"root_graph": self._root_graph_uri}
45 )
46 for inner_edge_row in inner_edges:
47 source_label = (
48 inner_edge_row["source_label"]
49 if inner_edge_row["source_label"] is not None
50 else urlparse(inner_edge_row["source_step"]).fragment
51 )
52 n = pydot.Node(
53 "",
54 fillcolor="lightgoldenrodyellow",
55 style="filled",
56 label=source_label,
57 shape="record",
58 )
59 n.set_name(str(inner_edge_row["source_step"]))
60 self._dot_graph.add_node(n)
61 target_label = (
62 inner_edge_row["target_label"]
63 if inner_edge_row["target_label"] is not None
64 else urlparse(inner_edge_row["target_step"]).fragment
65 )
66 n = pydot.Node(
67 "",
68 fillcolor="lightgoldenrodyellow",
69 style="filled",
70 label=target_label,
71 shape="record",
72 )
73 n.set_name(str(inner_edge_row["target_step"]))
74 self._dot_graph.add_node(n)
75 self._dot_graph.add_edge(
76 pydot.Edge(
77 str(inner_edge_row["source_step"]),
78 str(inner_edge_row["target_step"]),
79 )
80 )
81
82 def _set_input_edges(self) -> None:
83 with open(self._get_input_edges_query_path) as f:
84 get_input_edges_query = f.read()
85 inputs_subgraph = pydot.Subgraph(graph_name="cluster_inputs")
86 self._dot_graph.add_subgraph(inputs_subgraph)
87 inputs_subgraph.set_rank("same")
88 inputs_subgraph.create_attribute_methods(["style"])
89 inputs_subgraph.set_style("dashed")
90 inputs_subgraph.set_label("Workflow Inputs")
91
92 input_edges = self._rdf_graph.query(
93 get_input_edges_query, initBindings={"root_graph": self._root_graph_uri}
94 )
95 for input_row in input_edges:
96 n = pydot.Node(
97 "",
98 fillcolor="#94DDF4",
99 style="filled",
100 label=urlparse(input_row["input"]).fragment,
101 shape="record",
102 )
103 n.set_name(str(input_row["input"]))
104 inputs_subgraph.add_node(n)
105 self._dot_graph.add_edge(
106 pydot.Edge(str(input_row["input"]), str(input_row["step"]))
107 )
108
109 def _set_output_edges(self) -> None:
110 with open(self._get_output_edges_query_path) as f:
111 get_output_edges = f.read()
112 outputs_graph = pydot.Subgraph(graph_name="cluster_outputs")
113 self._dot_graph.add_subgraph(outputs_graph)
114 outputs_graph.set_rank("same")
115 outputs_graph.create_attribute_methods(["style"])
116 outputs_graph.set_style("dashed")
117 outputs_graph.set_label("Workflow Outputs")
118 outputs_graph.set_labelloc("b")
119 output_edges = self._rdf_graph.query(
120 get_output_edges, initBindings={"root_graph": self._root_graph_uri}
121 )
122 for output_edge_row in output_edges:
123 n = pydot.Node(
124 "",
125 fillcolor="#94DDF4",
126 style="filled",
127 label=urlparse(output_edge_row["output"]).fragment,
128 shape="record",
129 )
130 n.set_name(str(output_edge_row["output"]))
131 outputs_graph.add_node(n)
132 self._dot_graph.add_edge(
133 pydot.Edge(output_edge_row["step"], output_edge_row["output"])
134 )
135
136 def _get_root_graph_uri(self) -> rdflib.URIRef:
137 with open(self._get_root_query_path) as f:
138 get_root_query = f.read()
139 root = list(
140 self._rdf_graph.query(
141 get_root_query,
142 )
143 )
144 if len(root) != 1:
145 raise RuntimeError(
146 "Cannot identify root workflow! Notice that only Workflows can be visualized"
147 )
148
149 workflow = root[0]["workflow"] # type: rdflib.URIRef
150 return workflow
151
152 @classmethod
153 def _init_dot_graph(cls) -> pydot.Graph:
154 graph = pydot.Graph(graph_type="digraph", simplify=False)
155 graph.set_bgcolor("#eeeeee")
156 graph.set_clusterrank("local")
157 graph.set_labelloc("bottom")
158 graph.set_labelloc("bottom")
159 graph.set_labeljust("right")
160
161 return graph
162
163 def get_dot_graph(self) -> pydot.Graph:
164 """Get the dot graph object."""
165 return self._dot_graph
166
167 def dot(self) -> str:
168 """Get the graph as graphviz."""
169 return str(self._dot_graph.to_string())