10144: Replace work_unit -> link_to_log with partial so that preloaded objects are...
[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 owner_uuid
27     get(:owner_uuid)
28   end
29
30   def created_at
31     t = get(:created_at)
32     t = Time.parse(t) if (t.is_a? String)
33     t
34   end
35
36   def started_at
37     t = get(:started_at)
38     t = Time.parse(t) if (t.is_a? String)
39     t
40   end
41
42   def modified_at
43     t = get(:modified_at)
44     t = Time.parse(t) if (t.is_a? String)
45     t
46   end
47
48   def finished_at
49     t = get(:finished_at)
50     t = Time.parse(t) if (t.is_a? String)
51     t
52   end
53
54   def state_label
55     state = get(:state)
56     if ["Running", "RunningOnServer", "RunningOnClient"].include? state
57       "Running"
58     elsif state == 'New'
59       "Not started"
60     else
61       state
62     end
63   end
64
65   def state_bootstrap_class
66     state = state_label
67     case state
68     when 'Complete'
69       'success'
70     when 'Failed', 'Cancelled'
71       'danger'
72     when 'Running', 'RunningOnServer', 'RunningOnClient'
73       'info'
74     else
75       'default'
76     end
77   end
78
79   def success?
80     state = state_label
81     if state == 'Complete'
82       true
83     elsif state == 'Failed' or state == 'Cancelled'
84       false
85     else
86       nil
87     end
88   end
89
90   def child_summary
91     done = 0
92     failed = 0
93     todo = 0
94     running = 0
95     children.each do |c|
96       case c.state_label
97       when 'Complete'
98         done = done+1
99       when 'Failed', 'Cancelled'
100         failed = failed+1
101       when 'Running'
102         running = running+1
103       else
104         todo = todo+1
105       end
106     end
107
108     summary = {}
109     summary[:done] = done
110     summary[:failed] = failed
111     summary[:todo] = todo
112     summary[:running] = running
113     summary
114   end
115
116   def child_summary_str
117     summary = child_summary
118     summary_txt = ''
119
120     if state_label == 'Running'
121       done = summary[:done] || 0
122       running = summary[:running] || 0
123       failed = summary[:failed] || 0
124       todo = summary[:todo] || 0
125       total = done + running + failed + todo
126
127       if total > 0
128         summary_txt += "#{summary[:done]} #{'child'.pluralize(summary[:done])} done,"
129         summary_txt += "#{summary[:failed]} failed,"
130         summary_txt += "#{summary[:running]} running,"
131         summary_txt += "#{summary[:todo]} pending"
132       end
133     end
134     summary_txt
135   end
136
137   def progress
138     state = state_label
139     if state == 'Complete'
140       return 1.0
141     elsif state == 'Failed' or state == 'Cancelled'
142       return 0.0
143     end
144
145     summary = child_summary
146     return 0.0 if summary.nil?
147
148     done = summary[:done] || 0
149     running = summary[:running] || 0
150     failed = summary[:failed] || 0
151     todo = summary[:todo] || 0
152     total = done + running + failed + todo
153     if total > 0
154       (done+failed).to_f / total
155     else
156       0.0
157     end
158   end
159
160   def children
161     []
162   end
163
164   def outputs
165     []
166   end
167
168   def title
169     "process"
170   end
171
172   def has_unreadable_children
173     @unreadable_children
174   end
175
176   def walltime
177     if state_label != "Queued"
178       if started_at
179         ((if finished_at then finished_at else Time.now() end) - started_at)
180       end
181     end
182   end
183
184   def cputime
185     if state_label != "Queued"
186       if started_at
187         (runtime_constraints.andand[:min_nodes] || 1) * ((finished_at || Time.now()) - started_at)
188       end
189     end
190   end
191
192   def queuedtime
193     if state_label == "Queued"
194       Time.now - Time.parse(created_at.to_s)
195     end
196   end
197
198   def is_running?
199     state_label == 'Running'
200   end
201
202   def is_paused?
203     state_label == 'Paused'
204   end
205
206   def is_finished?
207     state_label.in? ["Complete", "Failed", "Cancelled"]
208   end
209
210   def is_failed?
211     state_label == 'Failed'
212   end
213
214   def show_runtime
215     runningtime = ApplicationController.helpers.determine_wallclock_runtime(if children.any? then children else [self] end)
216
217     walltime = 0
218     if started_at
219       walltime = if finished_at then (finished_at - started_at) else (Time.now - started_at) end
220     end
221
222     resp = '<p>'
223
224     if started_at
225       resp << "This #{title} started at "
226       resp << ApplicationController.helpers.render_localized_date(started_at)
227       resp << ". It "
228       if state_label == 'Complete'
229         resp << "completed in "
230       elsif state_label == 'Failed'
231          resp << "failed after "
232       else
233         resp << "has been active for "
234       end
235
236       if walltime > runningtime
237         resp << ApplicationController.helpers.render_time(walltime, false)
238       else
239        resp << ApplicationController.helpers.render_time(runningtime, false)
240       end
241
242       if finished_at
243         resp << " at "
244         resp << ApplicationController.helpers.render_localized_date(finished_at)
245       end
246       resp << "."
247     else
248       if state_label
249         resp << "This #{title} is "
250         resp << if state_label == 'Running' then 'active' else state_label.downcase end
251         resp << "."
252       end
253     end
254
255     if is_failed?
256       resp << " Check the Log tab for more detail about why it failed."
257     end
258     resp << "</p>"
259
260     resp << "<p>"
261     if state_label
262       resp << "It "
263       if state_label == 'Running'
264         resp << "has run"
265       else
266         resp << "ran"
267       end
268       resp << " for "
269
270       cpu_time = 0
271       if children.any?
272         cpu_time = children.map { |c|
273           if c.started_at
274              (c.runtime_constraints.andand[:min_nodes] || 1) * ((c.finished_at || Time.now()) - c.started_at)
275           else
276             0
277           end
278         }.reduce(:+) || 0
279       else
280         if started_at
281           cpu_time = (runtime_constraints.andand[:min_nodes] || 1) * ((finished_at || Time.now()) - started_at)
282         end
283       end
284
285       resp << ApplicationController.helpers.render_time(runningtime, false)
286       if (walltime - runningtime) > 0
287         resp << "("
288         resp << ApplicationController.helpers.render_time(walltime - runningtime, 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/runningtime).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       select { |log| log.properties[:text].is_a? String }.
315       reverse.
316       flat_map { |log| log.properties[:text].split("\n") }
317   end
318
319   protected
320
321   def get key, obj=@proxied
322     if obj.respond_to? key
323       obj.send(key)
324     elsif obj.is_a?(Hash)
325       obj[key]
326     end
327   end
328 end