1 module ProvenanceHelper
2 def self.describe_node(pdata, uuid)
3 rsc = ArvadosBase::resource_class_for_uuid uuid.to_s
5 href = "/#{rsc.to_s.underscore.pluralize rsc}/#{uuid}"
7 #"\"#{uuid}\" [label=\"#{rsc}\\n#{uuid}\",href=\"#{href}\"];\n"
12 return "\"#{uuid}\" [label=\"#{pdata[uuid][:name]}\",href=\"#{href}\",shape=oval];\n"
15 if pdata[uuid].respond_to? :files
16 files = pdata[uuid].files
17 elsif pdata[uuid][:files]
18 files = pdata[uuid][:files]
24 while i < 3 and i < files.length
25 label += "\\n" unless label == ""
30 label += "\\n⋮"
32 return "\"#{uuid}\" [label=\"#{label}\",href=\"#{href}\",shape=oval];\n"
36 return "\"#{uuid}\" [label=\"#{rsc}\",href=\"#{href}\"];\n"
42 def self.job_uuid(job)
43 # "#{job[:script]}\\n#{job[:script_version]}"
47 def self.collection_uuid(uuid)
48 m = /([a-f0-9]{32}(\+[0-9]+)?)(\+.*)?/.match(uuid.to_s)
56 def self.edge(tail, head, extra, opts)
57 if opts[:direction] == :bottom_up
58 gr = "\"#{tail}\" -> \"#{head}\""
60 gr = "\"#{head}\" -> \"#{tail}\""
65 gr += "#{k}=\"#{v}\","
73 def self.script_param_edges(pdata, visited, job, prefix, sp, opts)
75 if sp and not sp.empty?
80 k = prefix + "::" + k.to_s
82 gr += ProvenanceHelper::script_param_edges(pdata, visited, job, k.to_s, v, opts)
89 gr += ProvenanceHelper::script_param_edges(pdata, visited, job, "#{prefix}[#{i}]", v, opts)
91 node += "', '" unless node == ""
92 node = "['" if node == ""
100 #id = "#{job[:uuid]}_#{prefix}"
101 gr += "\"#{node}\" [label=\"#{node}\"];\n"
102 gr += edge(job_uuid(job), node, {:label => prefix}, opts)
105 m = collection_uuid(sp)
107 gr += edge(job_uuid(job), m, {:label => prefix}, opts)
108 gr += ProvenanceHelper::generate_provenance_edges(pdata, visited, m, opts)
109 elsif opts[:all_script_parameters]
110 #id = "#{job[:uuid]}_#{prefix}"
111 gr += "\"#{sp}\" [label=\"#{sp}\"];\n"
112 gr += edge(job_uuid(job), sp, {:label => prefix}, opts)
119 def self.generate_provenance_edges(pdata, visited, uuid, opts)
121 m = ProvenanceHelper::collection_uuid(uuid)
124 uuid = uuid.intern if uuid
126 if (not uuid) or uuid.empty? or visited[uuid]
128 #puts "already visited #{uuid}"
132 if not pdata[uuid] then
133 return ProvenanceHelper::describe_node(pdata, uuid)
138 #puts "visiting #{uuid}"
141 # uuid is a collection
142 gr += ProvenanceHelper::describe_node(pdata, uuid)
144 pdata.each do |k, job|
145 if job[:output] == uuid.to_s
146 gr += self.edge(uuid, job_uuid(job), {:label => "output"}, opts)
147 gr += ProvenanceHelper::generate_provenance_edges(pdata, visited, job[:uuid], opts)
149 if job[:log] == uuid.to_s
150 gr += edge(uuid, job_uuid(job), {:label => "log"}, opts)
151 gr += ProvenanceHelper::generate_provenance_edges(pdata, visited, job[:uuid], opts)
155 # uuid is something else
156 rsc = ArvadosBase::resource_class_for_uuid uuid.to_s
161 gr += ProvenanceHelper::script_param_edges(pdata, visited, job, "", job[:script_parameters], opts)
164 gr += ProvenanceHelper::describe_node(pdata, uuid)
168 pdata.each do |k, link|
169 if link[:head_uuid] == uuid.to_s and link[:link_class] == "provenance"
170 gr += ProvenanceHelper::describe_node(pdata, link[:tail_uuid])
171 gr += edge(link[:head_uuid], link[:tail_uuid], {:label => link[:name], :href => "/links/#{link[:uuid]}"}, opts)
172 gr += ProvenanceHelper::generate_provenance_edges(pdata, visited, link[:tail_uuid], opts)
176 #puts "finished #{uuid}"
181 def self.create_provenance_graph(pdata, uuid, opts={})
184 gr = """strict digraph {
185 node [fontsize=8,shape=box];
186 edge [fontsize=8];"""
188 if opts[:direction] == :bottom_up
189 gr += "edge [dir=back];"
192 #puts "pdata is #{pdata}"
195 if uuid.respond_to? :each
197 gr += ProvenanceHelper::generate_provenance_edges(pdata, visited, u, opts)
200 gr += ProvenanceHelper::generate_provenance_edges(pdata, visited, uuid, opts)
208 Open3.popen2("dot", "-Tsvg") do |stdin, stdout, wait_thr|
216 svg = svg.sub(/<\?xml.*?\?>/m, "")
217 svg = svg.sub(/<!DOCTYPE.*?>/m, "")