2872: Add "clone and edit" button to pipeline instance page.
authorTom Clegg <tom@curoverse.com>
Tue, 3 Jun 2014 06:12:43 +0000 (02:12 -0400)
committerTom Clegg <tom@curoverse.com>
Tue, 3 Jun 2014 06:12:43 +0000 (02:12 -0400)
apps/workbench/app/controllers/application_controller.rb
apps/workbench/app/controllers/pipeline_instances_controller.rb
apps/workbench/app/helpers/application_helper.rb
apps/workbench/app/models/arvados_base.rb
apps/workbench/app/views/folders/_show_contents_rows.html.erb
apps/workbench/app/views/pipeline_instances/_show_components.html.erb
apps/workbench/app/views/pipeline_instances/_show_inputs.html.erb
apps/workbench/app/views/pipeline_templates/_choose_rows.html.erb
apps/workbench/config/routes.rb

index 4347b9b7185182faefbb661ac04227f879ba43b3..1fcfc3aea7bc32d3e5016b924351770334c4524d 100644 (file)
@@ -191,6 +191,24 @@ class ApplicationController < ActionController::Base
     show
   end
 
+  # Clone the given object, merging any attribute values supplied as
+  # with a create action.
+  def copy
+    @new_resource_attrs ||= params[model_class.to_s.underscore.singularize]
+    @new_resource_attrs ||= {}
+    @object = @object.dup
+    @object.update_attributes @new_resource_attrs
+    if not @new_resource_attrs[:name] and @object.respond_to? :name
+      if @object.name and @object.name != ''
+        @object.name = "Copy of #{@object.name}"
+      else
+        @object.name = "Copy of unnamed #{@object.class_for_display.downcase}"
+      end
+    end
+    @object.save!
+    show
+  end
+
   def destroy
     if @object.destroy
       respond_to do |f|
index 3bb18aa0fdace3bcea1d01bd9f444bcdb215ae9b..4275410e24372bf3c54a489dac8e8d65ebbdbead 100644 (file)
@@ -3,6 +3,15 @@ class PipelineInstancesController < ApplicationController
   before_filter :find_objects_by_uuid, only: :compare
   include PipelineInstancesHelper
 
+  def copy
+    @object = @object.dup
+    @object.components.each do |cname, component|
+      component.delete :job
+    end
+    @object.state = 'New'
+    super
+  end
+
   def update
     @updates ||= params[@object.class.to_s.underscore.singularize.to_sym]
     if (components = @updates[:components])
index dc97251132ea106493ead45ff7e290e5c6746a0e..d9e97b95f75c909ee059ce4a306c4186d6acc1b3 100644 (file)
@@ -179,7 +179,12 @@ module ApplicationHelper
       "id" => span_id,
       :class => "editable"
     }.merge(htmloptions).merge(ajax_options)
-    span_tag + raw(' <a href="#" class="btn btn-xs btn-default btn-nodecorate" data-toggle="x-editable tooltip" data-toggle-selector="#' + span_id + '" data-placement="top" title="edit"><i class="fa fa-fw fa-pencil"></i></a>')
+    edit_button = raw('<a href="#" class="btn btn-xs btn-default btn-nodecorate" data-toggle="x-editable tooltip" data-toggle-selector="#' + span_id + '" data-placement="top" title="' + (htmloptions[:tiptitle] || 'edit') + '"><i class="fa fa-fw fa-pencil"></i></a>')
+    if htmloptions[:tipplacement] == :left
+      edit_button + ' ' + span_tag
+    else
+      span_tag + ' ' + edit_button
+    end
   end
 
   def render_pipeline_component_attribute(object, attr, subattr, value_info, htmloptions={})
index e8b4cafd6ac930039edadbfe58f967e62167309e..590f6282e9cff9b8090a572d0890ce1f3ad2dd16 100644 (file)
@@ -273,8 +273,9 @@ class ArvadosBase < ActiveRecord::Base
     uuid
   end
 
-  def dup
-    super.forget_uuid!
+  def initialize_copy orig
+    super
+    forget_uuid!
   end
 
   def attributes_for_display
