Merge branch 'origin/3443-read-collections-from-name-links' refs #3443
[arvados.git] / apps / workbench / app / controllers / actions_controller.rb
1 class ActionsController < ApplicationController
2
3   @@exposed_actions = {}
4   def self.expose_action method, &block
5     @@exposed_actions[method] = true
6     define_method method, block
7   end
8
9   def model_class
10     ArvadosBase::resource_class_for_uuid(params[:uuid])
11   end
12
13   def show
14     @object = model_class.andand.find(params[:uuid])
15     if @object.is_a? Link and
16         @object.link_class == 'name' and
17         ArvadosBase::resource_class_for_uuid(@object.head_uuid) == Collection
18       redirect_to collection_path(id: @object.uuid)
19     elsif @object
20       redirect_to @object
21     else
22       raise ActiveRecord::RecordNotFound
23     end
24   end
25
26   def post
27     params.keys.collect(&:to_sym).each do |param|
28       if @@exposed_actions[param]
29         return self.send(param)
30       end
31     end
32     redirect_to :back
33   end
34
35   expose_action :copy_selections_into_project do
36     move_or_copy :copy
37   end
38
39   expose_action :move_selections_into_project do
40     move_or_copy :move
41   end
42
43   def move_or_copy action
44     uuids_to_add = params["selection"]
45     uuids_to_add.
46       collect { |x| ArvadosBase::resource_class_for_uuid(x) }.
47       uniq.
48       each do |resource_class|
49       resource_class.filter([['uuid','in',uuids_to_add]]).each do |src|
50         if resource_class == Collection
51           dst = Link.new(owner_uuid: @object.uuid,
52                          tail_uuid: @object.uuid,
53                          head_uuid: src.uuid,
54                          link_class: 'name',
55                          name: src.uuid)
56         else
57           case action
58           when :copy
59             dst = src.dup
60             if dst.respond_to? :'name='
61               if dst.name
62                 dst.name = "Copy of #{dst.name}"
63               else
64                 dst.name = "Copy of unnamed #{dst.class_for_display.downcase}"
65               end
66             end
67           when :move
68             dst = src
69           else
70             raise ArgumentError.new "Unsupported action #{action}"
71           end
72           dst.owner_uuid = @object.uuid
73           dst.tail_uuid = @object.uuid if dst.class == Link
74         end
75         begin
76           dst.save!
77         rescue
78           dst.name += " (#{Time.now.localtime})" if dst.respond_to? :name=
79           dst.save!
80         end
81       end
82     end
83     redirect_to @object
84   end
85
86   def arv_normalize mt, *opts
87     r = ""
88     IO.popen(['arv-normalize'] + opts, 'w+b') do |io|
89       io.write mt
90       io.close_write
91       while buf = io.read(2**16)
92         r += buf
93       end
94     end
95     r
96   end
97
98   expose_action :combine_selected_files_into_collection do
99     lst = []
100     files = []
101     params["selection"].each do |s|
102       a = ArvadosBase::resource_class_for_uuid s
103       m = nil
104       if a == Link
105         begin
106           m = CollectionsHelper.match(Link.find(s).head_uuid)
107         rescue
108         end
109       else
110         m = CollectionsHelper.match(s)
111       end
112
113       if m and m[1] and m[2]
114         lst.append(m[1] + m[2])
115         files.append(m)
116       end
117     end
118
119     collections = Collection.where(uuid: lst)
120
121     chash = {}
122     collections.each do |c|
123       c.reload()
124       chash[c.uuid] = c
125     end
126
127     combined = ""
128     files.each do |m|
129       mt = chash[m[1]+m[2]].manifest_text
130       if m[4]
131         combined += arv_normalize mt, '--extract', m[4][1..-1]
132       else
133         combined += chash[m[1]+m[2]].manifest_text
134       end
135     end
136
137     normalized = arv_normalize combined
138     normalized_stripped = arv_normalize combined, '--strip'
139
140     require 'digest/md5'
141
142     d = Digest::MD5.new()
143     d << normalized_stripped
144     newuuid = "#{d.hexdigest}+#{normalized_stripped.length}"
145
146     env = Hash[ENV].
147       merge({
148               'ARVADOS_API_HOST' =>
149               arvados_api_client.arvados_v1_base.
150               sub(/\/arvados\/v1/, '').
151               sub(/^https?:\/\//, ''),
152               'ARVADOS_API_TOKEN' => Thread.current[:arvados_api_token],
153               'ARVADOS_API_HOST_INSECURE' =>
154               Rails.configuration.arvados_insecure_https ? 'true' : 'false'
155             })
156
157     IO.popen([env, 'arv-put', '--raw'], 'w+b') do |io|
158       io.write normalized_stripped
159       io.close_write
160       while buf = io.read(2**16)
161       end
162     end
163
164     newc = Collection.new({:uuid => newuuid, :manifest_text => normalized})
165     newc.save!
166
167     chash.each do |k,v|
168       l = Link.new({
169                      tail_uuid: k,
170                      head_uuid: newuuid,
171                      link_class: "provenance",
172                      name: "provided"
173                    })
174       l.save!
175     end
176
177     redirect_to controller: 'collections', action: :show, id: newc.uuid
178   end
179
180 end