8876: work_unit views in progress
authorradhika <radhika@curoverse.com>
Sat, 21 May 2016 03:40:00 +0000 (23:40 -0400)
committerradhika <radhika@curoverse.com>
Sat, 21 May 2016 03:40:00 +0000 (23:40 -0400)
apps/workbench/app/helpers/pipeline_instances_helper.rb
apps/workbench/app/models/job_work_unit.rb
apps/workbench/app/models/pipeline_instance_work_unit.rb
apps/workbench/app/models/proxy_work_unit.rb
apps/workbench/app/models/work_unit.rb
apps/workbench/app/views/jobs/_show_status.html.erb
apps/workbench/app/views/pipeline_instances/_show_components.html.erb
apps/workbench/app/views/work_unit/_component_detail.html.erb [new file with mode: 0644]
apps/workbench/app/views/work_unit/_show_child.html.erb [new file with mode: 0644]
apps/workbench/app/views/work_unit/_show_component.html.erb [new file with mode: 0644]
apps/workbench/test/unit/work_unit_test.rb

index 8fafbc2022d5873032d1f9565c2385a26f4a794b..b2486c1d90d70e15ec77c6c85d9dfcb57536f952 100644 (file)
@@ -70,8 +70,8 @@ module PipelineInstancesHelper
     timestamps = []
     jobs.each do |j|
       insert_at = 0
-      started_at = j[:started_at]
-      finished_at = (if j[:finished_at] then j[:finished_at] else Time.now end)
+      started_at = j.started_at
+      finished_at = (if j.finished_at then j.finished_at else Time.now end)
       if started_at
         timestamps = merge_range timestamps, started_at, finished_at
       end
index 2a5cb9881030009982704f6bfa5b42d948ef1a93..8246e8ac87880033719a18d3ae1c454da009b511 100644 (file)
@@ -42,7 +42,7 @@ class JobWorkUnit < ProxyWorkUnit
   end
 
   def log_collection
-    [self.proxied.log]
+    self.proxied.log
   end
 
   def output
index 3f8c9a2baaf2cca40062ec7c03f7d2793113881e..94990eee16ca2bd4f6d78c822e0f7a6d936b18a8 100644 (file)
@@ -5,7 +5,7 @@ class PipelineInstanceWorkUnit < ProxyWorkUnit
     jobs = {}
     results = Job.where(uuid: self.proxied.job_ids.values).results
     results.each do |j|
-      jobs[j.uuid] = j.work_unit("job #{items.size}")
+      jobs[j.uuid] = j
     end
 
     components = self.proxied.components
@@ -13,7 +13,7 @@ class PipelineInstanceWorkUnit < ProxyWorkUnit
       if c.is_a?(Hash)
         job = c[:job]
         if job and job[:uuid] and jobs[job[:uuid]]
-          items << jobs[job[:uuid]]
+          items << jobs[job[:uuid]].work_unit(name)
         else
           items << ProxyWorkUnit.new(c, name)
         end
@@ -48,8 +48,4 @@ class PipelineInstanceWorkUnit < ProxyWorkUnit
       0.0
     end
   end
-
-  def log_collection
-    self.proxied.job_log_ids
-  end
 end
index 502e6d250c151a4279c9e045a693fccb6950ed8c..e7806783eb0c41dc2f7b2e363d447680d619ab9b 100644 (file)
@@ -33,9 +33,9 @@ class ProxyWorkUnit < WorkUnit
 
   def state_label
     if ["Running", "RunningOnServer", "RunningOnClient"].include? self.proxied[:state].to_s
-      "running"
+      "Running"
     else
-      self.proxied[:state].to_s.downcase
+      self.proxied[:state].to_s
     end
   end
 
@@ -70,7 +70,7 @@ class ProxyWorkUnit < WorkUnit
     self.proxied[:script]
   end
 
-  def script_repository
+  def repository
     self.proxied[:repository]
   end
 
@@ -85,4 +85,8 @@ class ProxyWorkUnit < WorkUnit
   def runtime_constraints
     self.proxied[:runtime_constraints]
   end