index 54e7ff60efb0af38f112f102acccfaed2a3bcbed..6aa0a877faaa297fdc9dede9e95355741b59acf5 100644 (file)
@@ -15,7 +15,7 @@
 
       <%= render :partial => "show_object_button", :locals => {object: object, size: 'sm'} %>
 
-      <%= render_editable_attribute name_object, 'name', nil %>
+      <%= render_editable_attribute name_object, 'name', nil, {tipplacement: :left, tiptitle: 'rename'} %>
     </td>
     <td class="arv-description-in-table">
       <%= render_controller_partial(
index 6d6a8b1afb3c7957d743595031a79f05759d5904..efd36d2399971dcc869403216e01cb2b38212b4a 100644 (file)
@@ -1,9 +1,4 @@
-<% content_for :css do %>
-
-<% end %>
-
 <% template = PipelineTemplate.find(@object.pipeline_template_uuid) rescue nil %>
-
 <%= content_for :content_top do %>
   <h2>
     <%= render_editable_attribute @object, 'name', nil %>
   <% end %>
 <% end %>
 
-<% pipeline_job_uuids = [] %>
+<% content_for :tab_line_buttons do %>
+  <%= link_to(copy_pipeline_instance_path('id' => @object.uuid, 'pipeline_instance[state]' => 'New'),
+      class: 'btn btn-primary',
+      #data: {toggle: :tooltip, placement: :top}, title: 'copy and modify',
+      method: :post,
+      ) do %>
+    Clone and edit <i class="fa fa-fw fa-copy"></i>
+  <% end %>
+<% end %>
 
-<% if !@object.state.in? ['New', 'Ready', 'Paused'] %>
-<table class="table pipeline-components-table">
-  <colgroup>
-    <col style="width: 15%" />
-    <col style="width: 20%" />
-    <col style="width: 12%" />
-    <col style="width: 12%" />
-    <col style="width: 45%" />
-  </colgroup>
-  <thead>
-    <tr>
-      <th>
-        component
-      </th><th>
-        script, version
-      </th><th>
-        job
-        <%# format:'js' here helps browsers avoid using the cached js
-        content in html context (e.g., duplicate tab -> see
-        javascript) %>
-        <%= link_to '(refresh)', {format: :js}, {class: 'refresh hide', remote: true, method: 'get'} %>
-      </th><th>
-      </th><th>
-        output
-      </th>
-    </tr>
-  </thead>
-  <tbody>
-    <% render_pipeline_jobs.each do |pj| %>
-      <% if pj[:job].andand[:uuid]
-           pipeline_job_uuids << pj[:job][:uuid]
-         end %>
-    <tr>
-      <td>
-        <%= pj[:name] %>
-      </td><td>
-        <%= pj[:script] %>
-        <br /><span class="deemphasize"><%= pj[:script_version] %></span>
-      </td><td>
-        <%= pj[:progress_bar] %>
-        <% if @object.state == 'Complete' || @object.state == 'Failed' %>
-          <% if pj[:job].andand[:uuid] %>
-            <span class="deemphasize">
-            <%= link_to("..."+pj[:job][:uuid].last(15), job_url(id: pj[:job][:uuid])) %>
-            </span>
+<% if !@object.state.in? ['New', 'Ready'] %>
 
-            <% current_job = Job.find(pj[:job][:uuid]) rescue nil %>
-            <% if current_job.andand[:log] %>
-              <% fixup = /([a-f0-9]{32}\+\d+)(\+?.*)/.match(current_job[:log])%>
-              <% Collection.limit(1).where(uuid: fixup[1]).each do |c| %>
-                <% c.files.each do |file| %>
-                  <br/><span class="deemphasize">
-                  <a href="<%= collection_path(current_job[:log]) %>/<%= file[1] %>?disposition=inline&size=<%= file[2] %>">log</a>
-                  </span>
+  <% if @object.state.in? ['RunningOnClient', 'RunningOnServer'] %>
+    <% content_for :tab_line_buttons do %>
+      <%= link_to(url_for('pipeline_instance[state]' => 'Paused'),
+          class: 'btn btn-primary run-pipeline-button',
+          method: :patch
+          ) do %>
+        Stop <i class="fa fa-fw fa-stop"></i>
+      <% end %>
+    <% end %>
+  <% end %>
+
+  <% pipeline_job_uuids = [] %>
+
+  <div class="pull-right">
+    Current state: <span class="badge badge-info"><%= @object.state.sub('OnServer', '') %></span>&nbsp;
+  </div>
+
+  <table class="table pipeline-components-table">
+    <colgroup>
+      <col style="width: 15%" />
+      <col style="width: 20%" />
+      <col style="width: 12%" />
+      <col style="width: 12%" />
+      <col style="width: 45%" />
+    </colgroup>
+    <thead>
+      <tr>
+        <th>
+          component
+        </th><th>
+          script, version
+        </th><th>
+          job
+          <%# format:'js' here helps browsers avoid using the cached js
+          content in html context (e.g., duplicate tab -> see
+          javascript) %>
+          <%= link_to '(refresh)', {format: :js}, {class: 'refresh hide', remote: true, method: 'get'} %>
+        </th><th>
+        </th><th>
+          output
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <% render_pipeline_jobs.each do |pj| %>
+        <% if pj[:job].andand[:uuid]
+             pipeline_job_uuids << pj[:job][:uuid]
+           end %>
+      <tr>
+        <td>
+          <%= pj[:name] %>
+        </td><td>
+          <%= pj[:script] %>
+          <br /><span class="deemphasize"><%= pj[:script_version] %></span>
+        </td><td>
+          <%= pj[:progress_bar] %>
+          <% if @object.state == 'Complete' || @object.state == 'Failed' %>
+            <% if pj[:job].andand[:uuid] %>
+              <span class="deemphasize">
+              <%= link_to("..."+pj[:job][:uuid].last(15), job_url(id: pj[:job][:uuid])) %>
+              </span>
+
+              <% current_job = Job.find(pj[:job][:uuid]) rescue nil %>
+              <% if current_job.andand[:log] %>
+                <% fixup = /([a-f0-9]{32}\+\d+)(\+?.*)/.match(current_job[:log])%>
+                <% Collection.limit(1).where(uuid: fixup[1]).each do |c| %>
+                  <% c.files.each do |file| %>
+                    <br/><span class="deemphasize">
+                    <a href="<%= collection_path(current_job[:log]) %>/<%= file[1] %>?disposition=inline&size=<%= file[2] %>">log</a>
+                    </span>
+                  <% end %>
                 <% end %>
               <% end %>
             <% end %>
           <% end %>
-        <% end %>
-      </td><td>
-        <%= render(partial: 'job_status_label',
-                               locals: { :j => pj[:job] }) %>
-      </td><td>
-        <%= link_to_if_arvados_object pj[:output], {:thumbnail => true} %>
-      </td>
-    </tr>
-    <% end %>
-  </tbody>
-  <tfoot>
-    <tr><td colspan="5"></td></tr>
-  </tfoot>
-</table>
-
-<% if @object.state == 'RunningOnServer' || @object.state == 'RunningOnClient' %>
-<% content_for :js do %>
-setInterval(function(){$('a.refresh').click()}, 15000);
-<% end %>
-
-<% content_for :tab_line_buttons do %>
-  <%= form_tag @object, :method => :put do |f| %>
+        </td><td>
+          <%= render(partial: 'job_status_label',
+                                 locals: { :j => pj[:job] }) %>
+        </td><td>
+          <%= link_to_if_arvados_object pj[:output], {:thumbnail => true} %>
+        </td>
+      </tr>
+      <% end %>
+    </tbody>
+    <tfoot>
+      <tr><td colspan="5"></td></tr>
+    </tfoot>
+  </table>
 
-    <%= hidden_field @object.class.to_s.underscore.singularize.to_sym, :state, :value => 'Paused' %>
+  <% if @object.state.in? %w(RunningOnServer RunningOnClient) %>
 
-    <%= button_tag({class: 'btn btn-primary pull-right run-pipeline-button'}) do %>
-      Stop <i class="fa fa-fw fa-stop"></i>
+    <% content_for :js do %>
+      setInterval(function(){$('a.refresh').click()}, 15000);
     <% end %>
-  <% end %>
-<% end %>
 
     <% if !pipeline_job_uuids.empty? %>
       <h4>Log messages from running jobs</h4>
@@ -117,21 +128,22 @@ setInterval(function(){$('a.refresh').click()}, 15000);
       </div>
       <div class="arv-log-event-listener arv-log-event-handler-append-logs" id="pipeline_event_log_div" data-object-uuids="<%=pipeline_job_uuids.join(" ")%>"/>
     <% end %>
+
   <% end %>
 
-<% else %>    <%# State new or ready or paused %>
-  <% if @object.state == 'New' %>
+<% else %>
+  <%# state is either New or Ready %>
+
+  <% if @object.state.in? %w(New Ready) %>
     <p><i>Here are all of the pipeline's components (jobs that will need to run in order to complete the pipeline). If you know what you're doing (or you're experimenting) you can modify these parameters before starting the pipeline. Usually, you only need to edit the settings presented on the "Inputs" tab above.</i></p>
   <% end %>
 
   <% content_for :tab_line_buttons do %>
-    <%= form_tag @object, :method => :put do |f| %>
-
-      <%= hidden_field @object.class.to_s.underscore.singularize.to_sym, :state, :value => 'RunningOnServer' %>
-
-      <%= button_tag({class: 'btn btn-primary pull-right run-pipeline-button'}) do %>
-        Run <i class="fa fa-fw fa-play"></i>
-      <% end %>
+    <%= link_to(url_for('pipeline_instance[state]' => 'RunningOnServer'),
+        class: 'btn btn-primary run-pipeline-button',
+        method: :patch
+        ) do %>
+      Run <i class="fa fa-fw fa-play"></i>
     <% end %>
   <% end %>
 
index 556e9f81ed3658fe14806d16310c4205d32f6e12..5a867286c0deffdb0eac6064c0bc15b3ac55f3e6 100644 (file)
   <p><i>Provide <%= n_inputs > 1 ? 'values' : 'a value' %> for the following <%= n_inputs > 1 ? 'parameters' : 'parameter' %>, then click the "Run" button to start the pipeline.</i></p>
   <%= content_for :pi_input_form %>
 
-  <%= form_tag @object, :method => :put do |f| %>
-    <%= hidden_field @object.class.to_s.underscore.singularize.to_sym, :state, :value => 'RunningOnServer' %>
-    <%= button_tag({class: 'btn btn-primary run-pipeline-button'}) do %>
-      Run <i class="fa fa-fw fa-play"></i>
-    <% end %>
+  <%= link_to(url_for('pipeline_instance[state]' => 'RunningOnServer'),
+      class: 'btn btn-primary run-pipeline-button',
+      method: :patch
+      ) do %>
+    Run <i class="fa fa-fw fa-play"></i>
   <% end %>
 
 <% end %>
index 2f51d5a3fabd6570e5091067d45129247e8781fa..6ffd6ae99675171dfd309f05002c3c6e0c5b934c 100644 (file)
@@ -1,10 +1,10 @@
 <% @objects.each do |object| %>
   <div class="row filterable selectable <%= 'multiple' if multiple %>" data-object-uuid="<%= object.uuid %>">
-    <div class="col-md-6" style="overflow-x:hidden">
+    <div class="col-sm-12" style="overflow-x:hidden">
       <i class="fa fa-fw fa-gear"></i>
       <%= object.name %>
     </div>
-    <div class="col-md-6 arv-description-in-table">
+    <div class="col-sm-11 col-sm-push-1 arv-description-in-table">
       <%= object.description %>
     </div>
   </div>
index 94e2a115293a86090a484a74b5516a0c6aba7e40..971b0ce7307c0ee6fc8dd6443fe4bff966aa891f 100644 (file)
@@ -40,6 +40,7 @@ ArvadosWorkbench::Application.routes.draw do
   end
   resources :pipeline_instances do
     get 'compare', on: :collection
+    post 'copy', on: :member
   end
   resources :links
   get '/collections/graph' => 'collections#graph'