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