8784: Fix test for latest firefox.
[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, parent
10     @lbl = label
11     @proxied = proxied
12     @parent = parent
13   end
14
15   def label
16     @lbl
17   end
18
19   def uuid
20     get(:uuid)
21   end
22
23   def parent
24     @parent
25   end
26
27   def modified_by_user_uuid
28     get(:modified_by_user_uuid)
29   end
30
31   def owner_uuid
32     get(:owner_uuid)
33   end
34
35   def created_at
36     t = get(:created_at)
37     t = Time.parse(t) if (t.is_a? String)
38     t
39   end
40
41   def started_at
42     t = get(:started_at)
43     t = Time.parse(t) if (t.is_a? String)
44     t
45   end
46
47   def modified_at
48     t = get(:modified_at)
49     t = Time.parse(t) if (t.is_a? String)
50     t
51   end
52
53   def finished_at
54     t = get(:finished_at)
55     t = Time.parse(t) if (t.is_a? String)
56     t
57   end
58
59   def state_label
60     state = get(:state)
61     if ["Running", "RunningOnServer", "RunningOnClient"].include? state
62       "Running"
63     elsif state == 'New'
64       "Not started"
65     else
66       state
67     end
68   end
69
70   def state_bootstrap_class
71     state = state_label
72     case state
73     when 'Complete'
74       'success'
75     when 'Failed', 'Cancelled'
76       'danger'
77     when 'Running', 'RunningOnServer', 'RunningOnClient'
78       'info'
79     else
80       'default'
81     end
82   end
83
84   def success?
85     state = state_label
86     if state == 'Complete'
87       true
88     elsif state == 'Failed' or state == 'Cancelled'
89       false
90     else
91       nil
92     end
93   end
94
95   def child_summary
96     done = 0
97     failed = 0
98     todo = 0
99     running = 0
100     children.each do |c|
101       case c.state_label
102       when 'Complete'
103         done = done+1
104       when 'Failed', 'Cancelled'
105         failed = failed+1
106       when 'Running'
107         running = running+1
108       else
109         todo = todo+1
110       end
111     end
112
113     summary = {}
114     summary[:done] = done
115     summary[:failed] = failed
116     summary[:todo] = todo
117     summary[:running] = running
118     summary
119   end
120
121   def child_summary_str
122     summary = child_summary
123     summary_txt = ''
124
125     if state_label == 'Running'
126       done = summary[:done] || 0
127       running = summary[:running] || 0
128       failed = summary[:failed] || 0
129       todo = summary[:todo] || 0
130       total = done + running + failed + todo
131
132       if total > 0
133         summary_txt += "#{summary[:done]} #{'child'.pluralize(summary[:done])} done,"
134         summary_txt += "#{summary[:failed]} failed,"
135         summary_txt += "#{summary[:running]} running,"
136         summary_txt += "#{summary[:todo]} pending"
137       end
138     end
139     summary_txt
140   end
141
142   def progress
143     state = state_label
144     if state == 'Complete'
145       return 1.0
146     elsif state == 'Failed' or state == 'Cancelled'
147       return 0.0
148     end
149
150     summary = child_summary
151     return 0.0 if summary.nil?
152
153     done = summary[:done] || 0
154     running = summary[:running] || 0
155     failed = summary[:failed] || 0
156     todo = summary[:todo] || 0
157     total = done + running + failed + todo
158     if total > 0
159       (done+failed).to_f / total
160     else
161       0.0
162     end
163   end
164
165   def children
166     []
167   end
168
169   def outputs
170     []
171   end
172
173   def title
174     "process"
175   end
176
177   def has_unreadable_children
178     @unreadable_children
179   end
180
181   def walltime
182     if state_label != "Queued"
183       if started_at
184         ((if finished_at then finished_at else Time.now() end) - started_at)
185       end
186     end
187   end
188
189   def cputime
190     if children.any?
191       children.map { |c|
192         c.cputime
193       }.reduce(:+) || 0
194     else
195       if started_at
196         (runtime_constraints.andand[:min_nodes] || 1).to_i * ((finished_at || Time.now()) - started_at)
197       else
198         0
199       end
200     end
201   end
202
203   def queuedtime
204     if state_label == "Queued"
205       Time.now - Time.parse(created_at.to_s)
206     end
207   end
208
209   def is_running?
210     state_label == 'Running'
211   end
212
213   def is_paused?
214     state_label == 'Paused'
215   end
216
217   def is_finished?
218     state_label.in? ["Complete", "Failed", "Cancelled"]
219   end
220
221   def is_failed?
222     state_label == 'Failed'
223   end
224
225   def runtime_contributors
226     contributors = []
227     if children.any?
228       children.each{|c| contributors << c.runtime_contributors}
229     else
230       contributors << self
231     end
232     contributors.flatten
233   end
234
235   def runningtime
236     ApplicationController.helpers.determine_wallclock_runtime runtime_contributors
237   end
238
239   def show_runtime
240     walltime = 0
241     running_time = runningtime
242     if started_at
243       walltime = if finished_at then (finished_at - started_at) else (Time.now - started_at) end
244     end
245     resp = '<p>'
246
247     if started_at
248       resp << "This #{title} started at "
249       resp << ApplicationController.helpers.render_localized_date(started_at)
250       resp << ". It "
251       if state_label == 'Complete'
252         resp << "completed in "
253       elsif state_label == 'Failed'
254          resp << "failed after "
255       else
256         resp << "has been active for "
257       end
258
259       resp << ApplicationController.helpers.render_time(walltime, false)
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 has runtime of "
282
283       cpu_time = cputime
284
285       resp << ApplicationController.helpers.render_time(running_time, false)
286       if (walltime - running_time) > 0
287         resp << "("
288         resp << ApplicationController.helpers.render_time(walltime - running_time, false)
289         resp << "queued)"
290       end
291       if cpu_time == 0
292         resp << "."
293       else
294         resp << " and used "
295         resp << ApplicationController.helpers.render_time(cpu_time, false)
296         resp << " of node allocation time ("
297         resp << (cpu_time/running_time).round(1).to_s
298         resp << "&Cross; scaling)."
299       end
300     end
301     resp << "</p>"
302
303     resp
304   end
305
306   def log_object_uuids
307     [uuid]
308   end
309
310   def live_log_lines(limit)
311     Log.where(object_uuid: log_object_uuids).
312       order("created_at DESC").
313       limit(limit).
314       with_count('none').
315       select { |log| log.properties[:text].is_a? String }.
316       reverse.
317       flat_map { |log| log.properties[:text].split("\n") }
318   end
319
320   protected
321
322   def get key, obj=@proxied
323     if obj.respond_to? key
324       obj.send(key)
325     elsif obj.is_a?(Hash)
326       obj[key] || obj[key.to_s]
327     end
328   end
329 end