+
+  def children
+    []
+  end
 end
index 00f1435de58ad2df3c40bc83b3937ae0e0641d6c..41855199c8d4b6b7085b149a28e0da3777bffb4c 100644 (file)
@@ -60,8 +60,8 @@ class WorkUnit
     # returns script for this work unit, if any
   end
 
-  def script_repository
-    # returns this work unit's script_repository, if any
+  def repository
+    # returns this work unit's script repository, if any
   end
 
   def script_version
index 807520940c9218473f01935f732556afa6cd373a..8d54b20cfd341398f9cc43377721f73f486fe606 100644 (file)
@@ -8,8 +8,7 @@
    pj[:progress_bar] = render(partial: "job_progress",
                               locals: {:j => @object })
    tasks = JobTask.filter([['job_uuid', '=', @object.uuid]]).results
-   render(partial: 'pipeline_instances/running_component',
-          locals: { tasks: tasks, pj: pj, i: 0, expanded: true})
+   render(partial: 'work_unit/show_component', locals: {wu: @object.work_unit(@object[:name] || "this job")})
 %>
 
 <div class="panel panel-default">
index dae57aa0e85ebbe3ccf7ce183a185dcc860b3f0b..4196558b3c07570c2d0e84b059cba20145b4fa41 100644 (file)
@@ -9,7 +9,7 @@
        data-object-uuids="<%= @object.uuid %> <%= job_uuids.join(' ') %>"
        ></div>
 
-  <%= render_pipeline_components("running", :json) %>
+  <%= render partial: 'work_unit/show_component', locals: {wu: @object.work_unit(@object.name)} %>
 
 <% else %>
   <%# state is either New or Ready %>
