Diff
Modified: branches/datasets/app/controllers/workflows_controller.rb (2798 => 2799)
--- branches/datasets/app/controllers/workflows_controller.rb 2011-11-10 14:18:07 UTC (rev 2798)
+++ branches/datasets/app/controllers/workflows_controller.rb 2011-11-10 14:35:11 UTC (rev 2799)
@@ -20,6 +20,8 @@
before_filter :check_custom_workflow_type, : [:create, :create_version]
before_filter :check_is_owner, : [:edit, :update]
+
+ before_filter :get_example_data_sets, : [:tag_suggestions, :extra_metadata]
# declare sweepers and which actions should invoke them
cache_sweeper :workflow_sweeper, : [ :create, :create_version, :launch, :update, :update_version, :destroy_version, :destroy ]
@@ -637,11 +639,20 @@
end
def tag_suggestions
+
@suggestions = @workflow.get_tag_suggestions
+
end
+ def extra_metadata
+ end
+
def process_tag_suggestions
+ def form_name(*bits)
+ bits.join("_")
+ end
+
if params[:workflow] && params[:workflow][:body]
@workflow.body = params[:workflow][:body]
@workflow.save
@@ -651,6 +662,184 @@
@workflow.add_tag(tag.strip, current_user)
end
+ begin
+
+ # Fetch the existing data structure
+
+ data_sets = DataSet.find(:all,
+ :conditions => ["contributable_type = ? AND contributable_id = ? AND category = ? AND user_id = ?",
+ @workflow.class.name, @workflow.id, 'example', current_user])
+
+ # Parse form data
+
+ form_data = (0..params[form_name(["ds", "count"])].to_i - 1).map do |s|
+ {
+ ".id" => params[form_name(["ds", s, ".id"])],
+ "title" => params[form_name(["ds", s, "title"])],
+ "desc" => params[form_name(["ds", s, "desc"])],
+
+ "inputs" => (0..params[form_name(["ds", s, "input", "count"])].to_i - 1).map do |i|
+ { ".id" => params[form_name(["ds", s, "input", i, ".id"])],
+ "label" => params[form_name(["ds", s, "input", i, "label"])],
+ "data" => params[form_name(["ds", s, "input", i, "data"])]
+ }
+ end,
+
+ "outputs" => (0..params[form_name(["ds", s, "output", "count"])].to_i - 1).map do |o|
+ { ".id" => params[form_name(["ds", s, "output", o, ".id"])],
+ "label" => params[form_name(["ds", s, "output", o, "label"])],
+ "data" => params[form_name(["ds", s, "output", o, "data"])]
+ }
+ end
+ }
+ end
+
+ # Update the data structure
+
+ pre_ds_ids = data_sets.map do |ds| ds.id end
+ post_ds_ids = (form_data.map do |ds| ds[".id"] end - [nil]).map do |id| id.to_i end
+
+ # Update data sets
+
+ data_sets.each do |ds_ob|
+
+ if post_ds_ids.include?(ds_ob.id)
+
+ form_ds = form_data.select do |ds| ds[".id"] && (ds[".id"].to_i == ds_ob.id) end.first
+
+ update_ds = false
+
+ if ds_ob.title != form_ds["title"]
+ ds_ob.title = form_ds["title"]
+ updated_ds = true
+ end
+
+ if ds_ob.description != form_ds["desc"]
+ ds_ob.description = form_ds["desc"]
+ updated_ds = true
+ end
+
+ input_obs = ds_ob.data_items.select do |di| di.category == "input" end
+ output_obs = ds_ob.data_items.select do |di| di.category == "output" end
+
+ pre_input_ids = input_obs.map do |di| di.id end
+ pre_output_ids = output_obs.map do |di| di.id end
+
+ post_input_ids = (form_ds["inputs"].map do |input| input[".id"] end - [nil]).map do |input| input.to_i end
+
+ post_output_ids = (form_ds["outputs"].map do |output| output[".id"] end - [nil]).map do |output| output.to_i end
+
+ # Update inputs
+
+ input_obs.each do |input_ob|
+
+ if post_input_ids.include?(input_ob.id)
+
+ updated_input = false
+
+ form_input = form_ds["inputs"].select do |input| input[".id"] && (input[".id"].to_i == input_ob.id) end.first
+
+ if input_ob.label != form_input["label"]
+ input_ob.label = form_input["label"]
+ updated_input = true
+ end
+
+ if input_ob.data != form_input["data"]
+ input_ob.data = ""
+ updated_input = true
+ end
+
+ if updated_input
+ input_ob.save
+ updated_ds = true
+ end
+ end
+ end
+
+ # Create inputs
+
+ form_ds["inputs"].select do |input| input[".id"].nil? end.each do |input|
+ DataItem.create(:data_set => ds_ob, :category => "input",
+ :label => input["label"], :data ="" input["data"])
+ end
+
+ # Delete inputs
+
+ (pre_input_ids - post_input_ids).each do |id|
+ DataItem.destroy(id)
+ end
+
+ # Update outputs
+
+ output_obs.each do |output_ob|
+
+ if post_output_ids.include?(output_ob.id)
+
+ updated_output = false
+
+ form_output = form_ds["outputs"].select do |output| output[".id"] && (output[".id"].to_i == output_ob.id) end.first
+
+ if output_ob.label != form_output["label"]
+ output_ob.label = form_output["label"]
+ updated_output = true
+ end
+
+ if output_ob.data != form_output["data"]
+ output_ob.data = ""
+ updated_output = true
+ end
+
+ if updated_output
+ output_ob.save
+ updated_ds = true
+ end
+ end
+ end
+
+ # Create outputs
+
+ form_ds["outputs"].select do |output| output[".id"].nil? end.each do |output|
+ DataItem.create(:data_set => ds_ob, :category => "output",
+ :label => output["label"], :data ="" output["data"])
+ end
+
+ # Delete outputs
+
+ (pre_output_ids - post_output_ids).each do |id|
+ DataItem.destroy(id)
+ end
+
+ ds_ob.save if updated_ds
+ end
+ end
+
+ # Create data sets
+
+ form_data.select do |ds| ds[".id"].nil? end.each do |ds|
+
+ ds_ob = DataSet.create(:contributable => @workflow, :user => current_user,
+ :category => 'example', :title => ds["title"], :description => ds["desc"])
+
+ ds["inputs"].each do |input|
+ DataItem.create(:data_set => ds_ob, :category => "input",
+ :label => input["label"], :data ="" input["data"])
+ end
+
+ ds["outputs"].each do |output|
+ DataItem.create(:data_set => ds_ob, :category => "output",
+ :label => output["label"], :data ="" output["data"])
+ end
+ end
+
+ # Delete data sets
+
+ (pre_ds_ids - post_ds_ids).each do |id|
+ DataSet.destroy(id)
+ end
+
+ rescue
+ end
+
redirect_to(workflow_url(@workflow))
end
@@ -1054,5 +1243,29 @@
return ok
end
+ def get_example_data_sets
+
+ @data_sets = DataSet.find(:all,
+ :conditions => ["contributable_type = ? AND contributable_id = ? AND category = ? AND user_id = ?",
+ @workflow.class.name, @workflow.id, 'example', current_user.id]).map do |data_set|
+
+ { "title" => data_set.title,
+ "desc" => data_set.description,
+ ".id" => data_set.id,
+
+ "inputs" => data_set.data_items.select do |item|
+ item.category == 'input'
+ end.map do |input|
+ { ".id" => input.id, "label" => input.label, "data" => input.data }
+ end,
+
+ "outputs" => data_set.data_items.select do |item|
+ item.category == 'output'
+ end.map do |output|
+ { ".id" => output.id, "label" => output.label, "data" => output.data }
+ end
+ }
+ end
+ end
end
Added: branches/datasets/app/views/workflows/_data_sets_form.rhtml (0 => 2799)
--- branches/datasets/app/views/workflows/_data_sets_form.rhtml (rev 0)
+++ branches/datasets/app/views/workflows/_data_sets_form.rhtml 2011-11-10 14:35:11 UTC (rev 2799)
@@ -0,0 +1,6 @@
+<div id="data_sets"></div>
+
+<input type="button" return false;" value="Add new set of example inputs / outputs" />
+
+<script type="text/_javascript_" src=""
+<script type="text/_javascript_">renderDataSets(<%= data_sets.to_json %>)</script>
Added: branches/datasets/app/views/workflows/extra_metadata.rhtml (0 => 2799)
--- branches/datasets/app/views/workflows/extra_metadata.rhtml (rev 0)
+++ branches/datasets/app/views/workflows/extra_metadata.rhtml 2011-11-10 14:35:11 UTC (rev 2799)
@@ -0,0 +1,14 @@
+<%= _javascript__include_tag :fckeditor %>
+
+<h1>Example inputs / outputs</h1>
+
+<form id="metadata" action="" "/workflows/address@hidden/process_extra_metadata" %>" method="post">
+
+ <%= render :partial => "data_sets_form", :locals => { :data_sets => @data_sets, :default_ports => @default_ports } %>
+
+ <p style="text-align: center">
+ <input id="submit-button" type="submit" value="Update" />
+ </p>
+
+</form>
+
Modified: branches/datasets/app/views/workflows/tag_suggestions.rhtml (2798 => 2799)
--- branches/datasets/app/views/workflows/tag_suggestions.rhtml 2011-11-10 14:18:07 UTC (rev 2798)
+++ branches/datasets/app/views/workflows/tag_suggestions.rhtml 2011-11-10 14:35:11 UTC (rev 2799)
@@ -3,7 +3,7 @@
<h1>Extra workflow metadata</h1>
-<form action="" "/workflows/address@hidden/process_tag_suggestions" %>" method="post">
+<form id="metadata" action="" "/workflows/address@hidden/process_tag_suggestions" %>" method="post">
<% if @workflow.body.nil? || @workflow.body == "" %>
@@ -38,6 +38,10 @@
<div id="summary-text"></div>
+ <h1>Example inputs / outputs</h1>
+
+ <%= render :partial => "data_sets_form", :locals => { :data_sets => @data_sets, :default_ports => @default_ports } %>
+
<h2>Finish</h2>
<p>Complete the upload process.</p>
Modified: branches/datasets/config/routes.rb (2798 => 2799)
--- branches/datasets/config/routes.rb 2011-11-10 14:18:07 UTC (rev 2798)
+++ branches/datasets/config/routes.rb 2011-11-10 14:35:11 UTC (rev 2799)
@@ -118,6 +118,8 @@
:destroy_version => :delete,
:edit_version => :get,
:update_version => :put,
+ :process_extra_metadata => :post,
+ :extra_metadata => :get,
:process_tag_suggestions => :post,
:tag_suggestions => :get } do |workflow|
# workflows have nested citations
Modified: branches/datasets/config/schema.d/workflows.xml (2798 => 2799)
--- branches/datasets/config/schema.d/workflows.xml 2011-11-10 14:18:07 UTC (rev 2798)
+++ branches/datasets/config/schema.d/workflows.xml 2011-11-10 14:35:11 UTC (rev 2799)
@@ -51,6 +51,39 @@
</table>
+ <table name="data_sets">
+
+ <column type="integer" name="user_id"/>
+ <column type="string" name="contributable_type"/>
+ <column type="integer" name="contributable_id"/>
+ <column type="datetime" name="created_at"/>
+ <column type="datetime" name="updated_at"/>
+ <column type="text" name="title"/>
+ <column type="text" name="description"/>
+ <column type="text" name="description_html"/>
+ <column type="text" name="category"/>
+
+ <belongs-to target="users"/>
+ <belongs-to target="contributable" polymorphic="true"/>
+
+ <has-many target="data_items" foreign_key="data_set_id" dependent="destroy"/>
+
+ </table>
+
+ <table name="data_items">
+
+ <column type="integer" name="data_set_id"/>
+ <column type="datetime" name="created_at"/>
+ <column type="datetime" name="updated_at"/>
+ <column type="text" name="category"/>
+ <column type="text" name="label"/>
+ <column type="text" name="data"/>
+
+ <belongs-to target="users"/>
+ <belongs-to target="data_sets"/>
+
+ </table>
+
<table name="workflow_processors">
<column type="integer" name="workflow_id"/>
Added: branches/datasets/public/_javascript_s/data_sets.js (0 => 2799)
--- branches/datasets/public/_javascript_s/data_sets.js (rev 0)
+++ branches/datasets/public/_javascript_s/data_sets.js 2011-11-10 14:35:11 UTC (rev 2799)
@@ -0,0 +1,200 @@
+// myExperiment: app/controllers/workflows_controller.rb
+//
+// Copyright (c) 2010 University of Manchester and the University of Southampton.
+// See license.txt for details.
+
+function form_name() {
+ return Array.prototype.slice.call(arguments).join("_");
+}
+
+function saveDataSets() {
+
+ function saveOptionalField(name, key, outputHash) {
+
+ value_el = document.forms["metadata"][name]
+
+ if (value_el == undefined)
+ return;
+
+ outputHash[key] = value_el.value;
+ }
+
+ var data_set_count = parseInt(document.forms["metadata"][form_name("ds", "count")].value)
+
+ var data_sets = [];
+
+ for (var s = 0; s < data_set_count; s++) {
+ var data_set = {
+ "title" : document.forms["metadata"][form_name("ds", s, "title")].value,
+ "desc" : document.forms["metadata"][form_name("ds", s, "desc")].value
+ };
+
+ saveOptionalField(form_name("ds", s, ".id"), ".id", data_set);
+
+ data_set["inputs"] = []
+ data_set["outputs"] = []
+
+ var input_count = parseInt(document.forms["metadata"][form_name("ds", s, "input", "count")].value);
+ var output_count = parseInt(document.forms["metadata"][form_name("ds", s, "output", "count")].value);
+
+ for (var i = 0; i < input_count; i++) {
+
+ input = {
+ "label" : document.forms["metadata"][form_name("ds", s, "input", i, "label")].value,
+ "data" : document.forms["metadata"][form_name("ds", s, "input", i, "data")].value
+ };
+
+ saveOptionalField(form_name("ds", s, "input", i, ".id"), ".id", input);
+
+ data_set["inputs"].push(input);
+ }
+
+ for (var o = 0; o < output_count; o++) {
+
+ output = {
+ "label" : document.forms["metadata"][form_name("ds", s, "output", o, "label")].value,
+ "data" : document.forms["metadata"][form_name("ds", s, "output", o, "data")].value
+ };
+
+ saveOptionalField(form_name("ds", s, "output", o, ".id"), ".id", output)
+
+ data_set["outputs"].push(output);
+ }
+
+ data_sets.push(data_set);
+ }
+
+ return data_sets;
+}
+
+function renderDataSets(data_sets) {
+
+ function renderOptionalField(name, value) {
+ if (value == undefined) {
+ return "";
+ } else {
+ return "<input type='hidden' name='" + name + "' value='" + value + "'></input>";
+ }
+ }
+
+ var markup = "";
+
+ markup += "<input type='hidden' name='" + form_name("ds", "count") + "' value='" + data_sets.length + "'></input>";
+
+ if (data_sets.length == 0) {
+
+ markup += "<p><i>You have not provided any example data sets.</i></p>";
+
+ } else {
+
+ for (var s = 0; s < data_sets.length; s++) {
+
+ var data_set = data_sets[s];
+
+ markup += "<div class=\"form\">";
+ markup += renderOptionalField(form_name("ds", s, ".id"), data_set[".id"]);
+ markup += "<input type='hidden' name='" + form_name("ds", s, "input", "count") + "' value='" + data_set["inputs"].length + "'></input>"
+ markup += "<input type='hidden' name='" + form_name("ds", s, "output", "count") + "' value='" + data_set["outputs"].length + "'></input>"
+
+ markup += " <div class=\"dataset\">";
+ markup += " <div class=\"form-triple\">";
+ markup += " <div class=\"form-label\">Title</div>";
+ markup += " <div class=\"form-data\"><input class=\"dataset-title\" name=\"" + form_name("ds", s, "title") + "\" value=\"" + data_set["title"] + "\" /></div>";
+ markup += " <div class=\"form-action\"></div>";
+ markup += " </div>";
+ markup += " <div class=\"form-triple\">";
+ markup += " <div class=\"form-label\">Description</div>";
+ markup += " <div class=\"form-data\"><textarea class=\"dataset-description\" id=\"" + form_name("ds", s, "desc") + "\" name=\"" + form_name("ds", s, "desc") + "\">" + data_set["desc"] + "</textarea></div>";
+ markup += " <div class=\"form-action\"></div>";
+ markup += " </div>";
+ markup += " <table class=\"dataset-table\">";
+ markup += " <thead>";
+ markup += " <tr>";
+ markup += " <th class=\"form-label\">Input</th>";
+ markup += " <th class=\"form-data\">Value</th>";
+ markup += " </tr>";
+ markup += " </thead>";
+ markup += " <tbody>";
+
+ for (var i = 0; i < data_set["inputs"].length; i++) {
+
+ var input = data_set["inputs"][i];
+
+ markup += renderOptionalField(form_name("ds", s, "input", i, ".id"), input[".id"]);
+
+ markup += " <tr>";
+ markup += " <td class=\"label\"><input class=\"dataset-io-label\" name=\"" + form_name("ds", s, "input", i, "label") + "\" value=\"" + input["label"] + "\" /></td>";
+ markup += " <td class=\"data\"><textarea class=\"dataset-io-data\" name=\"" + form_name("ds", s, "input", i, "data") + "\">" + input["data"] + "</textarea></td>";
+ markup += " <td class=\"action\"><input class=\"dataset-io-action\" type=\"button\" value=\"Delete\" /></td>";
+ markup += " </tr>";
+ }
+
+ markup += " </tbody>";
+ markup += " </table>";
+ markup += " <input type='button' value='Add new input' />";
+ markup += " <table class=\"dataset-table\">";
+ markup += " <thead>";
+ markup += " <tr>";
+ markup += " <th class=\"form-label\">Output</th>";
+ markup += " <th class=\"form-data\">Value</th>";
+ markup += " </tr>";
+ markup += " </thead>";
+ markup += " <tbody>";
+
+ for (var i = 0; i < data_set["outputs"].length; i++) {
+
+ var output = data_set["outputs"][i];
+
+ markup += renderOptionalField(form_name("ds", s, "output", i, ".id"), output[".id"]);
+
+ markup += " <tr>";
+ markup += " <td class=\"label\"><input class=\"dataset-io-label\" name=\"" + form_name("ds", s, "output", i, "label") + "\" value=\"" + output["label"] + "\" /></td>";
+ markup += " <td class=\"data\"><textarea class=\"dataset-io-data\" name=\"" + form_name("ds", s, "output", i, "data") + "\">" + output["data"] + "</textarea></td>";
+ markup += " <td class=\"action\"><input class=\"dataset-io-action\" type=\"button\" value=\"Delete\" /></td>";
+ markup += " </tr>";
+ }
+
+ markup += " </tbody>";
+ markup += " </table>";
+ markup += " <input type='button' value='Add new output' /></div>";
+ markup += " <input class='delete-data-set' type='button' value='Delete set of example inputs / outputs' />";
+ markup += " <div style='clear: right'></div>";
+ markup += " </div>";
+ markup += "</div>";
+ }
+ }
+
+ document.getElementById('data_sets').innerHTML = markup;
+
+ for (var s = 0; s < data_sets.length; s++) {
+ var oFCKeditor = new FCKeditor(form_name("ds", s, "desc"), '400px', '80px', 'None');
+ oFCKeditor.BasePath = '/_javascript_s/fckeditor/';
+ oFCKeditor.Config['CustomConfigurationsPath'] = '/_javascript_s/fckcustom.js';
+ oFCKeditor.ReplaceTextarea();
+ }
+}
+
+function deleteDataSet(i) {
+ var data_sets = saveDataSets();
+ data_sets.splice(i, 1);
+ renderDataSets(data_sets);
+}
+
+function addDataSet() {
+ var data_sets = saveDataSets();
+ data_sets.push( { "title" : "", "desc" : "", "inputs" : [], "outputs" : [] } );
+ renderDataSets(data_sets);
+}
+
+function addDataItem(type, s) {
+ var data_sets = saveDataSets();
+ data_sets[s][type].push( { "label" : "", "data" : "" } );
+ renderDataSets(data_sets);
+}
+
+function deleteDataItem(type, s, i) {
+ var data_sets = saveDataSets();
+ data_sets[s][type].splice(i, 1);
+ renderDataSets(data_sets);
+}
+
Modified: branches/datasets/public/stylesheets/styles.css (2798 => 2799)
--- branches/datasets/public/stylesheets/styles.css 2011-11-10 14:18:07 UTC (rev 2798)
+++ branches/datasets/public/stylesheets/styles.css 2011-11-10 14:35:11 UTC (rev 2799)
@@ -2077,6 +2077,78 @@
padding-top: 1em;
}
+/* Data set form */
+
+.form {
+ background: #e8e8e8;
+ width: 566px;
+ padding: 6px;
+ border: 1px solid #d0d0d0;
+margin-top: 1em;
+margin-bottom: 1em;
+}
+
+.form-triple {
+ margin-top: 0.2em;
+ margin-bottom: 0.2em;
+}
+
+.form-triple * {
+ display: inline-block;
+ vertical-align: top;
+}
+
+.form-label {
+ width: 100px;
+}
+
+.form-data,
+.dataset-title,
+.dataset-description,
+.dataset-io-data,
+.dataset_io-description {
+ width: 400px;
+}
+
+.dataset-description,
+.dataset-io-data,
+.dataset_io-description {
+ height: 80px;
+}
+
+TABLE.dataset-table {
+ margin-top: 1.0em;
+ margin-bottom: 0.5em;
+ border-collapse: collapse;
+}
+
+TABLE.dataset-table TH {
+ color: white;
+ background: gray;
+ border: 1px solid gray;
+}
+
+TABLE.dataset-table TH,
+TABLE.dataset-table TD {
+ text-align: left;
+ vertical-align: top;
+ padding: 4px;
+}
+
+TABLE.dataset-table .label,
+TABLE.dataset-table .data {
+ background: #d0d0d0;
+ border: 1px solid gray;
+}
+
+.dataset-io-label {
+ width: 88px;
+}
+
+.delete-data-set {
+ float: right;
+}
+
/* Styles Related to topics */
table.topic {