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 self.describe_node(uuid)
57 rsc = ArvadosBase::resource_class_for_uuid uuid
59 "\"#{uuid}\" [label=\"#{rsc}\\n#{uuid}\",href=\"/#{rsc.to_s.underscore.pluralize rsc}/#{uuid}\"];"
65 def self.job_uuid(job)
66 # "#{job[:script]}\\n#{job[:script_version]}"
70 def self.collection_uuid(uuid)
71 m = /([a-f0-9]{32}(\+[0-9]+)?)(\+.*)?/.match(uuid.to_s)
79 def self.script_param_edges(visited, job, prefix, sp, opts)
81 if sp and not sp.empty?
86 k = prefix + "::" + k.to_s
88 gr += CollectionsController::script_param_edges(visited, job, k.to_s, v, opts)
92 gr += CollectionsController::script_param_edges(visited, job, prefix, v, opts)
95 m = collection_uuid(sp)
97 gr += "\"#{job_uuid(job)}\" -> \"#{m}\" [label=\" #{prefix}\"];"
98 gr += CollectionsController::generate_provenance_edges(visited, m, opts)
105 def self.generate_provenance_edges(pdata, uuid, opts)
107 m = CollectionsController::collection_uuid(uuid)
110 uuid = uuid.intern if uuid
112 if (not uuid) or uuid.empty? \
113 or (pdata[uuid] and pdata[uuid][:_visited])
115 #puts "already visited #{uuid}"
119 if not pdata[uuid] then
120 return CollectionsController::describe_node(uuid)
122 pdata[uuid][:_visited] = true
125 #puts "visiting #{uuid}"
128 # uuid is a collection
129 gr += CollectionsController::describe_node(uuid)
131 pdata.each do |k, job|
132 if job[:output] == uuid.to_s
133 gr += "\"#{uuid}\" -> \"#{job_uuid(job)}\" [label=\"output\"];"
134 gr += CollectionsController::generate_provenance_edges(pdata, job[:uuid])
136 if job[:log] == uuid.to_s
137 gr += "\"#{uuid}\" -> \"#{job_uuid(job)}\" [label=\"log\"];"
138 gr += CollectionsController::generate_provenance_edges(pdata, job[:uuid])
142 # uuid is something else
143 rsc = ArvadosBase::resource_class_for_uuid uuid.to_s
148 gr += CollectionsController::script_param_edges(pdata, job, "", job[:script_parameters], opts)
151 gr += CollectionsController::describe_node(uuid)
155 pdata.each do |k, link|
156 if link[:head_uuid] == uuid.to_s and link[:link_class] == "provenance"
157 gr += CollectionsController::describe_node(link[:tail_uuid])
158 gr += "\"#{link[:head_uuid]}\" -> \"#{link[:tail_uuid]}\" [label=\" #{link[:name]}\", href=\"/links/#{link[:uuid]}\"];"
159 gr += CollectionsController::generate_provenance_edges(pdata, link[:tail_uuid], opts)
163 #puts "finished #{uuid}"
168 def self.create_provenance_graph(pdata, uuid, opts={})
171 gr = """strict digraph {
172 node [fontsize=8,shape=box];
173 edge [dir=back,fontsize=8];"""
175 #puts "pdata is #{pdata}"
177 gr += CollectionsController::generate_provenance_edges(pdata, uuid, opts)
182 Open3.popen2("dot", "-Tsvg") do |stdin, stdout, wait_thr|
190 svg = svg.sub(/<\?xml.*?\?>/m, "")
191 svg = svg.sub(/<!DOCTYPE.*?>/m, "")
195 return super if !@object
198 @output2colorindex = {}
199 @sourcedata = {params[:uuid] => {uuid: params[:uuid]}}
205 any_hope_left = false
206 Job.where(output: @sourcedata.keys).sort_by { |a| a.finished_at || a.created_at }.reverse.each do |job|
207 if !@output2colorindex[job.output]
209 @output2colorindex[job.output] = (colorindex += 1) % 10
210 @provenance << {job: job, output: job.output}
211 @sourcedata.delete job.output
212 @output2job[job.output] = job
213 job.dependencies.each do |new_source_data|
214 unless @output2colorindex[new_source_data]
215 @sourcedata[new_source_data] = {uuid: new_source_data}
222 Link.where(head_uuid: @sourcedata.keys | @output2job.keys).each do |link|
223 if link.link_class == 'resources' and link.name == 'wants'
224 @protected[link.head_uuid] = true
227 Link.where(tail_uuid: @sourcedata.keys).each do |link|
228 if link.link_class == 'data_origin'
229 @sourcedata[link.tail_uuid][:data_origins] ||= []
230 @sourcedata[link.tail_uuid][:data_origins] << [link.name, link.head_kind, link.head_uuid]
233 Collection.where(uuid: @sourcedata.keys).each do |collection|
234 if @sourcedata[collection.uuid]
235 @sourcedata[collection.uuid][:collection] = collection
239 Collection.where(uuid: @object.uuid).each do |u|
240 @prov_svg = CollectionsController::create_provenance_graph u.provenance, u.uuid
246 def initialize(opts={})
250 return unless @opts[:uuid] && @opts[:file]
253 'ARVADOS_API_HOST' =>
254 $arvados_api_client.arvados_v1_base.
255 sub(/\/arvados\/v1/, '').
256 sub(/^https?:\/\//, ''),
257 'ARVADOS_API_TOKEN' =>
258 @opts[:arvados_api_token],
259 'ARVADOS_API_HOST_INSECURE' =>
260 Rails.configuration.arvados_insecure_https ? 'true' : 'false'
262 IO.popen([env, 'arv-get', "#{@opts[:uuid]}/#{@opts[:file]}"],
264 while buf = io.read(2**20)
268 Rails.logger.warn("#{@opts[:uuid]}/#{@opts[:file]}: #{$?}") if $? != 0