diff --git a/apps/workbench/app/views/work_unit/_component_detail.html.erb b/apps/workbench/app/views/work_unit/_component_detail.html.erb
new file mode 100644 (file)
index 0000000..045154a
--- /dev/null
@@ -0,0 +1,91 @@
+      <div class="container">
+        <div class="row">
+          <div class="col-md-5">
+            <table>
+              <% [:uuid, :modified_by_user_uuid, :created_at, :started_at, :finished_at, :priority].each do |k| %>
+                <% val = current_obj.send(k) if current_obj.respond_to?(k) %>
+                <% unless val.nil? %>
+                <tr>
+                  <td style="padding-right: 1em">
+                    <%= k.to_s %>:
+                  </td>
+                  <td>
+                    <% if k == :uuid %>
+                      <%= link_to_arvados_object_if_readable(val, val, link_text: val) %>
+                    <% elsif k.to_s.end_with? 'uuid' %>
+                      <%= link_to_arvados_object_if_readable(val, val, friendly_name: true) %>
+                    <% elsif k.to_s.end_with? '_at' %>
+                      <%= render_localized_date(val) %>
+                    <% else %>
+                      <%= val %>
+                    <% end %>
+                  </td>
+                </tr>
+                <% end %>
+              <% end %>
+            </table>
+          </div>
+          <div class="col-md-6">
+            <table>
+              <% # link to repo tree/file only if the repo is readable
+                 # and the commit is a sha1...
+                 repo =
+                 (/^[0-9a-f]{40}$/ =~ current_obj.script_version and
+                 Repository.where(name: current_obj.repository).first)
+
+                 # ...and the api server provides an http:// or https:// url
+                 repo = nil unless repo.andand.http_fetch_url
+                 %>
+              <% [:script, :repository, :script_version, :supplied_script_version, :nondeterministic].each do |k| %>
+                <% val = current_obj.send(k) if current_obj.respond_to?(k) %>
+                <% unless val.nil? %>
+                <tr>
+                  <td style="padding-right: 1em">
+                    <%= k.to_s %>:
+                  </td>
+                  <td>
+                    <% if repo and k == :repository %>
+                      <%= link_to val, show_repository_tree_path(id: repo.uuid, commit: current_obj.script_version, path: '/') %>
+                    <% elsif repo and k == :script %>
+                      <%= link_to val, show_repository_blob_path(id: repo.uuid, commit: current_obj.script_version, path: 'crunch_scripts/'+current_obj.script) %>
+                    <% elsif repo and k == :script_version %>
+                      <%= link_to val, show_repository_commit_path(id: repo.uuid, commit: current_obj.script_version) %>
+                    <% else %>
+                      <%= val %>
+                    <% end %>
+                  </td>
+                </tr>
+                <% end %>
+              <% end %>
+              <% if current_obj.runtime_constraints.andand[:docker_image] and current_obj.docker_image %>
+                <tr>
+                  <td style="padding-right: 1em">
+                    docker_image:
+                  </td>
+                  <td>
+                    <%= current_obj.runtime_constraints[:docker_image] %>
+                  </td>
+                </tr>
+                <tr>
+                  <td style="padding-right: 1em">
+                    docker_image_locator:
+                  </td>
+                  <td>
+                    <%= link_to_arvados_object_if_readable(current_obj.docker_image,
+                      current_obj.docker_image, friendly_name: true) %>
+                  </td>
+                </tr>
+              <% end %>
+            </table>
+          </div>
+        </div>
+        <% unless current_obj.parameters.nil? %>
+        <div class="row">
+          <div class="col-md-6">
+            <p>script_parameters:</p>
+            <pre><%= JSON.pretty_generate(current_obj.parameters) rescue nil %></pre>
+          </div>
+        </div>
+        <% end %>
+      </div>
+    </div>
diff --git a/apps/workbench/app/views/work_unit/_show_child.html.erb b/apps/workbench/app/views/work_unit/_show_child.html.erb
new file mode 100644 (file)
index 0000000..10c6ac0
--- /dev/null
@@ -0,0 +1,103 @@
+<div class="panel panel-default">
+  <div class="panel-heading">
+    <div class="container-fluid">
+      <div class="row-fluid">
+        <%# column offset 0 %>
+        <div class="col-md-2" style="word-break:break-all;">
+          <h4 class="panel-title">
+            <a data-toggle="collapse" href="#collapse<%= i %>">
+              <%= current_obj.label %> <span class="caret"></span>
+            </a>
+          </h4>
+        </div>
+
+        <%# column offset 2 %>
+        <div class="col-md-2 pipeline-instance-spacing">
+          <%= current_obj.progress %>
+        </div>
+
+        <%# column offset 4 %>
+        <% if not current_obj %>
+          <div class="col-md-8"></div>
+        <% else %>
+          <div class="col-md-1">
+              <% if current_obj.state_label.in? ["Complete", "Failed", "Cancelled"] %>
+                <% if current_obj.log_collection %>
+                  <% logCollection = Collection.find? current_obj.log_collection %>
+                  <% if logCollection %>
+                    <%= link_to "Log", job_path(current_obj.uuid, anchor: "Log") %>
+                  <% else %>
+                    Log unavailable
+                  <% end %>
+                <% end %>
+              <% elsif current_obj.state_label == "Running" %>
+                <% job = Job.find? current_obj.uuid %>
+                <% if job %>
+                  <%= link_to "Log", job_path(current_obj.uuid, anchor: "Log") %>
+                <% else %>
+                  Log unavailable
+                <% end %>
+              <% end %>
+          </div>
+
+          <%# column offset 5 %>
+          <% if current_obj.state_label != "Queued" %>
+          <div class="col-md-3">
+            <% if current_obj.started_at %>
+              <% walltime = ((if current_obj.finished_at then current_obj.finished_at else Time.now() end) - current_obj.started_at) %>
+              <% cputime = (current_obj.runtime_constraints.andand[:min_nodes] || 1) *
+                           ((current_obj.finished_at || Time.now()) - current_obj.started_at) %>
+              <%= render_runtime(walltime, false) %>
+              <% if cputime > 0 %> / <%= render_runtime(cputime, false) %> (<%= (cputime/walltime).round(1) %>&Cross;)<% end %>
+            <% end %>
+          </div>
+          <% end %>
+
+          <% if current_obj.state_label == "Queued" %>
+            <%# column offset 5 %>
+            <div class="col-md-6">
+              <% queuetime = Time.now - Time.parse(current_obj.created_at.to_s) %>
+              Queued for <%= render_runtime(queuetime, false) %>.
+            </div>
+          <% elsif current_obj.state_label == "Running" %>
+            <%# column offset 8 %>
+            <div class="col-md-3">
+              <span class="task-summary-status">
+                <%= current_obj.tasks_summary[:done] %>&nbsp;<%= "task".pluralize(current_obj.tasks_summary[:done]) %> done,
+                <%= current_obj.tasks_summary[:failed] %>&nbsp;failed,
+                <%= current_obj.tasks_summary[:running] %>&nbsp;running,
+                <%= current_obj.tasks_summary[:todo] %>&nbsp;pending
+              </span>
+            </div>
+          <% elsif current_obj.state_label.in? ["Complete", "Failed", "Cancelled"] %>
+            <%# column offset 8 %>
+            <div class="col-md-4 text-overflow-ellipsis">
+              <% if current_obj.output %>
+                <%= link_to_arvados_object_if_readable(current_obj.output, 'Output data not available', friendly_name: true) %>
+              <% elsif current_obj.output %>
+                <%= link_to_arvados_object_if_readable(current_obj.output, 'Output data not available', link_text: "Output of #{current_obj.label}") %>
+              <% else %>
+                No output.
+              <% end %>
+            </div>
+          <% end %>
+
+          <% if current_obj.state_label.in? ["Queued", "Running"] and @object.editable? %>
+            <%# column offset 11 %>
+            <div class="col-md-1 pipeline-instance-spacing">
+              <%= form_tag "/jobs/#{current_obj.uuid}/cancel", remote: true, style: "display:inline; padding-left: 1em" do |f| %>
+                <%= hidden_field_tag :return_to, url_for(@object) %>
+                <%= button_tag "Cancel", {class: 'btn btn-xs btn-danger', id: "cancel-job-button"} %>
+              <% end %>
+            </div>
+          <% end %>
+        <% end %>
+      </div>
+    </div>
+  </div>
+
+  <div id="collapse<%= i %>" class="panel-collapse collapse <%= if expanded then 'in' end %>">
+    <div class="panel-body">
+      <%= render partial: 'work_unit/show_component', locals: {wu: current_obj} %>
+  </div>
+</div>
diff --git a/apps/workbench/app/views/work_unit/_show_component.html.erb b/apps/workbench/app/views/work_unit/_show_component.html.erb
new file mode 100644 (file)
index 0000000..0e650ac
--- /dev/null
@@ -0,0 +1,105 @@
+<%# Work unit status %>
+
+<div class="pull-right" style="padding-left: 1em">
+  Current state: <span class="badge badge-<%= wu.state_bootstrap_class %>" data-wu-state="<%= wu.state_label %>">
+    <% if wu.state_label == "running" %>
+      Active
+    <% else %>
+      <%= wu.state_label %>
+    <% end %>
+  </span>&nbsp;
+</div>
+
+<% if wu.state_label == 'Paused' %>
+  <p>
+    This work unit is paused. Work unit children that are
+    already running will continue to run, but no new work units will be submitted.
+  </p>
+<% end %>
+
+<% runningtime = determine_wallclock_runtime(wu.children) %>
+
+<p>
+  <% if wu.started_at %>
+    This work unit started at <%= render_localized_date(wu.started_at) %>.
+    It
+    <% if wu.state_label == 'Complete' %>
+      completed in
+    <% elsif wu.state_label == 'Failed' %>
+      failed after
+    <% else %>
+      has been active for
+    <% end %>
+
+    <% walltime = if wu.finished_at then
+                    wu.finished_at - wu.started_at
+                  else
+                    Time.now - wu.started_at
+                  end %>
+
+    <%= if walltime > runningtime
+          render_runtime(walltime, false)
+        else
+          render_runtime(runningtime, false)
+        end %><% if wu.finished_at %> at <%= render_localized_date(wu.finished_at) %><% end %>.
+    <% else %>
+      It is <%= if wu.state_label == 'Running' then 'active' else wu.state.downcase end %>.
+        <% walltime = 0%>
+    <% end %>
+
+  <% if wu.state_label == 'Failed' %>
+    Check the Log tab for more detail about why it failed.
+  <% end %>
+</p>
+
+<p>
+    It
+    <% if wu.state_label == 'Running' %>
+      has run
+    <% else %>
+      ran
+    <% end %>
+    for
+    <%
+        cputime = wu.children.map { |c|
+        if c.started_at
+          (c.runtime_constraints.andand[:min_nodes] || 1) * ((c.finished_at || Time.now()) - c.started_at)
+        else
+          0
+        end
+       }.reduce(:+) || 0 %>
+    <%= render_runtime(runningtime, false) %><% if (walltime - runningtime) > 0 %>
+      (<%= render_runtime(walltime - runningtime, false) %> queued)<% end %><% if cputime == 0 %>.<% else %>
+      and used
+    <%= render_runtime(cputime, false) %>
+    of node allocation time (<%= (cputime/runningtime).round(1) %>&Cross; scaling).
+    <% end %>
+</p>
+
+<p>
+  <%= render partial: 'work_unit/component_detail', locals: {current_obj: wu} %>
+</p>
+
+<%# Work unit children %>
+
+<%
+  job_uuids = wu.children.collect {|c| c.uuid}.compact
+  if job_uuids.any?
+    resource_class = resource_class_for_uuid(job_uuids.first, friendly_name: true)
+    preload_objects_for_dataclass resource_class, job_uuids
+  end
+
+  job_collections = wu.children.collect {|j| j.output}.compact
+  job_collections.concat wu.children.collect {|j| j.docker_image}.uniq.compact
+  job_collections_pdhs = job_collections.select {|x| !(m = CollectionsHelper.match(x)).nil?}.uniq.compact
+  job_collections_uuids = job_collections - job_collections_pdhs
+  preload_collections_for_objects job_collections_uuids if job_collections_uuids.any?
+  preload_for_pdhs job_collections_pdhs if job_collections_pdhs.any?
+%>
+
+<% @descendent_count = 0 if !@descendent_count %>
+
+<% wu.children.each_with_index do |c, i| %>
+  <% @descendent_count += 1 %>
+  <%= render partial: 'work_unit/show_child', locals: {current_obj: c, i: @descendent_count, expanded: false} %>
+<% end %>
index 66b4d9cd7380c63f079dc40226c11fb9ea9123cb..63f6a5aacdf17ca2395c3ebe5b7a44bc5f0a4c92 100644 (file)
@@ -2,10 +2,10 @@ require 'test_helper'
 
 class WorkUnitTest < ActiveSupport::TestCase
   [
-    [Job, 'running_job_with_components', "jwu", 2, "running", nil, 0.2],
-    [PipelineInstance, 'pipeline_in_running_state', nil, 1, "running", nil, 0.0],
-    [PipelineInstance, 'has_component_with_completed_jobs', nil, 3, "complete", true, 1.0],
-    [PipelineInstance, 'pipeline_with_tagged_collection_input', "pwu", 1, "ready", nil, 0.0],
+    [Job, 'running_job_with_components', "jwu", 2, "Running", nil, 0.2],
+    [PipelineInstance, 'pipeline_in_running_state', nil, 1, "Running", nil, 0.0],
+    [PipelineInstance, 'has_component_with_completed_jobs', nil, 3, "Complete", true, 1.0],
+    [PipelineInstance, 'pipeline_with_tagged_collection_input', "pwu", 1, "Ready", nil, 0.0],
   ].each do |type, name, label, num_children, state, success, progress|
     test "children of #{name}" do
       use_token 'admin'