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