9043: Initial work on rendering input forms for CWL workflows. Works for
authorPeter Amstutz <peter.amstutz@curoverse.com>
Fri, 19 Aug 2016 20:36:46 +0000 (16:36 -0400)
committerPeter Amstutz <peter.amstutz@curoverse.com>
Thu, 25 Aug 2016 15:58:10 +0000 (11:58 -0400)
simple parameters.  Successfully writes back to container request object.

apps/workbench/app/controllers/container_requests_controller.rb
apps/workbench/app/helpers/application_helper.rb
apps/workbench/app/views/container_requests/_show_inputs.html.erb [new file with mode: 0644]

index f5a68fec27eb00f64e1d027d29c3a4079dd0687b..b82dbbc84118a2cf255b32709bc49584539d51b6 100644 (file)
@@ -5,7 +5,11 @@ class ContainerRequestsController < ApplicationController
   }
 
   def show_pane_list
-    %w(Status Log Advanced)
+    panes = %w(Status Log Graph Advanced)
+    if @object and @object.state == 'Uncommitted'
+      panes = %w(Inputs) + panes - %w(Log)
+    end
+    panes
   end
 
   def cancel
index a37ecda7041c99ff820b59bd7874d5e8f5b7e9e4..e364cd9a0f317f205f4f8653e5a23f20b24c6b12 100644 (file)
@@ -418,6 +418,102 @@ module ApplicationHelper
     lt
   end
 
+  def cwl_input_info(input_schema)
+    required = !(input_schema[:type].include? "null")
+    if input_schema[:type].is_a? Array
+      primary_type = input_schema[:type].select { |n| n != "null" }[0]
+    elsif input_schema[:type].is_a? String
+      primary_type = input_schema[:type]
+    end
+    return required, primary_type
+  end
+
+  def cwl_input_value(object, input_schema, set_attr_path)
+    param_id = input_schema[:id]
+    dn = ""
+    attrvalue = object
+    set_attr_path.each do |a|
+      dn += "[#{a}]"
+      attrvalue = attrvalue[a]
+    end
+    dn += "[#{param_id}]"
+    attrvalue = attrvalue[param_id.to_sym]
+    return dn, attrvalue, param_id
+  end
+
+  def cwl_inputs_required(object, inputs_schema, set_attr_path)
+    r = 0
+    inputs_schema.each do |input|
+      required, primary_type = cwl_input_info(input)
+      dn, attrvalue = cwl_input_value(object, input, set_attr_path)
+      r += 1 if required and attrvalue.nil?
+    end
+    r
+  end
+
+  def render_cwl_input(object, input_schema, set_attr_path, htmloptions={})
+    required, primary_type = cwl_input_info(input_schema)
+    if ["float", "double", "int", "long"].include? primary_type
+      datatype = "number"
+    else
+      datatype = "text"
+    end
+
+    dn, attrvalue, param_id = cwl_input_value(object, input_schema, set_attr_path)
+    attrvalue ||= ""
+
+    id = "#{object.uuid}-#{param_id}"
+
+    if ["Directory", "File"].include? primary_type
+      chooser_title = "Choose a #{primary_type == 'Directory' ? 'dataset' : 'file'}:"
+      selection_param = object.class.to_s.underscore + dn
+      display_value = attrvalue
+      modal_path = choose_collections_path \
+      ({ title: chooser_title,
+         filters: [['owner_uuid', '=', object.owner_uuid]].to_json,
+         action_name: 'OK',
+         action_href: container_request_path(id: object.uuid),
+         action_method: 'patch',
+         preconfigured_search_str: "",
+         action_data: {
+           merge: true,
+           use_preview_selection: primary_type == 'File' ? true : nil,
+           selection_param: selection_param,
+           success: 'page-refresh'
+         }.to_json,
+        })
+
+      return content_tag('div', :class => 'input-group') do
+        html = text_field_tag(dn, display_value,
+                              :class =>
+                              "form-control #{'required' if required}")
+        html + content_tag('span', :class => 'input-group-btn') do
+          link_to('Choose',
+                  modal_path,
+                  { :class => "btn btn-primary",
+                    :remote => true,
+                    :method => 'get',
+                  })
+        end
+      end
+    else
+      return link_to attrvalue, '#', {
+                     "data-emptytext" => "none",
+                     "data-placement" => "bottom",
+                     "data-type" => datatype,
+                     "data-url" => url_for(action: "update", id: object.uuid, controller: object.class.to_s.pluralize.underscore, merge: true),
+                     "data-title" => "Set value for #{input_schema[:id]}",
+                     "data-name" => dn,
+                     "data-pk" => "{id: \"#{object.uuid}\", key: \"#{object.class.to_s.underscore}\"}",
+                     "data-value" => attrvalue,
+                     # "clear" button interferes with form-control's up/down arrows
+                     "data-clear" => false,
+                     :class => "editable #{'required' if required} form-control",
+                     :id => id
+                   }.merge(htmloptions)
+    end
+  end
+
   def render_arvados_object_list_start(list, button_text, button_href,
                                        params={}, *rest, &block)
     show_max = params.delete(:show_max) || 3
diff --git a/apps/workbench/app/views/container_requests/_show_inputs.html.erb b/apps/workbench/app/views/container_requests/_show_inputs.html.erb
new file mode 100644 (file)
index 0000000..87b36c1
--- /dev/null
@@ -0,0 +1,40 @@
+<% n_inputs = cwl_inputs_required(@object, @object.mounts[:"/var/lib/cwl/workflow.json"][:content][:inputs], [:mounts, :"/var/lib/cwl/cwl.input.json", :content]) %>
+
+<% content_for :pi_input_form do %>
+<form role="form" style="width:60%">
+  <div class="form-group">
+    <% workflow = @object.mounts[:"/var/lib/cwl/workflow.json"][:content] %>
+    <% workflow[:inputs].each do |input| %>
+      <label for="#input-<%= input[:id] %>">
+        <%= input[:label] || input[:id] %>
+      </label>
+      <div>
+        <p class="form-control-static">
+          <%= render_cwl_input @object, input, [:mounts, :"/var/lib/cwl/cwl.input.json", :content] %>
+        </p>
+      </div>
+      <p class="help-block">
+        <%= input[:doc] %>
+      </p>
+    <% end %>
+  </div>
+</form>
+<% end %>
+
+<% if n_inputs == 0 %>
+  <p>This workflow does not need any further inputs specified. You can start it by clicking the "Run" button whenever you're ready. (It's not too late to change the settings, though.)</p>
+<% else %>
+  <%= render_unreadable_inputs_present %>
+
+  <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 workflow.</i></p>
+  <% if @object.editable? %>
+    <%= content_for :pi_input_form %>
+      <%= link_to(url_for('container_request[state]' => 'Committed'),
+          class: 'btn btn-primary run-pipeline-button',
+          method: :patch
+          ) do %>
+        Run <i class="fa fa-fw fa-play"></i>
+    <% end %>
+  <% end %>
+
+<% end %>