1 class CollectionsController < ApplicationController
2 skip_before_filter :find_object_by_uuid, :only => [:provenance]
3 skip_before_filter :check_user_agreements, :only => [:show_file]
6 if params[:search].andand.length.andand > 0
7 tags = Link.where(any: ['contains', params[:search]])
8 @collections = (Collection.where(uuid: tags.collect(&:head_uuid)) |
9 Collection.where(any: ['contains', params[:search]])).
12 @collections = Collection.limit(100)
14 @links = Link.limit(1000).
15 where(head_uuid: @collections.collect(&:uuid))
17 @collections.each do |c|
18 @collection_info[c.uuid] = {
27 @collection_info[link.head_uuid] ||= {}
28 info = @collection_info[link.head_uuid]
31 info[:tags] << link.name
34 info[:wanted_by_me] ||= link.tail_uuid == current_user.uuid
36 info[:provenance] << link.name
40 @request_url = request.url
44 opts = params.merge(arvados_api_token: Thread.current[:arvados_api_token])
45 if r = params[:file].match(/(\.\w+)/)
48 self.response.headers['Content-Type'] =
49 Rack::Mime::MIME_TYPES[ext] || 'application/octet-stream'
50 self.response.headers['Content-Length'] = params[:size] if params[:size]
51 self.response.headers['Content-Disposition'] = params[:disposition] if params[:disposition]
52 self.response_body = FileStreamer.new opts
55 def describe_node(uuid)
56 rsc = ArvadosBase::resource_class_for_uuid uuid
58 "\"#{uuid}\" [label=\"#{rsc}\\n#{uuid}\",href=\"#{url_for rsc}/#{uuid}\"];"
64 def describe_script(job)
65 #"""\"#{job.script_version}\" [label=\"#{job.script}: #{job.script_version}\"];
66 # \"#{job.uuid}\" -> \"#{job.script_version}\" [label=\"script\"];"""
67 "\"#{job.uuid}\" [label=\"#{job.script}\\n#{job.script_version}\"];"
71 "#{job.script}\\n#{job.script_version}"
74 def collection_uuid(uuid)
75 m = /([a-f0-9]{32}(\+[0-9]+)?)(\+.*)?/.match(uuid)
83 def script_param_edges(visited, job, prefix, sp)
85 if sp and not sp.empty?
90 k = prefix + "::" + k.to_s
92 gr += script_param_edges(visited, job, k.to_s, v)
96 gr += script_param_edges(visited, job, prefix, v)
99 m = collection_uuid(sp)
101 gr += "\"#{job_uuid(job)}\" -> \"#{m}\" [label=\" #{prefix}\"];"
102 gr += generate_provenance_edges(visited, m)
109 def generate_provenance_edges(visited, uuid)
111 m = collection_uuid(uuid)
113 if not uuid or uuid.empty? or visited[uuid] or visited[m]
117 #puts "visiting #{uuid}"
120 # uuid is a collection
124 gr += describe_node(uuid)
126 Job.where(output: uuid).each do |job|
127 #gr += describe_node(job_uuid(job))
128 gr += "\"#{uuid}\" -> \"#{job_uuid(job)}\" [label=\" output\"];"
129 gr += generate_provenance_edges(visited, job.uuid)
132 Job.where(log: uuid).each do |job|
133 #gr += describe_node(job_uuid(job))
134 gr += "\"#{uuid}\" -> \"#{job_uuid(job)}\" [label=\" log\"];"
135 gr += generate_provenance_edges(visited, job.uuid)
141 # uuid is something else
142 rsc = ArvadosBase::resource_class_for_uuid uuid
145 Job.where(uuid: uuid).each do |job|
146 gr += script_param_edges(visited, job, "", job.script_parameters)
147 #gr += describe_script(job)
150 gr += describe_node(uuid)
154 Link.where(head_uuid: uuid, link_class: "provenance").each do |link|
155 gr += describe_node(link.tail_uuid)
156 gr += "\"#{link.head_uuid}\" -> \"#{link.tail_uuid}\" [label=\" #{link.name}\", href=\"/links/#{link.uuid}\"];"
157 gr += generate_provenance_edges(visited, link.tail_uuid)
160 #puts "finished #{uuid}"
165 def create_provenance_graph(uuid)
168 gr = """strict digraph {
170 node [fontsize=8,shape=box];
171 edge [dir=back,fontsize=8];"""
174 gr += generate_provenance_edges(visited, uuid)
179 Open3.popen2("dot", "-Tsvg") do |stdin, stdout, wait_thr|
187 svg = svg.sub(/<\?xml.*?\?>/m, "")
188 svg = svg.sub(/<!DOCTYPE.*?>/m, "")
192 return super if !@object
195 @output2colorindex = {}
196 @sourcedata = {params[:uuid] => {uuid: params[:uuid]}}
202 any_hope_left = false
203 Job.where(output: @sourcedata.keys).sort_by { |a| a.finished_at || a.created_at }.reverse.each do |job|
204 if !@output2colorindex[job.output]
206 @output2colorindex[job.output] = (colorindex += 1) % 10
207 @provenance << {job: job, output: job.output}
208 @sourcedata.delete job.output
209 @output2job[job.output] = job
210 job.dependencies.each do |new_source_data|
211 unless @output2colorindex[new_source_data]
212 @sourcedata[new_source_data] = {uuid: new_source_data}
219 Link.where(head_uuid: @sourcedata.keys | @output2job.keys).each do |link|
220 if link.link_class == 'resources' and link.name == 'wants'
221 @protected[link.head_uuid] = true
224 Link.where(tail_uuid: @sourcedata.keys).each do |link|
225 if link.link_class == 'data_origin'
226 @sourcedata[link.tail_uuid][:data_origins] ||= []
227 @sourcedata[link.tail_uuid][:data_origins] << [link.name, link.head_kind, link.head_uuid]
230 Collection.where(uuid: @sourcedata.keys).each do |collection|
231 if @sourcedata[collection.uuid]
232 @sourcedata[collection.uuid][:collection] = collection
236 @prov_svg = create_provenance_graph(@object.uuid)
241 def initialize(opts={})
245 return unless @opts[:uuid] && @opts[:file]
248 'ARVADOS_API_HOST' =>
249 $arvados_api_client.arvados_v1_base.
250 sub(/\/arvados\/v1/, '').
251 sub(/^https?:\/\//, ''),
252 'ARVADOS_API_TOKEN' =>
253 @opts[:arvados_api_token],
254 'ARVADOS_API_HOST_INSECURE' =>
255 Rails.configuration.arvados_insecure_https ? 'true' : 'false'
257 IO.popen([env, 'arv-get', "#{@opts[:uuid]}/#{@opts[:file]}"],
259 while buf = io.read(2**20)
263 Rails.logger.warn("#{@opts[:uuid]}/#{@opts[:file]}: #{$?}") if $? != 0