Merge branch 'master' into 2596-refactor-pipeline-create
[arvados.git] / apps / workbench / app / controllers / collections_controller.rb
1 class CollectionsController < ApplicationController
2   skip_before_filter :find_object_by_uuid, :only => [:provenance]
3   skip_before_filter :check_user_agreements, :only => [:show_file]
4
5   def show_pane_list
6     %w(Files Attributes Metadata Provenance_graph Used_by JSON API)
7   end
8
9   def index
10     if params[:search].andand.length.andand > 0
11       tags = Link.where(any: ['contains', params[:search]])
12       @collections = (Collection.where(uuid: tags.collect(&:head_uuid)) |
13                       Collection.where(any: ['contains', params[:search]])).
14         uniq { |c| c.uuid }
15     else
16       if params[:limit]
17         limit = params[:limit].to_i
18       else
19         limit = 100
20       end
21
22       if params[:offset]
23         offset = params[:offset].to_i
24       else
25         offset = 0
26       end
27
28       @collections = Collection.limit(limit).offset(offset)
29     end
30     @links = Link.limit(1000).
31       where(head_uuid: @collections.collect(&:uuid))
32     @collection_info = {}
33     @collections.each do |c|
34       @collection_info[c.uuid] = {
35         tag_links: [],
36         wanted: false,
37         wanted_by_me: false,
38         provenance: [],
39         links: []
40       }
41     end
42     @links.each do |link|
43       @collection_info[link.head_uuid] ||= {}
44       info = @collection_info[link.head_uuid]
45       case link.link_class
46       when 'tag'
47         info[:tag_links] << link
48       when 'resources'
49         info[:wanted] = true
50         info[:wanted_by_me] ||= link.tail_uuid == current_user.uuid
51       when 'provenance'
52         info[:provenance] << link.name
53       end
54       info[:links] << link
55     end
56     @request_url = request.url
57   end
58
59   def show_file
60     opts = params.merge(arvados_api_token: Thread.current[:arvados_api_token])
61     if r = params[:file].match(/(\.\w+)/)
62       ext = r[1]
63     end
64     self.response.headers['Content-Type'] =
65       Rack::Mime::MIME_TYPES[ext] || 'application/octet-stream'
66     self.response.headers['Content-Length'] = params[:size] if params[:size]
67     self.response.headers['Content-Disposition'] = params[:disposition] if params[:disposition]
68     self.response_body = FileStreamer.new opts
69   end
70
71
72   def show
73     return super if !@object
74     @provenance = []
75     @output2job = {}
76     @output2colorindex = {}
77     @sourcedata = {params[:uuid] => {uuid: params[:uuid]}}
78     @protected = {}
79
80     colorindex = -1
81     any_hope_left = true
82     while any_hope_left
83       any_hope_left = false
84       Job.where(output: @sourcedata.keys).sort_by { |a| a.finished_at || a.created_at }.reverse.each do |job|
85         if !@output2colorindex[job.output]
86           any_hope_left = true
87           @output2colorindex[job.output] = (colorindex += 1) % 10
88           @provenance << {job: job, output: job.output}
89           @sourcedata.delete job.output
90           @output2job[job.output] = job
91           job.dependencies.each do |new_source_data|
92             unless @output2colorindex[new_source_data]
93               @sourcedata[new_source_data] = {uuid: new_source_data}
94             end
95           end
96         end
97       end
98     end
99
100     Link.where(head_uuid: @sourcedata.keys | @output2job.keys).each do |link|
101       if link.link_class == 'resources' and link.name == 'wants'
102         @protected[link.head_uuid] = true
103       end
104     end
105     Link.where(tail_uuid: @sourcedata.keys).each do |link|
106       if link.link_class == 'data_origin'
107         @sourcedata[link.tail_uuid][:data_origins] ||= []
108         @sourcedata[link.tail_uuid][:data_origins] << [link.name, link.head_uuid]
109       end
110     end
111     Collection.where(uuid: @sourcedata.keys).each do |collection|
112       if @sourcedata[collection.uuid]
113         @sourcedata[collection.uuid][:collection] = collection
114       end
115     end
116
117     Collection.where(uuid: @object.uuid).each do |u|
118       puts request
119       @prov_svg = ProvenanceHelper::create_provenance_graph(u.provenance, "provenance_svg",
120                                                             {:request => request,
121                                                               :direction => :bottom_up,
122                                                               :combine_jobs => :script_only}) rescue nil
123       @used_by_svg = ProvenanceHelper::create_provenance_graph(u.used_by, "used_by_svg",
124                                                                {:request => request,
125                                                                  :direction => :top_down,
126                                                                  :combine_jobs => :script_only,
127                                                                  :pdata_only => true}) rescue nil
128     end
129   end
130
131   protected
132   class FileStreamer
133     def initialize(opts={})
134       @opts = opts
135     end
136     def each
137       return unless @opts[:uuid] && @opts[:file]
138       env = Hash[ENV].
139         merge({
140                 'ARVADOS_API_HOST' =>
141                 $arvados_api_client.arvados_v1_base.
142                 sub(/\/arvados\/v1/, '').
143                 sub(/^https?:\/\//, ''),
144                 'ARVADOS_API_TOKEN' =>
145                 @opts[:arvados_api_token],
146                 'ARVADOS_API_HOST_INSECURE' =>
147                 Rails.configuration.arvados_insecure_https ? 'true' : 'false'
148               })
149       IO.popen([env, 'arv-get', "#{@opts[:uuid]}/#{@opts[:file]}"],
150                'rb') do |io|
151         while buf = io.read(2**20)
152           yield buf
153         end
154       end
155       Rails.logger.warn("#{@opts[:uuid]}/#{@opts[:file]}: #{$?}") if $? != 0
156     end
157   end
158 end