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