11305: added kernel and module dependency for docker migration in doc
[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 show_runtime
226     runningtime = ApplicationController.helpers.determine_wallclock_runtime(if children.any? then children else [self] end)
227
228     walltime = 0
229     if started_at
230       walltime = if finished_at then (finished_at - started_at) else (Time.now - started_at) end
231     end
232
233     resp = '<p>'
234
235     if started_at
236       resp << "This #{title} started at "
237       resp << ApplicationController.helpers.render_localized_date(started_at)
238       resp << ". It "
239       if state_label == 'Complete'
240         resp << "completed in "
241       elsif state_label == 'Failed'
242          resp << "failed after "
243       else
244         resp << "has been active for "
245       end
246
247       if walltime > runningtime
248         resp << ApplicationController.helpers.render_time(walltime, false)
249       else
250        resp << ApplicationController.helpers.render_time(runningtime, false)
251       end
252
253       if finished_at
254         resp << " at "
255         resp << ApplicationController.helpers.render_localized_date(finished_at)
256       end
257       resp << "."
258     else
259       if state_label
260         resp << "This #{title} is "
261         resp << if state_label == 'Running' then 'active' else state_label.downcase end
262         resp << "."
263       end
264     end
265
266     if is_failed?
267       resp << " Check the Log tab for more detail about why it failed."
268     end
269     resp << "</p>"
270
271     resp << "<p>"
272     if state_label
273       resp << "It "
274       if state_label == 'Running'
275         resp << "has run"
276       else
277         resp << "ran"
278       end
279       resp << " for "
280
281       cpu_time = cputime
282
283       resp << ApplicationController.helpers.render_time(runningtime, false)
284       if (walltime - runningtime) > 0
285         resp << "("
286         resp << ApplicationController.helpers.render_time(walltime - runningtime, false)
287         resp << "queued)"
288       end
289       if cpu_time == 0
290         resp << "."
291       else
292         resp << " and used "
293         resp << ApplicationController.helpers.render_time(cpu_time, false)
294         resp << " of node allocation time ("
295         resp << (cpu_time/runningtime).round(1).to_s
296         resp << "&Cross; scaling)."
297       end
298     end
299     resp << "</p>"
300
301     resp
302   end
303
304   def log_object_uuids
305     [uuid]
306   end
307
308   def live_log_lines(limit)
309     Log.where(object_uuid: log_object_uuids).
310       order("created_at DESC").
311       limit(limit).
312       select { |log| log.properties[:text].is_a? String }.
313       reverse.
314       flat_map { |log| log.properties[:text].split("\n") }
315   end
316
317   protected
318
319   def get key, obj=@proxied
320     if obj.respond_to? key
321       obj.send(key)
322     elsif obj.is_a?(Hash)
323       obj[key] || obj[key.to_s]
324     end
325   end
326 end