[ARVADOS] created: 981d800af263a756c509e2597c4f99bacd00a302
Git user
git at public.curoverse.com
Mon Aug 22 16:22:04 EDT 2016
at 981d800af263a756c509e2597c4f99bacd00a302 (commit)
commit 981d800af263a756c509e2597c4f99bacd00a302
Author: Peter Amstutz <peter.amstutz at curoverse.com>
Date: Mon Aug 22 16:21:59 2016 -0400
9043: Complete support for editing basic parameter types.
Supported parameter types: boolean, string, int, long, float, double, enum,
File, Directory. May be specified required or optional.
Not supported: arrays, unions (other than with "null"), records.
diff --git a/apps/workbench/app/controllers/container_requests_controller.rb b/apps/workbench/app/controllers/container_requests_controller.rb
index ff6105f..34f892a 100644
--- a/apps/workbench/app/controllers/container_requests_controller.rb
+++ b/apps/workbench/app/controllers/container_requests_controller.rb
@@ -23,22 +23,32 @@ class ContainerRequestsController < ApplicationController
def update
@updates ||= params[@object.class.to_s.underscore.singularize.to_sym]
- input_obj = @updates[:mounts][:"/var/lib/cwl/cwl.input.json"][:content]
- workflow = @object.mounts[:"/var/lib/cwl/workflow.json"][:content]
- workflow[:inputs].each do |input_schema|
- if input_obj.include? input_schema[:id]
- required, primary_type, param_id = cwl_input_info(input_schema)
- if primary_type == "boolean"
- input_obj[param_id] = input_obj[param_id] == "true"
- elsif ["int", "long"].include? primary_type
- input_obj[param_id] = input_obj[param_id].to_i
- elsif ["float", "double"].include? primary_type
- input_obj[param_id] = input_obj[param_id].to_f
- elsif ["File", "Directory"].include? primary_type
- input_obj[param_id] = {"class" => "File", "location" => "keep:" + input_obj[param_id]}
+ input_obj = @updates[:mounts].andand[:"/var/lib/cwl/cwl.input.json"].andand[:content]
+ if input_obj
+ workflow = @object.mounts[:"/var/lib/cwl/workflow.json"][:content]
+ workflow[:inputs].each do |input_schema|
+ if input_obj.include? input_schema[:id]
+ required, primary_type, param_id = cwl_input_info(input_schema)
+ if input_obj[param_id] == ""
+ input_obj[param_id] = nil
+ elsif primary_type == "boolean"
+ input_obj[param_id] = input_obj[param_id] == "true"
+ elsif ["int", "long"].include? primary_type
+ input_obj[param_id] = input_obj[param_id].to_i
+ elsif ["float", "double"].include? primary_type
+ input_obj[param_id] = input_obj[param_id].to_f
+ elsif ["File", "Directory"].include? primary_type
+ input_obj[param_id].match /^([0-9a-z]{5}-([0-9a-z]{5})-[0-9a-z]{15})(\/.*)?$/ do |re|
+ c = display_value = Collection.find(re[1])
+ input_obj[param_id] = {"class" => primary_type,
+ "location" => "keep:#{c.portable_data_hash}#{re[3]}",
+ "arv:collection" => input_obj[param_id]}
+ end
+ end
end
end
end
+ params[:merge] = true
super
end
diff --git a/apps/workbench/app/helpers/application_helper.rb b/apps/workbench/app/helpers/application_helper.rb
index a82c795..27c7504 100644
--- a/apps/workbench/app/helpers/application_helper.rb
+++ b/apps/workbench/app/helpers/application_helper.rb
@@ -424,6 +424,8 @@ module ApplicationHelper
primary_type = input_schema[:type].select { |n| n != "null" }[0]
elsif input_schema[:type].is_a? String
primary_type = input_schema[:type]
+ elsif input_schema[:type].is_a? Hash
+ primary_type = input_schema[:type]
end
param_id = input_schema[:id]
return required, primary_type, param_id
@@ -453,14 +455,25 @@ module ApplicationHelper
required, primary_type, param_id = cwl_input_info(input_schema)
dn, attrvalue = cwl_input_value(object, input_schema, set_attr_path + [param_id])
- attrvalue ||= ""
+ attrvalue = if attrvalue.nil? then "" else attrvalue end
id = "#{object.uuid}-#{param_id}"
+ opt_empty_selection = if required then [] else [{value: "", text: ""}] end
+
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
+ if attrvalue.is_a? Hash
+ display_value = attrvalue[:"arv:collection"] || attrvalue[:location]
+ display_value.match /^([0-9a-z]{5}-([0-9a-z]{5})-[0-9a-z]{15})(\/.*)?$/ do |re|
+ if re[3]
+ display_value = "#{Collection.find(re[1]).name} / #{re[3][1..-1]}"
+ else
+ display_value = Collection.find(re[1]).name
+ end
+ end
+ end
modal_path = choose_collections_path \
({ title: chooser_title,
filters: [['owner_uuid', '=', object.owner_uuid]].to_json,
@@ -494,7 +507,7 @@ module ApplicationHelper
"data-emptytext" => "none",
"data-placement" => "bottom",
"data-type" => "select",
- "data-source" => "[{value: true, text: \"true\"}, {value: false, text: \"false\"}]",
+ "data-source" => (opt_empty_selection + [{value: "true", text: "true"}, {value: "false", text: "false"}]).to_json,
"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,
@@ -505,9 +518,23 @@ module ApplicationHelper
:class => "editable #{'required' if required} form-control",
:id => id
}.merge(htmloptions)
- elsif "enum" == primary_type
-
- else
+ elsif primary_type.is_a? Hash and primary_type[:type] == "enum"
+ return link_to attrvalue, '#', {
+ "data-emptytext" => "none",
+ "data-placement" => "bottom",
+ "data-type" => "select",
+ "data-source" => (opt_empty_selection + primary_type[:symbols].map {|i| {:value => i, :text => i} }).to_json,
+ "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)
+ elsif primary_type.is_a? String
if ["float", "double", "int", "long"].include? primary_type
datatype = "number"
else
@@ -527,7 +554,9 @@ module ApplicationHelper
"data-clear" => false,
:class => "editable #{'required' if required} form-control",
:id => id
- }.merge(htmloptions)
+ }.merge(htmloptions)
+ else
+ return "Unable to render editing control for parameter type #{primary_type}"
end
end
diff --git a/apps/workbench/app/views/container_requests/_show_inputs.html.erb b/apps/workbench/app/views/container_requests/_show_inputs.html.erb
index 87b36c1..b365dc5 100644
--- a/apps/workbench/app/views/container_requests/_show_inputs.html.erb
+++ b/apps/workbench/app/views/container_requests/_show_inputs.html.erb
@@ -22,19 +22,19 @@
<% 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>
+ <p><i>This workflow does not need any further inputs specified. Click the "Run" button at the bottom of the page to start the workflow.</i></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 %>
+<% 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 %>
+
+<%= render_unreadable_inputs_present %>
commit 7db2c888db26814095ace7908645deed1035f360
Author: Peter Amstutz <peter.amstutz at curoverse.com>
Date: Fri Aug 19 17:33:18 2016 -0400
9043: Fixup incoming values from x-editable to have correct types. Work in progress.
diff --git a/apps/workbench/app/controllers/container_requests_controller.rb b/apps/workbench/app/controllers/container_requests_controller.rb
index b82dbbc..ff6105f 100644
--- a/apps/workbench/app/controllers/container_requests_controller.rb
+++ b/apps/workbench/app/controllers/container_requests_controller.rb
@@ -20,4 +20,26 @@ class ContainerRequestsController < ApplicationController
redirect_to @object
end
end
+
+ def update
+ @updates ||= params[@object.class.to_s.underscore.singularize.to_sym]
+ input_obj = @updates[:mounts][:"/var/lib/cwl/cwl.input.json"][:content]
+ workflow = @object.mounts[:"/var/lib/cwl/workflow.json"][:content]
+ workflow[:inputs].each do |input_schema|
+ if input_obj.include? input_schema[:id]
+ required, primary_type, param_id = cwl_input_info(input_schema)
+ if primary_type == "boolean"
+ input_obj[param_id] = input_obj[param_id] == "true"
+ elsif ["int", "long"].include? primary_type
+ input_obj[param_id] = input_obj[param_id].to_i
+ elsif ["float", "double"].include? primary_type
+ input_obj[param_id] = input_obj[param_id].to_f
+ elsif ["File", "Directory"].include? primary_type
+ input_obj[param_id] = {"class" => "File", "location" => "keep:" + input_obj[param_id]}
+ end
+ end
+ end
+ super
+ end
+
end
diff --git a/apps/workbench/app/helpers/application_helper.rb b/apps/workbench/app/helpers/application_helper.rb
index e364cd9..a82c795 100644
--- a/apps/workbench/app/helpers/application_helper.rb
+++ b/apps/workbench/app/helpers/application_helper.rb
@@ -425,41 +425,34 @@ module ApplicationHelper
elsif input_schema[:type].is_a? String
primary_type = input_schema[:type]
end
- return required, primary_type
+ param_id = input_schema[:id]
+ return required, primary_type, param_id
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]
+ attrvalue = attrvalue[a.to_sym]
end
- dn += "[#{param_id}]"
- attrvalue = attrvalue[param_id.to_sym]
- return dn, attrvalue, param_id
+ return dn, attrvalue
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)
+ required, primary_type, param_id = cwl_input_info(input)
+ dn, attrvalue = cwl_input_value(object, input, set_attr_path + [param_id])
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
+ required, primary_type, param_id = cwl_input_info(input_schema)
- dn, attrvalue, param_id = cwl_input_value(object, input_schema, set_attr_path)
+ dn, attrvalue = cwl_input_value(object, input_schema, set_attr_path + [param_id])
attrvalue ||= ""
id = "#{object.uuid}-#{param_id}"
@@ -496,7 +489,31 @@ module ApplicationHelper
})
end
end
+ elsif "boolean" == primary_type
+ return link_to attrvalue, '#', {
+ "data-emptytext" => "none",
+ "data-placement" => "bottom",
+ "data-type" => "select",
+ "data-source" => "[{value: true, text: \"true\"}, {value: false, text: \"false\"}]",
+ "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)
+ elsif "enum" == primary_type
+
else
+ if ["float", "double", "int", "long"].include? primary_type
+ datatype = "number"
+ else
+ datatype = "text"
+ end
+
return link_to attrvalue, '#', {
"data-emptytext" => "none",
"data-placement" => "bottom",
commit ca2b19520d90617d8de643f6a0ea741b94846632
Author: Peter Amstutz <peter.amstutz at curoverse.com>
Date: Fri Aug 19 16:36:46 2016 -0400
9043: Initial work on rendering input forms for CWL workflows. Works for
simple parameters. Successfully writes back to container request object.
diff --git a/apps/workbench/app/controllers/container_requests_controller.rb b/apps/workbench/app/controllers/container_requests_controller.rb
index f5a68fe..b82dbbc 100644
--- a/apps/workbench/app/controllers/container_requests_controller.rb
+++ b/apps/workbench/app/controllers/container_requests_controller.rb
@@ -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
diff --git a/apps/workbench/app/helpers/application_helper.rb b/apps/workbench/app/helpers/application_helper.rb
index a37ecda..e364cd9 100644
--- a/apps/workbench/app/helpers/application_helper.rb
+++ b/apps/workbench/app/helpers/application_helper.rb
@@ -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
index 0000000..87b36c1
--- /dev/null
+++ b/apps/workbench/app/views/container_requests/_show_inputs.html.erb
@@ -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 %>
commit 1f1db46a139e93a0a4528aabcea2efea9cd8c7aa
Author: Peter Amstutz <peter.amstutz at curoverse.com>
Date: Thu Aug 18 15:16:07 2016 -0400
Test fixture for uncommitted container request.
diff --git a/services/api/test/fixtures/container_requests.yml b/services/api/test/fixtures/container_requests.yml
index de41ff0..22d7451 100644
--- a/services/api/test/fixtures/container_requests.yml
+++ b/services/api/test/fixtures/container_requests.yml
@@ -151,6 +151,114 @@ running_anonymous_accessible:
vcpus: 1
ram: 123
+uncommitted:
+ uuid: zzzzz-xvhdp-cr4uncommittedc
+ owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+ name: uncommitted
+ created_at: 2016-01-11 11:11:11.111111111 Z
+ updated_at: 2016-01-11 11:11:11.111111111 Z
+ modified_at: 2016-01-11 11:11:11.111111111 Z
+ modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+ command: ["arvados-cwl-runner", "--local", "--api=containers",
+ "/var/lib/cwl/workflow.json", "/var/lib/cwl/cwl.input.json"]
+ output_path: "/var/spool/cwl"
+ cwd: "/var/spool/cwl"
+ priority: 1
+ state: "Uncommitted"
+ container_image: arvados/jobs
+ mounts: {
+ "/var/lib/cwl/workflow.json": {
+ "kind": "json",
+ "content": {
+ "cwlVersion": "v1.0",
+ "class": "CommandLineTool",
+ "inputs": [
+ {
+ "doc": "a longer documentation string for this parameter (optional)",
+ "type": "boolean",
+ "id": "ex_boolean",
+ "label": "a short label for this parameter (optional)"
+ },
+ {
+ "doc": "directory selection should present the workbench collection picker",
+ "type": "Directory",
+ "id": "ex_dir"
+ },
+ {
+ "type": "double",
+ "id": "ex_double"
+ },
+ {
+ "doc": "file selection should present the workbench file picker",
+ "type": "File",
+ "id": "ex_file"
+ },
+ {
+ "doc": "any parameter can be turned into an array with {\"type\": \"array\", \"items\": \"the_type\"}\n",
+ "type": {
+ "items": "File",
+ "type": "array"
+ },
+ "id": "ex_file_array"
+ },
+ {
+ "type": "float",
+ "id": "ex_float"
+ },
+ {
+ "type": "int",
+ "id": "ex_int"
+ },
+ {
+ "type": "long",
+ "id": "ex_long"
+ },
+ {
+ "doc": "any parameter can be turned into an optional array with [\"null\", {\"type\": \"array\", \"items\": \"the_type\"}\n",
+ "type": [
+ "null",
+ {
+ "items": "File",
+ "type": "array"
+ }
+ ],
+ "id": "ex_opt_file_array"
+ },
+ {
+ "doc": "any parameter can be made optional by providing a union of [\"null\", \"the_type\"]\n",
+ "type": [
+ "null",
+ "string"
+ ],
+ "id": "ex_opt_string"
+ },
+ {
+ "type": "string",
+ "id": "ex_string"
+ }
+ ],
+ "outputs": []
+ },
+ },
+ "/var/lib/cwl/cwl.input.json": {
+ "kind": "json",
+ "content": {}
+ },
+ "stdout": {
+ "kind": "file",
+ "path": "/var/spool/cwl/cwl.output.json"
+ },
+ "/var/spool/cwl": {
+ "kind": "collection",
+ "writable": True
+ }
+ }
+ runtime_constraints:
+ vcpus: 1
+ ram: 256000000
+ API: true
+
+
# Test Helper trims the rest of the file
# Do not add your fixtures below this line as the rest of this file will be trimmed by test_helper
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list