Merge branch 'master' into 9372-container-display
[arvados.git] / apps / workbench / app / models / proxy_work_unit.rb
1 class ProxyWorkUnit < WorkUnit
2   require 'time'
3
4   attr_accessor :lbl
5   attr_accessor :proxied
6   attr_accessor :my_children
7   attr_accessor :unreadable_children
8
9   def initialize proxied, label
10     @lbl = label
11     @proxied = proxied
12   end
13
14   def label
15     @lbl
16   end
17
18   def uuid
19     get(:uuid)
20   end
21
22   def modified_by_user_uuid
23     get(:modified_by_user_uuid)
24   end
25
26   def created_at
27     t = get(:created_at)
28     t = Time.parse(t) if (t.is_a? String)
29     t
30   end
31
32   def started_at
33     t = get(:started_at)
34     t = Time.parse(t) if (t.is_a? String)
35     t
36   end
37
38   def modified_at
39     t = get(:modified_at)
40     t = Time.parse(t) if (t.is_a? String)
41     t
42   end
43
44   def finished_at
45     t = get(:finished_at)
46     t = Time.parse(t) if (t.is_a? String)
47     t
48   end
49
50   def state_label
51     state = get(:state)
52     if ["Running", "RunningOnServer", "RunningOnClient"].include? state
53       "Running"
54     else
55       state
56     end
57   end
58
59   def state_bootstrap_class
60     state = state_label
61     case state
62     when 'Complete'
63       'success'
64     when 'Failed', 'Cancelled'
65       'danger'
66     when 'Running', 'RunningOnServer', 'RunningOnClient'
67       'info'
68     else
69       'default'
70     end
71   end
72
73   def success?
74     state = state_label
75     if state == 'Complete'
76       true
77     elsif state == 'Failed' or state == 'Cancelled'
78       false
79     else
80       nil
81     end
82   end
83
84   def child_summary
85     done = 0
86     failed = 0
87     todo = 0
88     running = 0
89     children.each do |c|
90       case c.state_label
91       when 'Complete'
92         done = done+1
93       when 'Failed', 'Cancelled'
94         failed = failed+1
95       when 'Running'
96         running = running+1
97       else
98         todo = todo+1
99       end
100     end
101
102     summary = {}
103     summary[:done] = done
104     summary[:failed] = failed
105     summary[:todo] = todo
106     summary[:running] = running
107     summary
108   end
109
110   def child_summary_str
111     summary = child_summary
112     summary_txt = ''
113
114     if state_label == 'Running'
115       done = summary[:done] || 0
116       running = summary[:running] || 0
117       failed = summary[:failed] || 0
118       todo = summary[:todo] || 0
119       total = done + running + failed + todo
120
121       if total > 0
122         summary_txt += "#{summary[:done]} #{'child'.pluralize(summary[:done])} done,"
123         summary_txt += "#{summary[:failed]} failed,"
124         summary_txt += "#{summary[:running]} running,"
125         summary_txt += "#{summary[:todo]} pending"
126       end
127     end
128     summary_txt
129   end
130
131   def progress
132     state = state_label
133     if state == 'Complete'
134       return 1.0
135     elsif state == 'Failed' or state == 'Cancelled'
136       return 0.0
137     end
138
139     summary = child_summary
140     return 0.0 if summary.nil?
141
142     done = summary[:done] || 0
143     running = summary[:running] || 0
144     failed = summary[:failed] || 0
145     todo = summary[:todo] || 0
146     total = done + running + failed + todo
147     if total > 0
148       (done+failed).to_f / total
149     else
150       0.0
151     end
152   end
153
154   def children
155     []
156   end
157
158   def outputs
159     []
160   end
161
162   def title
163     "process"
164   end
165
166   def has_unreadable_children
167     @unreadable_children
168   end
169
170   def readable?
171     resource_class = ArvadosBase::resource_class_for_uuid(uuid)
172     resource_class.where(uuid: [uuid]).first rescue nil
173   end
174
175   def link_to_log
176     if state_label.in? ["Complete", "Failed", "Cancelled"]
177       lc = log_collection
178       if lc
179         logCollection = Collection.find? lc
180         if logCollection
181           ApplicationController.helpers.link_to("Log", "#{uri}#Log")
182         else
183           "Log unavailable"
184         end
185       end
186     elsif state_label == "Running"
187       if readable?
188         ApplicationController.helpers.link_to("Log", "#{uri}#Log")
189       else
190         "Log unavailable"
191       end
192     end
193   end
194
195   def walltime
196     if state_label != "Queued"
197       if started_at
198         ((if finished_at then finished_at else Time.now() end) - started_at)
199       end
200     end
201   end
202
203   def cputime
204     if state_label != "Queued"
205       if started_at
206         (runtime_constraints.andand[:min_nodes] || 1) * ((finished_at || Time.now()) - started_at)
207       end
208     end
209   end
210
211   def queuedtime
212     if state_label == "Queued"
213       Time.now - Time.parse(created_at.to_s)
214     end
215   end
216
217   def is_running?
218     state_label == 'Running'
219   end
220
221   def is_paused?
222     state_label == 'Paused'
223   end
224
225   def is_finished?
226     state_label.in? ["Complete", "Failed", "Cancelled"]
227   end
228
229   def is_failed?
230     state_label == 'Failed'
231   end
232
233   def show_runtime
234     runningtime = ApplicationController.helpers.determine_wallclock_runtime(if children.any? then children else [self] end)
235
236     walltime = 0
237     if started_at
238       walltime = if finished_at then (finished_at - started_at) else (Time.now - started_at) end
239     end
240
241     resp = '<p>'
242
243     if started_at
244       resp << "This #{title} started at "
245       resp << ApplicationController.helpers.render_localized_date(started_at)
246       resp << ". It "
247       if state_label == 'Complete'
248         resp << "completed in "
249       elsif state_label == 'Failed'
250          resp << "failed after "
251       else
252         resp << "has been active for "
253       end
254
255       if walltime > runningtime
256         resp << ApplicationController.helpers.render_time(walltime, false)
257       else
258        resp << ApplicationController.helpers.render_time(runningtime, false)
259       end
260
261       if finished_at
262         resp << " at "
263         resp << ApplicationController.helpers.render_localized_date(finished_at)
264       end
265       resp << "."
266     else
267       if state_label
268         resp << "This #{title} is "
269         resp << if state_label == 'Running' then 'active' else state_label.downcase end
270         resp << "."
271       end
272     end
273
274     if is_failed?
275       resp << " Check the Log tab for more detail about why it failed."
276     end
277     resp << "</p>"
278
279     resp << "<p>"
280     if state_label
281       resp << "It "
282       if state_label == 'Running'
283         resp << "has run"
284       else
285         resp << "ran"
286       end
287       resp << " for "
288
289       cpu_time = 0
290       if children.any?
291         cpu_time = children.map { |c|
292           if c.started_at
293              (c.runtime_constraints.andand[:min_nodes] || 1) * ((c.finished_at || Time.now()) - c.started_at)
294           else
295             0
296           end
297         }.reduce(:+) || 0
298       else
299         if started_at
300           cpu_time = (runtime_constraints.andand[:min_nodes] || 1) * ((finished_at || Time.now()) - started_at)
301         end
302       end
303
304       resp << ApplicationController.helpers.render_time(runningtime, false)
305       if (walltime - runningtime) > 0
306         resp << "("
307         resp << ApplicationController.helpers.render_time(walltime - runningtime, false)
308         resp << "queued)"
309       end
310       if cpu_time == 0
311         resp << "."
312       else
313         resp << " and used "
314         resp << ApplicationController.helpers.render_time(cpu_time, false)
315         resp << " of node allocation time ("
316         resp << (cpu_time/runningtime).round(1).to_s
317         resp << "&Cross; scaling)."
318       end
319     end
320     resp << "</p>"
321
322     resp
323   end
324
325   def uri
326     uuid = get(:uuid)
327     "/#{@proxied.class.table_name}/#{uuid}"
328   end
329
330   protected
331
332   def get key, obj=@proxied
333     if obj.respond_to? key
334       obj.send(key)
335     elsif obj.is_a?(Hash)
336       obj[key]
337     end
338   end
339 end