galaxy-commits
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions
commit/galaxy-central: guerler: ToolForm: Fix sorting of content labels
by commits-noreply@bitbucket.org 15 Oct '14
by commits-noreply@bitbucket.org 15 Oct '14
15 Oct '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/6e605c5d9fef/
Changeset: 6e605c5d9fef
User: guerler
Date: 2014-10-15 22:38:07+00:00
Summary: ToolForm: Fix sorting of content labels
Affected #: 3 files
diff -r a100c09d3e3ad5d8df2747384d3d6b22c3658cb9 -r 6e605c5d9feff984bfb5e67f2c36a400abdcae85 client/galaxy/scripts/mvc/tools/tools-content.js
--- a/client/galaxy/scripts/mvc/tools/tools-content.js
+++ b/client/galaxy/scripts/mvc/tools/tools-content.js
@@ -27,6 +27,11 @@
// backup summary
self.summary = response;
+ // sort by id
+ self.summary.sort(function(a, b) {
+ return a.hid > b.hid ? -1 : (a.hid < b.hid ? 1 : 0);
+ });
+
// log
console.debug('tools-content::initialize() - Completed.');
diff -r a100c09d3e3ad5d8df2747384d3d6b22c3658cb9 -r 6e605c5d9feff984bfb5e67f2c36a400abdcae85 static/scripts/mvc/tools/tools-content.js
--- a/static/scripts/mvc/tools/tools-content.js
+++ b/static/scripts/mvc/tools/tools-content.js
@@ -27,6 +27,11 @@
// backup summary
self.summary = response;
+ // sort by id
+ self.summary.sort(function(a, b) {
+ return a.hid > b.hid ? -1 : (a.hid < b.hid ? 1 : 0);
+ });
+
// log
console.debug('tools-content::initialize() - Completed.');
diff -r a100c09d3e3ad5d8df2747384d3d6b22c3658cb9 -r 6e605c5d9feff984bfb5e67f2c36a400abdcae85 static/scripts/packed/mvc/tools/tools-content.js
--- a/static/scripts/packed/mvc/tools/tools-content.js
+++ b/static/scripts/packed/mvc/tools/tools-content.js
@@ -1,1 +1,1 @@
-define(["utils/utils"],function(a){return Backbone.Model.extend({initialize:function(c){this.base_url=galaxy_config.root+"api/histories/"+c.history_id+"/contents";this.datatypes={};this.summary={};var b=this;a.get({url:galaxy_config.root+"api/datatypes/mapping",cache:true,success:function(d){b.datatypes=d;a.get({url:b.base_url+"?deleted=false",success:function(e){b.summary=e;console.debug("tools-content::initialize() - Completed.");c.success&&c.success()},error:function(e){console.debug("tools-content::initialize() - Ajax request for summary failed.");console.debug(e)}})},error:function(d){console.debug("tools-content::initialize() - Ajax request for datatypes failed.");console.debug(d)}})},filterType:function(c){c=c||{};var b=[];var g="dataset";if(c.src=="hdca"){g="dataset_collection"}for(var d in this.summary){var e=this.summary[d];var f=false;for(var d in c.extensions){if(this._matchType(c.extensions[d],e.extension)){f=true;break}}if((e.history_content_type===g)&&(f||!c.extensions)){b.push(e)}}return b},get:function(b){return _.findWhere(this.summary,b)||{}},getDetails:function(b){if(!b.id||b.id==="null"){b.success&&b.success();return}var c=this.base_url+"/datasets/"+b.id;if(b.src=="hdca"){c=this.base_url+"/dataset_collections/"+b.id}a.get({url:c,success:function(d){b.success&&b.success(d)},error:function(d){b.success&&b.success();console.debug("tools-content::getDetails() - Ajax request for content failed.");console.debug(d)}})},_matchType:function(f,b){var c=this.datatypes.ext_to_class_name[f];if(!c){console.debug("tools-content::_matchType() - Specific target class unavailable. Accepting all formats.");return true}var d=this.datatypes.ext_to_class_name[b];if(!d){console.debug("tools-content::_matchType() - Specific reference class unavailable. Accepting all formats.");return true}var e=this.datatypes.class_to_classes[d];if(e[c]){return true}return false}})});
\ No newline at end of file
+define(["utils/utils"],function(a){return Backbone.Model.extend({initialize:function(c){this.base_url=galaxy_config.root+"api/histories/"+c.history_id+"/contents";this.datatypes={};this.summary={};var b=this;a.get({url:galaxy_config.root+"api/datatypes/mapping",cache:true,success:function(d){b.datatypes=d;a.get({url:b.base_url+"?deleted=false",success:function(e){b.summary=e;b.summary.sort(function(g,f){return g.hid>f.hid?-1:(g.hid<f.hid?1:0)});console.debug("tools-content::initialize() - Completed.");c.success&&c.success()},error:function(e){console.debug("tools-content::initialize() - Ajax request for summary failed.");console.debug(e)}})},error:function(d){console.debug("tools-content::initialize() - Ajax request for datatypes failed.");console.debug(d)}})},filterType:function(c){c=c||{};var b=[];var g="dataset";if(c.src=="hdca"){g="dataset_collection"}for(var d in this.summary){var e=this.summary[d];var f=false;for(var d in c.extensions){if(this._matchType(c.extensions[d],e.extension)){f=true;break}}if((e.history_content_type===g)&&(f||!c.extensions)){b.push(e)}}return b},get:function(b){return _.findWhere(this.summary,b)||{}},getDetails:function(b){if(!b.id||b.id==="null"){b.success&&b.success();return}var c=this.base_url+"/datasets/"+b.id;if(b.src=="hdca"){c=this.base_url+"/dataset_collections/"+b.id}a.get({url:c,success:function(d){b.success&&b.success(d)},error:function(d){b.success&&b.success();console.debug("tools-content::getDetails() - Ajax request for content failed.");console.debug(d)}})},_matchType:function(f,b){var c=this.datatypes.ext_to_class_name[f];if(!c){console.debug("tools-content::_matchType() - Specific target class unavailable. Accepting all formats.");return true}var d=this.datatypes.ext_to_class_name[b];if(!d){console.debug("tools-content::_matchType() - Specific reference class unavailable. Accepting all formats.");return true}var e=this.datatypes.class_to_classes[d];if(e[c]){return true}return false}})});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: guerler: ToolForm: Highlight first error only
by commits-noreply@bitbucket.org 15 Oct '14
by commits-noreply@bitbucket.org 15 Oct '14
15 Oct '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a100c09d3e3a/
Changeset: a100c09d3e3a
User: guerler
Date: 2014-10-15 20:43:42+00:00
Summary: ToolForm: Highlight first error only
Affected #: 6 files
diff -r d94ef8154c63145b0e2928b099017324097d4352 -r a100c09d3e3ad5d8df2747384d3d6b22c3658cb9 client/galaxy/scripts/mvc/tools/tools-form.js
--- a/client/galaxy/scripts/mvc/tools/tools-form.js
+++ b/client/galaxy/scripts/mvc/tools/tools-form.js
@@ -132,7 +132,7 @@
// button menu
var menu = new Ui.ButtonMenu({
icon : 'fa-gear',
- tooltip : 'Click to see a list of available operations.'
+ tooltip : 'Click to see a list of options.'
});
// add question option
diff -r d94ef8154c63145b0e2928b099017324097d4352 -r a100c09d3e3ad5d8df2747384d3d6b22c3658cb9 client/galaxy/scripts/mvc/tools/tools-jobs.js
--- a/client/galaxy/scripts/mvc/tools/tools-jobs.js
+++ b/client/galaxy/scripts/mvc/tools/tools-jobs.js
@@ -55,6 +55,7 @@
var error_messages = self.app.tree.matchResponse(response.message.data);
for (var input_id in error_messages) {
self._foundError(input_id, error_messages[input_id]);
+ break;
}
} else {
// show error message with details
diff -r d94ef8154c63145b0e2928b099017324097d4352 -r a100c09d3e3ad5d8df2747384d3d6b22c3658cb9 static/scripts/mvc/tools/tools-form.js
--- a/static/scripts/mvc/tools/tools-form.js
+++ b/static/scripts/mvc/tools/tools-form.js
@@ -132,7 +132,7 @@
// button menu
var menu = new Ui.ButtonMenu({
icon : 'fa-gear',
- tooltip : 'Click to see a list of available operations.'
+ tooltip : 'Click to see a list of options.'
});
// add question option
diff -r d94ef8154c63145b0e2928b099017324097d4352 -r a100c09d3e3ad5d8df2747384d3d6b22c3658cb9 static/scripts/mvc/tools/tools-jobs.js
--- a/static/scripts/mvc/tools/tools-jobs.js
+++ b/static/scripts/mvc/tools/tools-jobs.js
@@ -55,6 +55,7 @@
var error_messages = self.app.tree.matchResponse(response.message.data);
for (var input_id in error_messages) {
self._foundError(input_id, error_messages[input_id]);
+ break;
}
} else {
// show error message with details
diff -r d94ef8154c63145b0e2928b099017324097d4352 -r a100c09d3e3ad5d8df2747384d3d6b22c3658cb9 static/scripts/packed/mvc/tools/tools-form.js
--- a/static/scripts/packed/mvc/tools/tools-form.js
+++ b/static/scripts/packed/mvc/tools/tools-form.js
@@ -1,1 +1,1 @@
-define(["utils/utils","mvc/ui/ui-portlet","mvc/ui/ui-misc","mvc/citation/citation-model","mvc/citation/citation-view","mvc/tools","mvc/tools/tools-template","mvc/tools/tools-content","mvc/tools/tools-section","mvc/tools/tools-tree","mvc/tools/tools-jobs"],function(i,h,l,j,a,e,d,f,k,c,g){var b=Backbone.View.extend({container:"body",initialize:function(n){console.debug(n);var m=this;if(parent.Galaxy&&parent.Galaxy.modal){this.modal=parent.Galaxy.modal}else{this.modal=new l.Modal.View()}this.options=n;this.model=n.model;this.inputs=n.model.inputs;this.setElement("<div/>");$(this.container).append(this.$el);this.tree=new c(this);this.job_handler=new g(this);this.field_list={};this.input_list={};this.element_list={};this.content=new f({history_id:this.options.history_id,success:function(){m._buildForm()}})},message:function(m){$(this.container).empty();$(this.container).append(m)},reset:function(){for(var m in this.element_list){this.element_list[m].reset()}},refresh:function(){this.tree.refresh();for(var m in this.field_list){this.field_list[m].trigger("change")}console.debug("tools-form::refresh() - Recreated data structure. Refresh.")},_refreshForm:function(){var m=this;var n=this.tree.finalize({data:function(o){if(o.values.length>0&&o.values[0]&&o.values[0].src==="hda"){return m.content.get({id:o.values[0].id}).dataset_id}return null}});console.debug("tools-form::_refreshForm() - Refreshing inputs/states.");console.debug(n);i.request({type:"GET",url:galaxy_config.root+"tool_runner/index?tool_id="+this.options.id+"&form_refresh=True",data:n,success:function(o){console.debug("tools-form::_refreshForm() - Refreshed inputs/states.");console.debug(o)},error:function(o){console.debug("tools-form::_refreshForm() - Refresh request failed.");console.debug(o)}})},_buildForm:function(){var n=this;var p=new l.ButtonMenu({icon:"fa-gear",tooltip:"Click to see a list of available operations."});p.addMenu({icon:"fa-question-circle",title:"Question?",tooltip:"Ask a question about this tool (Biostar)",onclick:function(){window.open(n.options.biostar_url+"/p/new/post/")}});p.addMenu({icon:"fa-search",title:"Search",tooltip:"Search help for this tool (Biostar)",onclick:function(){window.open(n.options.biostar_url+"/t/"+n.options.id+"/")}});p.addMenu({icon:"fa-share",title:"Share",tooltip:"Share this tool",onclick:function(){prompt("Copy to clipboard: Ctrl+C, Enter",window.location.origin+galaxy_config.root+"root?tool_id="+n.options.id)}});if(Galaxy.currUser.get("is_admin")){p.addMenu({icon:"fa-download",title:"Download",tooltip:"Download this tool",onclick:function(){window.location.href=galaxy_config.root+"api/tools/"+n.options.id+"/download"}})}this.section=new k.View(n,{inputs:this.inputs,cls:"ui-table-plain"});if(this.incompatible){this.$el.hide();$("#tool-form-classic").show();return}this.portlet=new h.View({icon:"fa-wrench",title:"<b>"+this.model.name+"</b> "+this.model.description,operations:{menu:p},buttons:{execute:new l.Button({icon:"fa-check",tooltip:"Execute the tool",title:"Execute",cls:"btn btn-primary",floating:"clear",onclick:function(){n.job_handler.submit()}})}});if(!this.options.biostar_url){button_question.$el.hide();button_search.$el.hide()}this.$el.append(this.portlet.$el);if(this.options.help!=""){this.$el.append(d.help(this.options.help))}if(this.options.citations){this.$el.append(d.citations());var m=new j.ToolCitationCollection();m.tool_id=this.options.id;var o=new a.CitationListView({collection:m});o.render();m.fetch()}this.portlet.append(this.section.$el);this.refresh()}});return{View:b}});
\ No newline at end of file
+define(["utils/utils","mvc/ui/ui-portlet","mvc/ui/ui-misc","mvc/citation/citation-model","mvc/citation/citation-view","mvc/tools","mvc/tools/tools-template","mvc/tools/tools-content","mvc/tools/tools-section","mvc/tools/tools-tree","mvc/tools/tools-jobs"],function(i,h,l,j,a,e,d,f,k,c,g){var b=Backbone.View.extend({container:"body",initialize:function(n){console.debug(n);var m=this;if(parent.Galaxy&&parent.Galaxy.modal){this.modal=parent.Galaxy.modal}else{this.modal=new l.Modal.View()}this.options=n;this.model=n.model;this.inputs=n.model.inputs;this.setElement("<div/>");$(this.container).append(this.$el);this.tree=new c(this);this.job_handler=new g(this);this.field_list={};this.input_list={};this.element_list={};this.content=new f({history_id:this.options.history_id,success:function(){m._buildForm()}})},message:function(m){$(this.container).empty();$(this.container).append(m)},reset:function(){for(var m in this.element_list){this.element_list[m].reset()}},refresh:function(){this.tree.refresh();for(var m in this.field_list){this.field_list[m].trigger("change")}console.debug("tools-form::refresh() - Recreated data structure. Refresh.")},_refreshForm:function(){var m=this;var n=this.tree.finalize({data:function(o){if(o.values.length>0&&o.values[0]&&o.values[0].src==="hda"){return m.content.get({id:o.values[0].id}).dataset_id}return null}});console.debug("tools-form::_refreshForm() - Refreshing inputs/states.");console.debug(n);i.request({type:"GET",url:galaxy_config.root+"tool_runner/index?tool_id="+this.options.id+"&form_refresh=True",data:n,success:function(o){console.debug("tools-form::_refreshForm() - Refreshed inputs/states.");console.debug(o)},error:function(o){console.debug("tools-form::_refreshForm() - Refresh request failed.");console.debug(o)}})},_buildForm:function(){var n=this;var p=new l.ButtonMenu({icon:"fa-gear",tooltip:"Click to see a list of options."});p.addMenu({icon:"fa-question-circle",title:"Question?",tooltip:"Ask a question about this tool (Biostar)",onclick:function(){window.open(n.options.biostar_url+"/p/new/post/")}});p.addMenu({icon:"fa-search",title:"Search",tooltip:"Search help for this tool (Biostar)",onclick:function(){window.open(n.options.biostar_url+"/t/"+n.options.id+"/")}});p.addMenu({icon:"fa-share",title:"Share",tooltip:"Share this tool",onclick:function(){prompt("Copy to clipboard: Ctrl+C, Enter",window.location.origin+galaxy_config.root+"root?tool_id="+n.options.id)}});if(Galaxy.currUser.get("is_admin")){p.addMenu({icon:"fa-download",title:"Download",tooltip:"Download this tool",onclick:function(){window.location.href=galaxy_config.root+"api/tools/"+n.options.id+"/download"}})}this.section=new k.View(n,{inputs:this.inputs,cls:"ui-table-plain"});if(this.incompatible){this.$el.hide();$("#tool-form-classic").show();return}this.portlet=new h.View({icon:"fa-wrench",title:"<b>"+this.model.name+"</b> "+this.model.description,operations:{menu:p},buttons:{execute:new l.Button({icon:"fa-check",tooltip:"Execute the tool",title:"Execute",cls:"btn btn-primary",floating:"clear",onclick:function(){n.job_handler.submit()}})}});if(!this.options.biostar_url){button_question.$el.hide();button_search.$el.hide()}this.$el.append(this.portlet.$el);if(this.options.help!=""){this.$el.append(d.help(this.options.help))}if(this.options.citations){this.$el.append(d.citations());var m=new j.ToolCitationCollection();m.tool_id=this.options.id;var o=new a.CitationListView({collection:m});o.render();m.fetch()}this.portlet.append(this.section.$el);this.refresh()}});return{View:b}});
\ No newline at end of file
diff -r d94ef8154c63145b0e2928b099017324097d4352 -r a100c09d3e3ad5d8df2747384d3d6b22c3658cb9 static/scripts/packed/mvc/tools/tools-jobs.js
--- a/static/scripts/packed/mvc/tools/tools-jobs.js
+++ b/static/scripts/packed/mvc/tools/tools-jobs.js
@@ -1,1 +1,1 @@
-define(["utils/utils","mvc/tools/tools-template"],function(b,a){return Backbone.Model.extend({initialize:function(d,c){this.app=d;this.options=b.merge(c,this.optionsDefault)},submit:function(){var c=this;var d={tool_id:this.app.options.id,inputs:this.app.tree.finalize()};this.app.reset();if(!this._validation(d)){console.debug("tools-jobs::submit - Submission canceled. Validation failed.");return}console.debug(d);this.app.modal.show({title:"Please wait...",body:"progress",closing_events:true,buttons:{Close:function(){c.app.modal.hide()}}});b.request({type:"POST",url:galaxy_config.root+"api/tools",data:d,success:function(e){c.app.modal.hide();c.app.message(a.success(e));c._refreshHdas()},error:function(e,g){c.app.modal.hide();if(e&&e.message&&e.message.data){var h=c.app.tree.matchResponse(e.message.data);for(var f in h){c._foundError(f,h[f])}}else{console.debug(e);c.app.modal.show({title:"Job submission failed",body:a.error(d),buttons:{Close:function(){c.app.modal.hide()}}})}}})},_foundError:function(c,d){var e=this.app.element_list[c];e.error(d||"Please verify this parameter.");$(this.app.container).animate({scrollTop:e.$el.offset().top-20},500)},_validation:function(g){var c=g.inputs;var k=-1;for(var i in c){var e=c[i];var j=this.app.tree.match(i);var d=this.app.field_list[j];var h=this.app.input_list[j];if(h&&d&&d.validate&&!d.validate()){this._foundError(j);return false}if(e.batch){var f=e.values.length;if(k===-1){k=f}else{if(k!==f){this._foundError(j,"Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>"+f+"</b> selection(s) while a previous field contains <b>"+k+"</b>.");return false}}}}return true},_refreshHdas:function(){if(parent.Galaxy&&parent.Galaxy.currHistoryPanel){parent.Galaxy.currHistoryPanel.refreshContents()}}})});
\ No newline at end of file
+define(["utils/utils","mvc/tools/tools-template"],function(b,a){return Backbone.Model.extend({initialize:function(d,c){this.app=d;this.options=b.merge(c,this.optionsDefault)},submit:function(){var c=this;var d={tool_id:this.app.options.id,inputs:this.app.tree.finalize()};this.app.reset();if(!this._validation(d)){console.debug("tools-jobs::submit - Submission canceled. Validation failed.");return}console.debug(d);this.app.modal.show({title:"Please wait...",body:"progress",closing_events:true,buttons:{Close:function(){c.app.modal.hide()}}});b.request({type:"POST",url:galaxy_config.root+"api/tools",data:d,success:function(e){c.app.modal.hide();c.app.message(a.success(e));c._refreshHdas()},error:function(e,g){c.app.modal.hide();if(e&&e.message&&e.message.data){var h=c.app.tree.matchResponse(e.message.data);for(var f in h){c._foundError(f,h[f]);break}}else{console.debug(e);c.app.modal.show({title:"Job submission failed",body:a.error(d),buttons:{Close:function(){c.app.modal.hide()}}})}}})},_foundError:function(c,d){var e=this.app.element_list[c];e.error(d||"Please verify this parameter.");$(this.app.container).animate({scrollTop:e.$el.offset().top-20},500)},_validation:function(g){var c=g.inputs;var k=-1;for(var i in c){var e=c[i];var j=this.app.tree.match(i);var d=this.app.field_list[j];var h=this.app.input_list[j];if(h&&d&&d.validate&&!d.validate()){this._foundError(j);return false}if(e.batch){var f=e.values.length;if(k===-1){k=f}else{if(k!==f){this._foundError(j,"Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>"+f+"</b> selection(s) while a previous field contains <b>"+k+"</b>.");return false}}}}return true},_refreshHdas:function(){if(parent.Galaxy&&parent.Galaxy.currHistoryPanel){parent.Galaxy.currHistoryPanel.refreshContents()}}})});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dannon: Merged in jmchilton/galaxy-central-fork-1 (pull request #530)
by commits-noreply@bitbucket.org 15 Oct '14
by commits-noreply@bitbucket.org 15 Oct '14
15 Oct '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/d94ef8154c63/
Changeset: d94ef8154c63
User: dannon
Date: 2014-10-15 20:30:56+00:00
Summary: Merged in jmchilton/galaxy-central-fork-1 (pull request #530)
Implement creating tool shed repository revisions via API tarball upload.
Affected #: 5 files
diff -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 -r d94ef8154c63145b0e2928b099017324097d4352 lib/galaxy/tools/actions/upload_common.py
--- a/lib/galaxy/tools/actions/upload_common.py
+++ b/lib/galaxy/tools/actions/upload_common.py
@@ -30,7 +30,7 @@
f.file.close()
upload_dataset['file_data'] = dict( filename=f.filename,
local_filename=local_filename )
- elif type( f ) == dict and 'filename' and 'local_filename' not in f:
+ elif type( f ) == dict and 'local_filename' not in f:
raise Exception( 'Uploaded file was encoded in a way not understood by Galaxy.' )
if upload_dataset['url_paste'] and upload_dataset['url_paste'].strip() != '':
upload_dataset['url_paste'], is_multi_byte = datatypes.sniff.stream_to_file( StringIO.StringIO( upload_dataset['url_paste'] ), prefix="strio_url_paste_" )
diff -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 -r d94ef8154c63145b0e2928b099017324097d4352 lib/galaxy/webapps/tool_shed/api/repositories.py
--- a/lib/galaxy/webapps/tool_shed/api/repositories.py
+++ b/lib/galaxy/webapps/tool_shed/api/repositories.py
@@ -2,11 +2,15 @@
import logging
import os
import tarfile
+import StringIO
import tempfile
from time import strftime
+from cgi import FieldStorage
+
from galaxy import util
from galaxy import web
+from galaxy.datatypes import checkers
from galaxy.model.orm import and_
from galaxy.web.base.controller import BaseAPIController
from galaxy.web.base.controller import HTTPBadRequest
@@ -16,10 +20,14 @@
from tool_shed.metadata import repository_metadata_manager
from tool_shed.repository_types import util as rt_util
+from tool_shed.dependencies import attribute_handlers
+
from tool_shed.util import basic_util
+from tool_shed.util import commit_util
from tool_shed.util import encoding_util
from tool_shed.util import hg_util
from tool_shed.util import repository_util
+from tool_shed.util import repository_content_util
from tool_shed.util import shed_util_common as suc
from tool_shed.util import tool_util
@@ -568,3 +576,108 @@
action='show',
id=trans.security.encode_id( repository.id ) )
return repository_dict
+
+ @web.expose_api
+ def create_changeset_revision( self, trans, id, payload, **kwd ):
+ """
+ POST /api/repositories/{encoded_repository_id}/changeset_revision
+
+ Create a new tool shed repository commit - leaving PUT on parent
+ resource open for updating meta-attirbutes of the repository (and
+ Galaxy doesn't allow PUT multipart data anyway
+ https://trello.com/c/CQwmCeG6)
+
+ :param id: the encoded id of the Repository object
+
+ The following parameters may be included in the payload.
+ :param commit_message: hg commit message for update.
+ """
+
+ # Example URL: http://localhost:9009/api/repositories/f9cad7b01a472135
+ rdah = attribute_handlers.RepositoryDependencyAttributeHandler( trans.app, unpopulate=False )
+ tdah = attribute_handlers.ToolDependencyAttributeHandler( trans.app, unpopulate=False )
+
+ repository = suc.get_repository_in_tool_shed( trans.app, id )
+ repo_dir = repository.repo_path( trans.app )
+ repo = hg_util.get_repo_for_repository( trans.app, repository=None, repo_path=repo_dir, create=False )
+
+ upload_point = commit_util.get_upload_point( repository, **kwd )
+ tip = repository.tip( trans.app )
+
+ file_data = payload.get('file')
+ # Code stolen from gx's upload_common.py
+ if isinstance( file_data, FieldStorage ):
+ assert not isinstance( file_data.file, StringIO.StringIO )
+ assert file_data.file.name != '<fdopen>'
+ local_filename = util.mkstemp_ln( file_data.file.name, 'upload_file_data_' )
+ file_data.file.close()
+ file_data = dict( filename=file_data.filename,
+ local_filename=local_filename )
+ elif type( file_data ) == dict and 'local_filename' not in file_data:
+ raise Exception( 'Uploaded file was encoded in a way not understood.' )
+
+ commit_message = kwd.get( 'commit_message', 'Uploaded' )
+
+ uploaded_file = open(file_data['local_filename'], 'rb')
+ uploaded_file_name = file_data['local_filename']
+
+ isgzip = False
+ isbz2 = False
+ isgzip = checkers.is_gzip( uploaded_file_name )
+ if not isgzip:
+ isbz2 = checkers.is_bz2( uploaded_file_name )
+ if ( isgzip or isbz2 ):
+ # Open for reading with transparent compression.
+ tar = tarfile.open( uploaded_file_name, 'r:*' )
+ else:
+ tar = tarfile.open( uploaded_file_name )
+
+ new_repo_alert = False
+ remove_repo_files_not_in_tar = True
+
+ ok, message, files_to_remove, content_alert_str, undesirable_dirs_removed, undesirable_files_removed = \
+ repository_content_util.upload_tar(
+ trans,
+ rdah,
+ tdah,
+ repository,
+ tar,
+ uploaded_file,
+ upload_point,
+ remove_repo_files_not_in_tar,
+ commit_message,
+ new_repo_alert
+ )
+ if ok:
+ # Update the repository files for browsing.
+ hg_util.update_repository( repo )
+ # Get the new repository tip.
+ if tip == repository.tip( trans.app ):
+ trans.response.status = 400
+ message = 'No changes to repository.'
+ ok = False
+ else:
+ rmm = repository_metadata_manager.RepositoryMetadataManager( app=trans.app,
+ user=trans.user,
+ repository=repository )
+ status, error_message = \
+ rmm.set_repository_metadata_due_to_new_tip( trans.request.host,
+ content_alert_str=content_alert_str,
+ **kwd )
+ if error_message:
+ ok = False
+ trans.response.status = 500
+ message = error_message
+ else:
+ trans.response.status = 500
+
+ if not ok:
+ return {
+ "err_msg": message,
+ "content_alert": content_alert_str
+ }
+ else:
+ return {
+ "message": message,
+ "content_alert": content_alert_str
+ }
diff -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 -r d94ef8154c63145b0e2928b099017324097d4352 lib/galaxy/webapps/tool_shed/buildapp.py
--- a/lib/galaxy/webapps/tool_shed/buildapp.py
+++ b/lib/galaxy/webapps/tool_shed/buildapp.py
@@ -119,6 +119,12 @@
name_prefix='user_',
path_prefix='/api',
parent_resources=dict( member_name='user', collection_name='users' ) )
+ webapp.mapper.connect( 'repository_create_changeset_revision',
+ '/api/repositories/:id/changeset_revision',
+ controller='repositories',
+ action='create_changeset_revision',
+ conditions=dict( method=[ "POST" ] ) )
+
webapp.finalize_config()
# Wrap the webapp in some useful middleware
if kwargs.get( 'middleware', True ):
diff -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 -r d94ef8154c63145b0e2928b099017324097d4352 lib/galaxy/webapps/tool_shed/controllers/upload.py
--- a/lib/galaxy/webapps/tool_shed/controllers/upload.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/upload.py
@@ -20,6 +20,7 @@
from tool_shed.util import commit_util
from tool_shed.util import hg_util
from tool_shed.util import shed_util_common as suc
+from tool_shed.util import repository_content_util
from tool_shed.util import xml_util
from galaxy import eggs
@@ -129,16 +130,18 @@
istar = False
if istar:
ok, message, files_to_remove, content_alert_str, undesirable_dirs_removed, undesirable_files_removed = \
- self.upload_tar( trans,
- rdah,
- tdah,
- repository,
- tar,
- uploaded_file,
- upload_point,
- remove_repo_files_not_in_tar,
- commit_message,
- new_repo_alert )
+ repository_content_util.upload_tar(
+ trans,
+ rdah,
+ tdah,
+ repository,
+ tar,
+ uploaded_file,
+ upload_point,
+ remove_repo_files_not_in_tar,
+ commit_message,
+ new_repo_alert
+ )
elif uploaded_directory:
ok, message, files_to_remove, content_alert_str, undesirable_dirs_removed, undesirable_files_removed = \
self.upload_directory( trans,
@@ -407,69 +410,3 @@
commit_message,
undesirable_dirs_removed,
undesirable_files_removed )
-
- def upload_tar( self, trans, rdah, tdah, repository, tar, uploaded_file, upload_point, remove_repo_files_not_in_tar,
- commit_message, new_repo_alert ):
- # Upload a tar archive of files.
- repo_dir = repository.repo_path( trans.app )
- repo = hg_util.get_repo_for_repository( trans.app, repository=None, repo_path=repo_dir, create=False )
- undesirable_dirs_removed = 0
- undesirable_files_removed = 0
- ok, message = commit_util.check_archive( repository, tar )
- if not ok:
- tar.close()
- uploaded_file.close()
- return ok, message, [], '', undesirable_dirs_removed, undesirable_files_removed
- else:
- if upload_point is not None:
- full_path = os.path.abspath( os.path.join( repo_dir, upload_point ) )
- else:
- full_path = os.path.abspath( repo_dir )
- filenames_in_archive = []
- for tarinfo_obj in tar.getmembers():
- ok = os.path.basename( tarinfo_obj.name ) not in commit_util.UNDESIRABLE_FILES
- if ok:
- for file_path_item in tarinfo_obj.name.split( '/' ):
- if file_path_item in commit_util.UNDESIRABLE_DIRS:
- undesirable_dirs_removed += 1
- ok = False
- break
- else:
- undesirable_files_removed += 1
- if ok:
- filenames_in_archive.append( tarinfo_obj.name )
- # Extract the uploaded tar to the load_point within the repository hierarchy.
- tar.extractall( path=full_path )
- tar.close()
- uploaded_file.close()
- for filename in filenames_in_archive:
- uploaded_file_name = os.path.join( full_path, filename )
- if os.path.split( uploaded_file_name )[ -1 ] == rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME:
- # Inspect the contents of the file to see if toolshed or changeset_revision attributes
- # are missing and if so, set them appropriately.
- altered, root_elem, error_message = rdah.handle_tag_attributes( uploaded_file_name )
- if error_message:
- return False, error_message, [], '', [], []
- elif altered:
- tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
- shutil.move( tmp_filename, uploaded_file_name )
- elif os.path.split( uploaded_file_name )[ -1 ] == rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME:
- # Inspect the contents of the file to see if toolshed or changeset_revision
- # attributes are missing and if so, set them appropriately.
- altered, root_elem, error_message = tdah.handle_tag_attributes( uploaded_file_name )
- if error_message:
- return False, error_message, [], '', [], []
- if altered:
- tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
- shutil.move( tmp_filename, uploaded_file_name )
- return commit_util.handle_directory_changes( trans.app,
- trans.request.host,
- trans.user.username,
- repository,
- full_path,
- filenames_in_archive,
- remove_repo_files_not_in_tar,
- new_repo_alert,
- commit_message,
- undesirable_dirs_removed,
- undesirable_files_removed )
diff -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 -r d94ef8154c63145b0e2928b099017324097d4352 lib/tool_shed/util/repository_content_util.py
--- /dev/null
+++ b/lib/tool_shed/util/repository_content_util.py
@@ -0,0 +1,75 @@
+import os
+import shutil
+
+from tool_shed.util import commit_util
+from tool_shed.util import hg_util
+from tool_shed.util import xml_util
+
+import tool_shed.repository_types.util as rt_util
+
+
+def upload_tar( trans, rdah, tdah, repository, tar, uploaded_file, upload_point, remove_repo_files_not_in_tar,
+ commit_message, new_repo_alert ):
+ # Upload a tar archive of files.
+ repo_dir = repository.repo_path( trans.app )
+ hg_util.get_repo_for_repository( trans.app, repository=None, repo_path=repo_dir, create=False )
+ undesirable_dirs_removed = 0
+ undesirable_files_removed = 0
+ ok, message = commit_util.check_archive( repository, tar )
+ if not ok:
+ tar.close()
+ uploaded_file.close()
+ return ok, message, [], '', undesirable_dirs_removed, undesirable_files_removed
+ else:
+ if upload_point is not None:
+ full_path = os.path.abspath( os.path.join( repo_dir, upload_point ) )
+ else:
+ full_path = os.path.abspath( repo_dir )
+ filenames_in_archive = []
+ for tarinfo_obj in tar.getmembers():
+ ok = os.path.basename( tarinfo_obj.name ) not in commit_util.UNDESIRABLE_FILES
+ if ok:
+ for file_path_item in tarinfo_obj.name.split( '/' ):
+ if file_path_item in commit_util.UNDESIRABLE_DIRS:
+ undesirable_dirs_removed += 1
+ ok = False
+ break
+ else:
+ undesirable_files_removed += 1
+ if ok:
+ filenames_in_archive.append( tarinfo_obj.name )
+ # Extract the uploaded tar to the load_point within the repository hierarchy.
+ tar.extractall( path=full_path )
+ tar.close()
+ uploaded_file.close()
+ for filename in filenames_in_archive:
+ uploaded_file_name = os.path.join( full_path, filename )
+ if os.path.split( uploaded_file_name )[ -1 ] == rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME:
+ # Inspect the contents of the file to see if toolshed or changeset_revision attributes
+ # are missing and if so, set them appropriately.
+ altered, root_elem, error_message = rdah.handle_tag_attributes( uploaded_file_name )
+ if error_message:
+ return False, error_message, [], '', [], []
+ elif altered:
+ tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
+ shutil.move( tmp_filename, uploaded_file_name )
+ elif os.path.split( uploaded_file_name )[ -1 ] == rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME:
+ # Inspect the contents of the file to see if toolshed or changeset_revision
+ # attributes are missing and if so, set them appropriately.
+ altered, root_elem, error_message = tdah.handle_tag_attributes( uploaded_file_name )
+ if error_message:
+ return False, error_message, [], '', [], []
+ if altered:
+ tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
+ shutil.move( tmp_filename, uploaded_file_name )
+ return commit_util.handle_directory_changes( trans.app,
+ trans.request.host,
+ trans.user.username,
+ repository,
+ full_path,
+ filenames_in_archive,
+ remove_repo_files_not_in_tar,
+ new_repo_alert,
+ commit_message,
+ undesirable_dirs_removed,
+ undesirable_files_removed )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/d6059c7194ef/
Changeset: d6059c7194ef
User: jmchilton
Date: 2014-10-14 20:30:27+00:00
Summary: Implement creating tool shed repository revisions via API tarball upload.
Affected #: 4 files
diff -r 42cd5d55755a729dd2f19ccd1ed20649ed29f6ac -r d6059c7194effc68dbfae4a459bd456c82fe8478 lib/galaxy/webapps/tool_shed/api/repositories.py
--- a/lib/galaxy/webapps/tool_shed/api/repositories.py
+++ b/lib/galaxy/webapps/tool_shed/api/repositories.py
@@ -2,11 +2,15 @@
import logging
import os
import tarfile
+import StringIO
import tempfile
from time import strftime
+from cgi import FieldStorage
+
from galaxy import util
from galaxy import web
+from galaxy.datatypes import checkers
from galaxy.model.orm import and_
from galaxy.web.base.controller import BaseAPIController
from galaxy.web.base.controller import HTTPBadRequest
@@ -16,10 +20,14 @@
from tool_shed.metadata import repository_metadata_manager
from tool_shed.repository_types import util as rt_util
+from tool_shed.dependencies import attribute_handlers
+
from tool_shed.util import basic_util
+from tool_shed.util import commit_util
from tool_shed.util import encoding_util
from tool_shed.util import hg_util
from tool_shed.util import repository_util
+from tool_shed.util import repository_content_util
from tool_shed.util import shed_util_common as suc
from tool_shed.util import tool_util
@@ -568,3 +576,109 @@
action='show',
id=trans.security.encode_id( repository.id ) )
return repository_dict
+
+ @web.expose_api
+ def create_changeset_revision( self, trans, id, payload, **kwd ):
+ """
+ POST /api/repositories/{encoded_repository_id}/changeset_revision
+
+ Create a new tool shed repository commit - leaving PUT on parent
+ resource open for updating meta-attirbutes of the repository (and
+ Galaxy doesn't allow PUT multipart data anyway
+ https://trello.com/c/CQwmCeG6)
+
+ :param id: the encoded id of the Repository object
+
+ The following parameters may be included in the payload.
+ :param commit_message: hg commit message for update.
+ """
+
+ # Example URL: http://localhost:9009/api/repositories/f9cad7b01a472135
+ rdah = attribute_handlers.RepositoryDependencyAttributeHandler( trans.app, unpopulate=False )
+ tdah = attribute_handlers.ToolDependencyAttributeHandler( trans.app, unpopulate=False )
+
+ repository = suc.get_repository_in_tool_shed( trans.app, id )
+ repo_dir = repository.repo_path( trans.app )
+ repo = hg_util.get_repo_for_repository( trans.app, repository=None, repo_path=repo_dir, create=False )
+
+ upload_point = commit_util.get_upload_point( repository, **kwd )
+ tip = repository.tip( trans.app )
+
+ file_data = payload.get('file')
+ # Code stolen from gx's upload_common.py
+ if isinstance( file_data, FieldStorage ):
+ assert not isinstance( file_data.file, StringIO.StringIO )
+ assert file_data.file.name != '<fdopen>'
+ local_filename = util.mkstemp_ln( file_data.file.name, 'upload_file_data_' )
+ file_data.file.close()
+ file_data = dict( filename=file_data.filename,
+ local_filename=local_filename )
+ elif type( file_data ) == dict and 'filename' and 'local_filename' not in file_data:
+ raise Exception( 'Uploaded file was encoded in a way not understood.' )
+
+ commit_message = kwd.get( 'commit_message', 'Uploaded' )
+
+ uploaded_file = open(file_data['local_filename'], 'rb')
+ uploaded_file_name = file_data['local_filename']
+
+ ok = True
+ isgzip = False
+ isbz2 = False
+ isgzip = checkers.is_gzip( uploaded_file_name )
+ if not isgzip:
+ isbz2 = checkers.is_bz2( uploaded_file_name )
+ if ( isgzip or isbz2 ):
+ # Open for reading with transparent compression.
+ tar = tarfile.open( uploaded_file_name, 'r:*' )
+ else:
+ tar = tarfile.open( uploaded_file_name )
+
+ new_repo_alert = False
+ remove_repo_files_not_in_tar = True
+
+ ok, message, files_to_remove, content_alert_str, undesirable_dirs_removed, undesirable_files_removed = \
+ repository_content_util.upload_tar(
+ trans,
+ rdah,
+ tdah,
+ repository,
+ tar,
+ uploaded_file,
+ upload_point,
+ remove_repo_files_not_in_tar,
+ commit_message,
+ new_repo_alert
+ )
+ if ok:
+ # Update the repository files for browsing.
+ hg_util.update_repository( repo )
+ # Get the new repository tip.
+ if tip == repository.tip( trans.app ):
+ trans.response.status = 400
+ message = 'No changes to repository.'
+ ok = False
+ else:
+ rmm = repository_metadata_manager.RepositoryMetadataManager( app=trans.app,
+ user=trans.user,
+ repository=repository )
+ status, error_message = \
+ rmm.set_repository_metadata_due_to_new_tip( trans.request.host,
+ content_alert_str=content_alert_str,
+ **kwd )
+ if error_message:
+ ok = False
+ trans.response.status = 500
+ message = error_message
+ else:
+ trans.response.status = 500
+
+ if not ok:
+ return {
+ "err_msg": message,
+ "content_alert": content_alert_str
+ }
+ else:
+ return {
+ "message": message,
+ "content_alert": content_alert_str
+ }
diff -r 42cd5d55755a729dd2f19ccd1ed20649ed29f6ac -r d6059c7194effc68dbfae4a459bd456c82fe8478 lib/galaxy/webapps/tool_shed/buildapp.py
--- a/lib/galaxy/webapps/tool_shed/buildapp.py
+++ b/lib/galaxy/webapps/tool_shed/buildapp.py
@@ -119,6 +119,12 @@
name_prefix='user_',
path_prefix='/api',
parent_resources=dict( member_name='user', collection_name='users' ) )
+ webapp.mapper.connect( 'repository_create_changeset_revision',
+ '/api/repositories/:id/changeset_revision',
+ controller='repositories',
+ action='create_changeset_revision',
+ conditions=dict( method=[ "POST" ] ) )
+
webapp.finalize_config()
# Wrap the webapp in some useful middleware
if kwargs.get( 'middleware', True ):
diff -r 42cd5d55755a729dd2f19ccd1ed20649ed29f6ac -r d6059c7194effc68dbfae4a459bd456c82fe8478 lib/galaxy/webapps/tool_shed/controllers/upload.py
--- a/lib/galaxy/webapps/tool_shed/controllers/upload.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/upload.py
@@ -20,6 +20,7 @@
from tool_shed.util import commit_util
from tool_shed.util import hg_util
from tool_shed.util import shed_util_common as suc
+from tool_shed.util import repository_content_util
from tool_shed.util import xml_util
from galaxy import eggs
@@ -129,16 +130,18 @@
istar = False
if istar:
ok, message, files_to_remove, content_alert_str, undesirable_dirs_removed, undesirable_files_removed = \
- self.upload_tar( trans,
- rdah,
- tdah,
- repository,
- tar,
- uploaded_file,
- upload_point,
- remove_repo_files_not_in_tar,
- commit_message,
- new_repo_alert )
+ repository_content_util.upload_tar(
+ trans,
+ rdah,
+ tdah,
+ repository,
+ tar,
+ uploaded_file,
+ upload_point,
+ remove_repo_files_not_in_tar,
+ commit_message,
+ new_repo_alert
+ )
elif uploaded_directory:
ok, message, files_to_remove, content_alert_str, undesirable_dirs_removed, undesirable_files_removed = \
self.upload_directory( trans,
@@ -407,69 +410,3 @@
commit_message,
undesirable_dirs_removed,
undesirable_files_removed )
-
- def upload_tar( self, trans, rdah, tdah, repository, tar, uploaded_file, upload_point, remove_repo_files_not_in_tar,
- commit_message, new_repo_alert ):
- # Upload a tar archive of files.
- repo_dir = repository.repo_path( trans.app )
- repo = hg_util.get_repo_for_repository( trans.app, repository=None, repo_path=repo_dir, create=False )
- undesirable_dirs_removed = 0
- undesirable_files_removed = 0
- ok, message = commit_util.check_archive( repository, tar )
- if not ok:
- tar.close()
- uploaded_file.close()
- return ok, message, [], '', undesirable_dirs_removed, undesirable_files_removed
- else:
- if upload_point is not None:
- full_path = os.path.abspath( os.path.join( repo_dir, upload_point ) )
- else:
- full_path = os.path.abspath( repo_dir )
- filenames_in_archive = []
- for tarinfo_obj in tar.getmembers():
- ok = os.path.basename( tarinfo_obj.name ) not in commit_util.UNDESIRABLE_FILES
- if ok:
- for file_path_item in tarinfo_obj.name.split( '/' ):
- if file_path_item in commit_util.UNDESIRABLE_DIRS:
- undesirable_dirs_removed += 1
- ok = False
- break
- else:
- undesirable_files_removed += 1
- if ok:
- filenames_in_archive.append( tarinfo_obj.name )
- # Extract the uploaded tar to the load_point within the repository hierarchy.
- tar.extractall( path=full_path )
- tar.close()
- uploaded_file.close()
- for filename in filenames_in_archive:
- uploaded_file_name = os.path.join( full_path, filename )
- if os.path.split( uploaded_file_name )[ -1 ] == rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME:
- # Inspect the contents of the file to see if toolshed or changeset_revision attributes
- # are missing and if so, set them appropriately.
- altered, root_elem, error_message = rdah.handle_tag_attributes( uploaded_file_name )
- if error_message:
- return False, error_message, [], '', [], []
- elif altered:
- tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
- shutil.move( tmp_filename, uploaded_file_name )
- elif os.path.split( uploaded_file_name )[ -1 ] == rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME:
- # Inspect the contents of the file to see if toolshed or changeset_revision
- # attributes are missing and if so, set them appropriately.
- altered, root_elem, error_message = tdah.handle_tag_attributes( uploaded_file_name )
- if error_message:
- return False, error_message, [], '', [], []
- if altered:
- tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
- shutil.move( tmp_filename, uploaded_file_name )
- return commit_util.handle_directory_changes( trans.app,
- trans.request.host,
- trans.user.username,
- repository,
- full_path,
- filenames_in_archive,
- remove_repo_files_not_in_tar,
- new_repo_alert,
- commit_message,
- undesirable_dirs_removed,
- undesirable_files_removed )
diff -r 42cd5d55755a729dd2f19ccd1ed20649ed29f6ac -r d6059c7194effc68dbfae4a459bd456c82fe8478 lib/tool_shed/util/repository_content_util.py
--- /dev/null
+++ b/lib/tool_shed/util/repository_content_util.py
@@ -0,0 +1,75 @@
+import os
+import shutil
+
+from tool_shed.util import commit_util
+from tool_shed.util import hg_util
+from tool_shed.util import xml_util
+
+import tool_shed.repository_types.util as rt_util
+
+
+def upload_tar( trans, rdah, tdah, repository, tar, uploaded_file, upload_point, remove_repo_files_not_in_tar,
+ commit_message, new_repo_alert ):
+ # Upload a tar archive of files.
+ repo_dir = repository.repo_path( trans.app )
+ hg_util.get_repo_for_repository( trans.app, repository=None, repo_path=repo_dir, create=False )
+ undesirable_dirs_removed = 0
+ undesirable_files_removed = 0
+ ok, message = commit_util.check_archive( repository, tar )
+ if not ok:
+ tar.close()
+ uploaded_file.close()
+ return ok, message, [], '', undesirable_dirs_removed, undesirable_files_removed
+ else:
+ if upload_point is not None:
+ full_path = os.path.abspath( os.path.join( repo_dir, upload_point ) )
+ else:
+ full_path = os.path.abspath( repo_dir )
+ filenames_in_archive = []
+ for tarinfo_obj in tar.getmembers():
+ ok = os.path.basename( tarinfo_obj.name ) not in commit_util.UNDESIRABLE_FILES
+ if ok:
+ for file_path_item in tarinfo_obj.name.split( '/' ):
+ if file_path_item in commit_util.UNDESIRABLE_DIRS:
+ undesirable_dirs_removed += 1
+ ok = False
+ break
+ else:
+ undesirable_files_removed += 1
+ if ok:
+ filenames_in_archive.append( tarinfo_obj.name )
+ # Extract the uploaded tar to the load_point within the repository hierarchy.
+ tar.extractall( path=full_path )
+ tar.close()
+ uploaded_file.close()
+ for filename in filenames_in_archive:
+ uploaded_file_name = os.path.join( full_path, filename )
+ if os.path.split( uploaded_file_name )[ -1 ] == rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME:
+ # Inspect the contents of the file to see if toolshed or changeset_revision attributes
+ # are missing and if so, set them appropriately.
+ altered, root_elem, error_message = rdah.handle_tag_attributes( uploaded_file_name )
+ if error_message:
+ return False, error_message, [], '', [], []
+ elif altered:
+ tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
+ shutil.move( tmp_filename, uploaded_file_name )
+ elif os.path.split( uploaded_file_name )[ -1 ] == rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME:
+ # Inspect the contents of the file to see if toolshed or changeset_revision
+ # attributes are missing and if so, set them appropriately.
+ altered, root_elem, error_message = tdah.handle_tag_attributes( uploaded_file_name )
+ if error_message:
+ return False, error_message, [], '', [], []
+ if altered:
+ tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
+ shutil.move( tmp_filename, uploaded_file_name )
+ return commit_util.handle_directory_changes( trans.app,
+ trans.request.host,
+ trans.user.username,
+ repository,
+ full_path,
+ filenames_in_archive,
+ remove_repo_files_not_in_tar,
+ new_repo_alert,
+ commit_message,
+ undesirable_dirs_removed,
+ undesirable_files_removed )
https://bitbucket.org/galaxy/galaxy-central/commits/7d8d1396b7ca/
Changeset: 7d8d1396b7ca
User: jmchilton
Date: 2014-10-15 19:27:23+00:00
Summary: Fixes to tool shed tar upload API enhancement.
Thanks to Bjoern and Dannon for the comments.
Affected #: 2 files
diff -r d6059c7194effc68dbfae4a459bd456c82fe8478 -r 7d8d1396b7cad129e9d89677a8c10d5bc9b2f1f4 lib/galaxy/tools/actions/upload_common.py
--- a/lib/galaxy/tools/actions/upload_common.py
+++ b/lib/galaxy/tools/actions/upload_common.py
@@ -30,7 +30,7 @@
f.file.close()
upload_dataset['file_data'] = dict( filename=f.filename,
local_filename=local_filename )
- elif type( f ) == dict and 'filename' and 'local_filename' not in f:
+ elif type( f ) == dict and 'local_filename' not in f:
raise Exception( 'Uploaded file was encoded in a way not understood by Galaxy.' )
if upload_dataset['url_paste'] and upload_dataset['url_paste'].strip() != '':
upload_dataset['url_paste'], is_multi_byte = datatypes.sniff.stream_to_file( StringIO.StringIO( upload_dataset['url_paste'] ), prefix="strio_url_paste_" )
diff -r d6059c7194effc68dbfae4a459bd456c82fe8478 -r 7d8d1396b7cad129e9d89677a8c10d5bc9b2f1f4 lib/galaxy/webapps/tool_shed/api/repositories.py
--- a/lib/galaxy/webapps/tool_shed/api/repositories.py
+++ b/lib/galaxy/webapps/tool_shed/api/repositories.py
@@ -613,7 +613,7 @@
file_data.file.close()
file_data = dict( filename=file_data.filename,
local_filename=local_filename )
- elif type( file_data ) == dict and 'filename' and 'local_filename' not in file_data:
+ elif type( file_data ) == dict and 'local_filename' not in file_data:
raise Exception( 'Uploaded file was encoded in a way not understood.' )
commit_message = kwd.get( 'commit_message', 'Uploaded' )
@@ -621,7 +621,6 @@
uploaded_file = open(file_data['local_filename'], 'rb')
uploaded_file_name = file_data['local_filename']
- ok = True
isgzip = False
isbz2 = False
isgzip = checkers.is_gzip( uploaded_file_name )
https://bitbucket.org/galaxy/galaxy-central/commits/d94ef8154c63/
Changeset: d94ef8154c63
User: dannon
Date: 2014-10-15 20:30:56+00:00
Summary: Merged in jmchilton/galaxy-central-fork-1 (pull request #530)
Implement creating tool shed repository revisions via API tarball upload.
Affected #: 5 files
diff -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 -r d94ef8154c63145b0e2928b099017324097d4352 lib/galaxy/tools/actions/upload_common.py
--- a/lib/galaxy/tools/actions/upload_common.py
+++ b/lib/galaxy/tools/actions/upload_common.py
@@ -30,7 +30,7 @@
f.file.close()
upload_dataset['file_data'] = dict( filename=f.filename,
local_filename=local_filename )
- elif type( f ) == dict and 'filename' and 'local_filename' not in f:
+ elif type( f ) == dict and 'local_filename' not in f:
raise Exception( 'Uploaded file was encoded in a way not understood by Galaxy.' )
if upload_dataset['url_paste'] and upload_dataset['url_paste'].strip() != '':
upload_dataset['url_paste'], is_multi_byte = datatypes.sniff.stream_to_file( StringIO.StringIO( upload_dataset['url_paste'] ), prefix="strio_url_paste_" )
diff -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 -r d94ef8154c63145b0e2928b099017324097d4352 lib/galaxy/webapps/tool_shed/api/repositories.py
--- a/lib/galaxy/webapps/tool_shed/api/repositories.py
+++ b/lib/galaxy/webapps/tool_shed/api/repositories.py
@@ -2,11 +2,15 @@
import logging
import os
import tarfile
+import StringIO
import tempfile
from time import strftime
+from cgi import FieldStorage
+
from galaxy import util
from galaxy import web
+from galaxy.datatypes import checkers
from galaxy.model.orm import and_
from galaxy.web.base.controller import BaseAPIController
from galaxy.web.base.controller import HTTPBadRequest
@@ -16,10 +20,14 @@
from tool_shed.metadata import repository_metadata_manager
from tool_shed.repository_types import util as rt_util
+from tool_shed.dependencies import attribute_handlers
+
from tool_shed.util import basic_util
+from tool_shed.util import commit_util
from tool_shed.util import encoding_util
from tool_shed.util import hg_util
from tool_shed.util import repository_util
+from tool_shed.util import repository_content_util
from tool_shed.util import shed_util_common as suc
from tool_shed.util import tool_util
@@ -568,3 +576,108 @@
action='show',
id=trans.security.encode_id( repository.id ) )
return repository_dict
+
+ @web.expose_api
+ def create_changeset_revision( self, trans, id, payload, **kwd ):
+ """
+ POST /api/repositories/{encoded_repository_id}/changeset_revision
+
+ Create a new tool shed repository commit - leaving PUT on parent
+ resource open for updating meta-attirbutes of the repository (and
+ Galaxy doesn't allow PUT multipart data anyway
+ https://trello.com/c/CQwmCeG6)
+
+ :param id: the encoded id of the Repository object
+
+ The following parameters may be included in the payload.
+ :param commit_message: hg commit message for update.
+ """
+
+ # Example URL: http://localhost:9009/api/repositories/f9cad7b01a472135
+ rdah = attribute_handlers.RepositoryDependencyAttributeHandler( trans.app, unpopulate=False )
+ tdah = attribute_handlers.ToolDependencyAttributeHandler( trans.app, unpopulate=False )
+
+ repository = suc.get_repository_in_tool_shed( trans.app, id )
+ repo_dir = repository.repo_path( trans.app )
+ repo = hg_util.get_repo_for_repository( trans.app, repository=None, repo_path=repo_dir, create=False )
+
+ upload_point = commit_util.get_upload_point( repository, **kwd )
+ tip = repository.tip( trans.app )
+
+ file_data = payload.get('file')
+ # Code stolen from gx's upload_common.py
+ if isinstance( file_data, FieldStorage ):
+ assert not isinstance( file_data.file, StringIO.StringIO )
+ assert file_data.file.name != '<fdopen>'
+ local_filename = util.mkstemp_ln( file_data.file.name, 'upload_file_data_' )
+ file_data.file.close()
+ file_data = dict( filename=file_data.filename,
+ local_filename=local_filename )
+ elif type( file_data ) == dict and 'local_filename' not in file_data:
+ raise Exception( 'Uploaded file was encoded in a way not understood.' )
+
+ commit_message = kwd.get( 'commit_message', 'Uploaded' )
+
+ uploaded_file = open(file_data['local_filename'], 'rb')
+ uploaded_file_name = file_data['local_filename']
+
+ isgzip = False
+ isbz2 = False
+ isgzip = checkers.is_gzip( uploaded_file_name )
+ if not isgzip:
+ isbz2 = checkers.is_bz2( uploaded_file_name )
+ if ( isgzip or isbz2 ):
+ # Open for reading with transparent compression.
+ tar = tarfile.open( uploaded_file_name, 'r:*' )
+ else:
+ tar = tarfile.open( uploaded_file_name )
+
+ new_repo_alert = False
+ remove_repo_files_not_in_tar = True
+
+ ok, message, files_to_remove, content_alert_str, undesirable_dirs_removed, undesirable_files_removed = \
+ repository_content_util.upload_tar(
+ trans,
+ rdah,
+ tdah,
+ repository,
+ tar,
+ uploaded_file,
+ upload_point,
+ remove_repo_files_not_in_tar,
+ commit_message,
+ new_repo_alert
+ )
+ if ok:
+ # Update the repository files for browsing.
+ hg_util.update_repository( repo )
+ # Get the new repository tip.
+ if tip == repository.tip( trans.app ):
+ trans.response.status = 400
+ message = 'No changes to repository.'
+ ok = False
+ else:
+ rmm = repository_metadata_manager.RepositoryMetadataManager( app=trans.app,
+ user=trans.user,
+ repository=repository )
+ status, error_message = \
+ rmm.set_repository_metadata_due_to_new_tip( trans.request.host,
+ content_alert_str=content_alert_str,
+ **kwd )
+ if error_message:
+ ok = False
+ trans.response.status = 500
+ message = error_message
+ else:
+ trans.response.status = 500
+
+ if not ok:
+ return {
+ "err_msg": message,
+ "content_alert": content_alert_str
+ }
+ else:
+ return {
+ "message": message,
+ "content_alert": content_alert_str
+ }
diff -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 -r d94ef8154c63145b0e2928b099017324097d4352 lib/galaxy/webapps/tool_shed/buildapp.py
--- a/lib/galaxy/webapps/tool_shed/buildapp.py
+++ b/lib/galaxy/webapps/tool_shed/buildapp.py
@@ -119,6 +119,12 @@
name_prefix='user_',
path_prefix='/api',
parent_resources=dict( member_name='user', collection_name='users' ) )
+ webapp.mapper.connect( 'repository_create_changeset_revision',
+ '/api/repositories/:id/changeset_revision',
+ controller='repositories',
+ action='create_changeset_revision',
+ conditions=dict( method=[ "POST" ] ) )
+
webapp.finalize_config()
# Wrap the webapp in some useful middleware
if kwargs.get( 'middleware', True ):
diff -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 -r d94ef8154c63145b0e2928b099017324097d4352 lib/galaxy/webapps/tool_shed/controllers/upload.py
--- a/lib/galaxy/webapps/tool_shed/controllers/upload.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/upload.py
@@ -20,6 +20,7 @@
from tool_shed.util import commit_util
from tool_shed.util import hg_util
from tool_shed.util import shed_util_common as suc
+from tool_shed.util import repository_content_util
from tool_shed.util import xml_util
from galaxy import eggs
@@ -129,16 +130,18 @@
istar = False
if istar:
ok, message, files_to_remove, content_alert_str, undesirable_dirs_removed, undesirable_files_removed = \
- self.upload_tar( trans,
- rdah,
- tdah,
- repository,
- tar,
- uploaded_file,
- upload_point,
- remove_repo_files_not_in_tar,
- commit_message,
- new_repo_alert )
+ repository_content_util.upload_tar(
+ trans,
+ rdah,
+ tdah,
+ repository,
+ tar,
+ uploaded_file,
+ upload_point,
+ remove_repo_files_not_in_tar,
+ commit_message,
+ new_repo_alert
+ )
elif uploaded_directory:
ok, message, files_to_remove, content_alert_str, undesirable_dirs_removed, undesirable_files_removed = \
self.upload_directory( trans,
@@ -407,69 +410,3 @@
commit_message,
undesirable_dirs_removed,
undesirable_files_removed )
-
- def upload_tar( self, trans, rdah, tdah, repository, tar, uploaded_file, upload_point, remove_repo_files_not_in_tar,
- commit_message, new_repo_alert ):
- # Upload a tar archive of files.
- repo_dir = repository.repo_path( trans.app )
- repo = hg_util.get_repo_for_repository( trans.app, repository=None, repo_path=repo_dir, create=False )
- undesirable_dirs_removed = 0
- undesirable_files_removed = 0
- ok, message = commit_util.check_archive( repository, tar )
- if not ok:
- tar.close()
- uploaded_file.close()
- return ok, message, [], '', undesirable_dirs_removed, undesirable_files_removed
- else:
- if upload_point is not None:
- full_path = os.path.abspath( os.path.join( repo_dir, upload_point ) )
- else:
- full_path = os.path.abspath( repo_dir )
- filenames_in_archive = []
- for tarinfo_obj in tar.getmembers():
- ok = os.path.basename( tarinfo_obj.name ) not in commit_util.UNDESIRABLE_FILES
- if ok:
- for file_path_item in tarinfo_obj.name.split( '/' ):
- if file_path_item in commit_util.UNDESIRABLE_DIRS:
- undesirable_dirs_removed += 1
- ok = False
- break
- else:
- undesirable_files_removed += 1
- if ok:
- filenames_in_archive.append( tarinfo_obj.name )
- # Extract the uploaded tar to the load_point within the repository hierarchy.
- tar.extractall( path=full_path )
- tar.close()
- uploaded_file.close()
- for filename in filenames_in_archive:
- uploaded_file_name = os.path.join( full_path, filename )
- if os.path.split( uploaded_file_name )[ -1 ] == rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME:
- # Inspect the contents of the file to see if toolshed or changeset_revision attributes
- # are missing and if so, set them appropriately.
- altered, root_elem, error_message = rdah.handle_tag_attributes( uploaded_file_name )
- if error_message:
- return False, error_message, [], '', [], []
- elif altered:
- tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
- shutil.move( tmp_filename, uploaded_file_name )
- elif os.path.split( uploaded_file_name )[ -1 ] == rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME:
- # Inspect the contents of the file to see if toolshed or changeset_revision
- # attributes are missing and if so, set them appropriately.
- altered, root_elem, error_message = tdah.handle_tag_attributes( uploaded_file_name )
- if error_message:
- return False, error_message, [], '', [], []
- if altered:
- tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
- shutil.move( tmp_filename, uploaded_file_name )
- return commit_util.handle_directory_changes( trans.app,
- trans.request.host,
- trans.user.username,
- repository,
- full_path,
- filenames_in_archive,
- remove_repo_files_not_in_tar,
- new_repo_alert,
- commit_message,
- undesirable_dirs_removed,
- undesirable_files_removed )
diff -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 -r d94ef8154c63145b0e2928b099017324097d4352 lib/tool_shed/util/repository_content_util.py
--- /dev/null
+++ b/lib/tool_shed/util/repository_content_util.py
@@ -0,0 +1,75 @@
+import os
+import shutil
+
+from tool_shed.util import commit_util
+from tool_shed.util import hg_util
+from tool_shed.util import xml_util
+
+import tool_shed.repository_types.util as rt_util
+
+
+def upload_tar( trans, rdah, tdah, repository, tar, uploaded_file, upload_point, remove_repo_files_not_in_tar,
+ commit_message, new_repo_alert ):
+ # Upload a tar archive of files.
+ repo_dir = repository.repo_path( trans.app )
+ hg_util.get_repo_for_repository( trans.app, repository=None, repo_path=repo_dir, create=False )
+ undesirable_dirs_removed = 0
+ undesirable_files_removed = 0
+ ok, message = commit_util.check_archive( repository, tar )
+ if not ok:
+ tar.close()
+ uploaded_file.close()
+ return ok, message, [], '', undesirable_dirs_removed, undesirable_files_removed
+ else:
+ if upload_point is not None:
+ full_path = os.path.abspath( os.path.join( repo_dir, upload_point ) )
+ else:
+ full_path = os.path.abspath( repo_dir )
+ filenames_in_archive = []
+ for tarinfo_obj in tar.getmembers():
+ ok = os.path.basename( tarinfo_obj.name ) not in commit_util.UNDESIRABLE_FILES
+ if ok:
+ for file_path_item in tarinfo_obj.name.split( '/' ):
+ if file_path_item in commit_util.UNDESIRABLE_DIRS:
+ undesirable_dirs_removed += 1
+ ok = False
+ break
+ else:
+ undesirable_files_removed += 1
+ if ok:
+ filenames_in_archive.append( tarinfo_obj.name )
+ # Extract the uploaded tar to the load_point within the repository hierarchy.
+ tar.extractall( path=full_path )
+ tar.close()
+ uploaded_file.close()
+ for filename in filenames_in_archive:
+ uploaded_file_name = os.path.join( full_path, filename )
+ if os.path.split( uploaded_file_name )[ -1 ] == rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME:
+ # Inspect the contents of the file to see if toolshed or changeset_revision attributes
+ # are missing and if so, set them appropriately.
+ altered, root_elem, error_message = rdah.handle_tag_attributes( uploaded_file_name )
+ if error_message:
+ return False, error_message, [], '', [], []
+ elif altered:
+ tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
+ shutil.move( tmp_filename, uploaded_file_name )
+ elif os.path.split( uploaded_file_name )[ -1 ] == rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME:
+ # Inspect the contents of the file to see if toolshed or changeset_revision
+ # attributes are missing and if so, set them appropriately.
+ altered, root_elem, error_message = tdah.handle_tag_attributes( uploaded_file_name )
+ if error_message:
+ return False, error_message, [], '', [], []
+ if altered:
+ tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
+ shutil.move( tmp_filename, uploaded_file_name )
+ return commit_util.handle_directory_changes( trans.app,
+ trans.request.host,
+ trans.user.username,
+ repository,
+ full_path,
+ filenames_in_archive,
+ remove_repo_files_not_in_tar,
+ new_repo_alert,
+ commit_message,
+ undesirable_dirs_removed,
+ undesirable_files_removed )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: History structure: found a way to sync SVG and DOM when expanding/collapsing jobs and datasets in the vertical layout
by commits-noreply@bitbucket.org 15 Oct '14
by commits-noreply@bitbucket.org 15 Oct '14
15 Oct '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/9728fcd5167c/
Changeset: 9728fcd5167c
User: carlfeberhard
Date: 2014-10-15 20:21:51+00:00
Summary: History structure: found a way to sync SVG and DOM when expanding/collapsing jobs and datasets in the vertical layout
Affected #: 9 files
diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 client/galaxy/scripts/mvc/history/history-structure-view.js
--- a/client/galaxy/scripts/mvc/history/history-structure-view.js
+++ b/client/galaxy/scripts/mvc/history/history-structure-view.js
@@ -52,14 +52,22 @@
//this.debug( job.outputCollection );
// create the bbone view for the job (to be positioned later accrd. to the layout) and cache
- var li = new JOB_LI.JobListItemView({ model: job, expanded: true });
+ var li = new JOB_LI.JobListItemView({ model: job });
//var li = new JOB_LI.JobListItemView({ model: job });
- li.$el.appendTo( view.$el );
+ li.render( 0 ).$el.appendTo( view.$el );
view._jobLiMap[ job.id ] = li;
+ view._setUpJobListeners( li );
+
});
return view.jobs;
},
+ _setUpJobListeners : function( jobLi ){
+ // update the layout during expansion and collapsing of job and output li's
+ jobLi.on( 'expanding expanded collapsing collapsed', this.render, this );
+ jobLi.foldout.on( 'view:expanding view:expanded view:collapsing view:collapsed', this.render, this );
+ },
+
layoutDefaults : {
paddingTop : 8,
paddingLeft : 20,
@@ -131,32 +139,32 @@
},
render : function( options ){
- this.log( this + '.renderComponent:', options );
+ this.debug( this + '.render:', options );
var view = this;
- view.component.eachVertex( function( v ){
- //TODO:? liMap needed - can't we attach to vertex?
- var li = view._jobLiMap[ v.name ];
- if( !li.$el.is( ':visible' ) ){
- li.render( 0 );
- }
- });
+ function _render(){
+ view._updateLayout();
+ // set up the display containers
+ view.$el
+ .width( view.layout.el.width )
+ .height( view.layout.el.height );
+ view.renderSVG();
- view._updateLayout();
- // set up the display containers
- view.$el
- .width( view.layout.el.width )
- .height( view.layout.el.height );
- this.renderSVG();
-
- // position the job views accrd. to the layout
- view.component.eachVertex( function( v ){
- //TODO:? liMap needed - can't we attach to vertex?
- var li = view._jobLiMap[ v.name ],
- position = view.layout.nodeMap[ li.model.id ];
- //this.debug( position );
- li.$el.css({ top: position.y, left: position.x });
- });
+ // position the job views accrd. to the layout
+ view.component.eachVertex( function( v ){
+//TODO:? liMap needed - can't we attach to vertex?
+ var li = view._jobLiMap[ v.name ],
+ position = view.layout.nodeMap[ li.model.id ];
+ //this.debug( position );
+ li.$el.css({ top: position.y, left: position.x });
+ });
+ }
+//TODO: hack - li's invisible in updateLayout without this delay
+ if( !this.$el.is( ':visible' ) ){
+ _.delay( _render, 0 );
+ } else {
+ _render();
+ }
return this;
},
@@ -195,7 +203,7 @@
.on( 'mouseover', highlightConnect )
.on( 'mouseout', unhighlightConnect );
- connections.transition()
+ connections
.attr( 'd', function( d ){ return view._connectionPath( d ); });
//TODO: ? can we use tick here to update the links?
@@ -256,7 +264,7 @@
*/
var VerticalHistoryStructureComponent = HistoryStructureComponent.extend({
- logger : console,
+ //logger : console,
className : HistoryStructureComponent.prototype.className + ' vertical',
@@ -376,8 +384,8 @@
_createComponent : function( component ){
this.log( this + '._createComponent:', component );
- return new HistoryStructureComponent({
- //return new VerticalHistoryStructureComponent({
+ //return new HistoryStructureComponent({
+ return new VerticalHistoryStructureComponent({
model : this.model,
component : component
});
diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 client/galaxy/scripts/mvc/job/job-li.js
--- a/client/galaxy/scripts/mvc/job/job-li.js
+++ b/client/galaxy/scripts/mvc/job/job-li.js
@@ -30,7 +30,6 @@
/** where should pages from links be displayed? (default to new tab/window) */
this.linkTarget = attributes.linkTarget || '_blank';
- this._setUpListeners();
},
/** In this override, add the state as a class for use with state-based CSS */
diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 client/galaxy/scripts/mvc/list/list-item.js
--- a/client/galaxy/scripts/mvc/list/list-item.js
+++ b/client/galaxy/scripts/mvc/list/list-item.js
@@ -17,7 +17,7 @@
/** are the details of this view expanded/shown or not? */
this.expanded = attributes.expanded || false;
this.log( '\t expanded:', this.expanded );
- this.fxSpeed = attributes.fxSpeed || this.fxSpeed;
+ this.fxSpeed = attributes.fxSpeed !== undefined? attributes.fxSpeed : this.fxSpeed;
},
// ........................................................................ render main
@@ -119,15 +119,8 @@
*/
expand : function(){
var view = this;
- return view._fetchModelDetails()
- .always(function(){
- var $newDetails = view._renderDetails();
- view.$details().replaceWith( $newDetails );
- // needs to be set after the above or the slide will not show
- view.expanded = true;
- view.$details().slideDown( view.fxSpeed, function(){
- view.trigger( 'expanded', view );
- });
+ return view._fetchModelDetails().always( function(){
+ view._expand();
});
},
@@ -141,6 +134,24 @@
return jQuery.when();
},
+ /** Inner fn called when expand (public) has fetched the details */
+ _expand : function(){
+ var view = this,
+ $newDetails = view._renderDetails();
+ view.$details().replaceWith( $newDetails );
+ // needs to be set after the above or the slide will not show
+ view.expanded = true;
+ view.$details().slideDown({
+ duration : view.fxSpeed,
+ step: function(){
+ view.trigger( 'expanding', view );
+ },
+ complete: function(){
+ view.trigger( 'expanded', view );
+ }
+ });
+ },
+
/** Hide the body/details of an HDA.
* @fires collapsed when a body has been collapsed
*/
@@ -148,8 +159,14 @@
this.debug( this + '(ExpandableView).collapse' );
var view = this;
view.expanded = false;
- this.$details().slideUp( view.fxSpeed, function(){
- view.trigger( 'collapsed', view );
+ this.$details().slideUp({
+ duration : view.fxSpeed,
+ step: function(){
+ view.trigger( 'collapsing', view );
+ },
+ complete: function(){
+ view.trigger( 'collapsed', view );
+ }
});
}
@@ -377,11 +394,13 @@
* disrespect attributes.expanded if drilldown
*/
initialize : function( attributes ){
- ListItemView.prototype.initialize.call( this, attributes );
//TODO: hackish
if( this.foldoutStyle === 'drilldown' ){ this.expanded = false; }
this.foldoutStyle = attributes.foldoutStyle || this.foldoutStyle;
this.foldoutPanelClass = attributes.foldoutPanelClass || this.foldoutPanelClass;
+
+ ListItemView.prototype.initialize.call( this, attributes );
+ this.foldout = this._createFoldoutPanel();
},
//TODO:?? override to exclude foldout scope?
@@ -395,7 +414,7 @@
//TODO: hackish
if( this.foldoutStyle === 'drilldown' ){ return $(); }
var $newDetails = ListItemView.prototype._renderDetails.call( this );
- return this._attachFoldout( this._createFoldoutPanel(), $newDetails );
+ return this._attachFoldout( this.foldout, $newDetails );
},
/** In this override, handle collection expansion. */
@@ -419,7 +438,8 @@
_getFoldoutPanelOptions : function(){
return {
// propagate foldout style down
- foldoutStyle : this.foldoutStyle
+ foldoutStyle : this.foldoutStyle,
+ fxSpeed : this.fxSpeed
};
},
@@ -438,25 +458,13 @@
return view._fetchModelDetails()
.always(function(){
if( view.foldoutStyle === 'foldout' ){
- view._expandByFoldout();
+ view._expand();
} else if( view.foldoutStyle === 'drilldown' ){
view._expandByDrilldown();
}
});
},
- /** For foldout, call render details then slide down */
- _expandByFoldout : function(){
- var view = this;
- var $newDetails = view._renderDetails();
- view.$details().replaceWith( $newDetails );
- // needs to be set after the above or the slide will not show
- view.expanded = true;
- view.$details().slideDown( view.fxSpeed, function(){
- view.trigger( 'expanded', view );
- });
- },
-
/** For drilldown, set up close handler and fire expanded:drilldown
* containing views can listen to this and handle other things
* (like hiding themselves) by listening for expanded/collapsed:drilldown
@@ -464,7 +472,6 @@
_expandByDrilldown : function(){
var view = this;
// attachment and rendering done by listener
- view.foldout = this._createFoldoutPanel();
view.foldout.on( 'close', function(){
view.trigger( 'collapsed:drilldown', view, view.foldout );
});
diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 static/scripts/mvc/history/history-structure-view.js
--- a/static/scripts/mvc/history/history-structure-view.js
+++ b/static/scripts/mvc/history/history-structure-view.js
@@ -52,14 +52,22 @@
//this.debug( job.outputCollection );
// create the bbone view for the job (to be positioned later accrd. to the layout) and cache
- var li = new JOB_LI.JobListItemView({ model: job, expanded: true });
+ var li = new JOB_LI.JobListItemView({ model: job });
//var li = new JOB_LI.JobListItemView({ model: job });
- li.$el.appendTo( view.$el );
+ li.render( 0 ).$el.appendTo( view.$el );
view._jobLiMap[ job.id ] = li;
+ view._setUpJobListeners( li );
+
});
return view.jobs;
},
+ _setUpJobListeners : function( jobLi ){
+ // update the layout during expansion and collapsing of job and output li's
+ jobLi.on( 'expanding expanded collapsing collapsed', this.render, this );
+ jobLi.foldout.on( 'view:expanding view:expanded view:collapsing view:collapsed', this.render, this );
+ },
+
layoutDefaults : {
paddingTop : 8,
paddingLeft : 20,
@@ -131,32 +139,32 @@
},
render : function( options ){
- this.log( this + '.renderComponent:', options );
+ this.debug( this + '.render:', options );
var view = this;
- view.component.eachVertex( function( v ){
- //TODO:? liMap needed - can't we attach to vertex?
- var li = view._jobLiMap[ v.name ];
- if( !li.$el.is( ':visible' ) ){
- li.render( 0 );
- }
- });
+ function _render(){
+ view._updateLayout();
+ // set up the display containers
+ view.$el
+ .width( view.layout.el.width )
+ .height( view.layout.el.height );
+ view.renderSVG();
- view._updateLayout();
- // set up the display containers
- view.$el
- .width( view.layout.el.width )
- .height( view.layout.el.height );
- this.renderSVG();
-
- // position the job views accrd. to the layout
- view.component.eachVertex( function( v ){
- //TODO:? liMap needed - can't we attach to vertex?
- var li = view._jobLiMap[ v.name ],
- position = view.layout.nodeMap[ li.model.id ];
- //this.debug( position );
- li.$el.css({ top: position.y, left: position.x });
- });
+ // position the job views accrd. to the layout
+ view.component.eachVertex( function( v ){
+//TODO:? liMap needed - can't we attach to vertex?
+ var li = view._jobLiMap[ v.name ],
+ position = view.layout.nodeMap[ li.model.id ];
+ //this.debug( position );
+ li.$el.css({ top: position.y, left: position.x });
+ });
+ }
+//TODO: hack - li's invisible in updateLayout without this delay
+ if( !this.$el.is( ':visible' ) ){
+ _.delay( _render, 0 );
+ } else {
+ _render();
+ }
return this;
},
@@ -195,7 +203,7 @@
.on( 'mouseover', highlightConnect )
.on( 'mouseout', unhighlightConnect );
- connections.transition()
+ connections
.attr( 'd', function( d ){ return view._connectionPath( d ); });
//TODO: ? can we use tick here to update the links?
@@ -256,7 +264,7 @@
*/
var VerticalHistoryStructureComponent = HistoryStructureComponent.extend({
- logger : console,
+ //logger : console,
className : HistoryStructureComponent.prototype.className + ' vertical',
@@ -376,8 +384,8 @@
_createComponent : function( component ){
this.log( this + '._createComponent:', component );
- return new HistoryStructureComponent({
- //return new VerticalHistoryStructureComponent({
+ //return new HistoryStructureComponent({
+ return new VerticalHistoryStructureComponent({
model : this.model,
component : component
});
diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 static/scripts/mvc/job/job-li.js
--- a/static/scripts/mvc/job/job-li.js
+++ b/static/scripts/mvc/job/job-li.js
@@ -30,7 +30,6 @@
/** where should pages from links be displayed? (default to new tab/window) */
this.linkTarget = attributes.linkTarget || '_blank';
- this._setUpListeners();
},
/** In this override, add the state as a class for use with state-based CSS */
diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 static/scripts/mvc/list/list-item.js
--- a/static/scripts/mvc/list/list-item.js
+++ b/static/scripts/mvc/list/list-item.js
@@ -17,7 +17,7 @@
/** are the details of this view expanded/shown or not? */
this.expanded = attributes.expanded || false;
this.log( '\t expanded:', this.expanded );
- this.fxSpeed = attributes.fxSpeed || this.fxSpeed;
+ this.fxSpeed = attributes.fxSpeed !== undefined? attributes.fxSpeed : this.fxSpeed;
},
// ........................................................................ render main
@@ -119,15 +119,8 @@
*/
expand : function(){
var view = this;
- return view._fetchModelDetails()
- .always(function(){
- var $newDetails = view._renderDetails();
- view.$details().replaceWith( $newDetails );
- // needs to be set after the above or the slide will not show
- view.expanded = true;
- view.$details().slideDown( view.fxSpeed, function(){
- view.trigger( 'expanded', view );
- });
+ return view._fetchModelDetails().always( function(){
+ view._expand();
});
},
@@ -141,6 +134,24 @@
return jQuery.when();
},
+ /** Inner fn called when expand (public) has fetched the details */
+ _expand : function(){
+ var view = this,
+ $newDetails = view._renderDetails();
+ view.$details().replaceWith( $newDetails );
+ // needs to be set after the above or the slide will not show
+ view.expanded = true;
+ view.$details().slideDown({
+ duration : view.fxSpeed,
+ step: function(){
+ view.trigger( 'expanding', view );
+ },
+ complete: function(){
+ view.trigger( 'expanded', view );
+ }
+ });
+ },
+
/** Hide the body/details of an HDA.
* @fires collapsed when a body has been collapsed
*/
@@ -148,8 +159,14 @@
this.debug( this + '(ExpandableView).collapse' );
var view = this;
view.expanded = false;
- this.$details().slideUp( view.fxSpeed, function(){
- view.trigger( 'collapsed', view );
+ this.$details().slideUp({
+ duration : view.fxSpeed,
+ step: function(){
+ view.trigger( 'collapsing', view );
+ },
+ complete: function(){
+ view.trigger( 'collapsed', view );
+ }
});
}
@@ -377,11 +394,13 @@
* disrespect attributes.expanded if drilldown
*/
initialize : function( attributes ){
- ListItemView.prototype.initialize.call( this, attributes );
//TODO: hackish
if( this.foldoutStyle === 'drilldown' ){ this.expanded = false; }
this.foldoutStyle = attributes.foldoutStyle || this.foldoutStyle;
this.foldoutPanelClass = attributes.foldoutPanelClass || this.foldoutPanelClass;
+
+ ListItemView.prototype.initialize.call( this, attributes );
+ this.foldout = this._createFoldoutPanel();
},
//TODO:?? override to exclude foldout scope?
@@ -395,7 +414,7 @@
//TODO: hackish
if( this.foldoutStyle === 'drilldown' ){ return $(); }
var $newDetails = ListItemView.prototype._renderDetails.call( this );
- return this._attachFoldout( this._createFoldoutPanel(), $newDetails );
+ return this._attachFoldout( this.foldout, $newDetails );
},
/** In this override, handle collection expansion. */
@@ -419,7 +438,8 @@
_getFoldoutPanelOptions : function(){
return {
// propagate foldout style down
- foldoutStyle : this.foldoutStyle
+ foldoutStyle : this.foldoutStyle,
+ fxSpeed : this.fxSpeed
};
},
@@ -438,25 +458,13 @@
return view._fetchModelDetails()
.always(function(){
if( view.foldoutStyle === 'foldout' ){
- view._expandByFoldout();
+ view._expand();
} else if( view.foldoutStyle === 'drilldown' ){
view._expandByDrilldown();
}
});
},
- /** For foldout, call render details then slide down */
- _expandByFoldout : function(){
- var view = this;
- var $newDetails = view._renderDetails();
- view.$details().replaceWith( $newDetails );
- // needs to be set after the above or the slide will not show
- view.expanded = true;
- view.$details().slideDown( view.fxSpeed, function(){
- view.trigger( 'expanded', view );
- });
- },
-
/** For drilldown, set up close handler and fire expanded:drilldown
* containing views can listen to this and handle other things
* (like hiding themselves) by listening for expanded/collapsed:drilldown
@@ -464,7 +472,6 @@
_expandByDrilldown : function(){
var view = this;
// attachment and rendering done by listener
- view.foldout = this._createFoldoutPanel();
view.foldout.on( 'close', function(){
view.trigger( 'collapsed:drilldown', view, view.foldout );
});
diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 static/scripts/packed/mvc/history/history-structure-view.js
--- a/static/scripts/packed/mvc/history/history-structure-view.js
+++ b/static/scripts/packed/mvc/history/history-structure-view.js
@@ -1,1 +1,1 @@
-define(["mvc/job/job-model","mvc/job/job-li","mvc/history/history-content-model","mvc/history/job-dag","mvc/base-mvc","utils/localization","libs/d3"],function(e,f,i,c,h,a){var b=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure-component",initialize:function(j){this.log(this+"(HistoryStructureComponent).initialize:",j);this.component=j.component;this._jobLiMap={};this._createJobModels();this.layout=this._createLayout(j.layoutOptions)},_createJobModels:function(){var j=this;j.component.eachVertex(function(o){var n=o.data.job,m=new e.Job(n);var l=_.map(m.get("outputs"),function(p){var r=p.src==="hda"?"dataset":"dataset_collection",q=i.typeIdStr(r,p.id);return j.model.contents.get(q)});m.outputCollection.reset(l);m.outputCollection.historyId=j.model.id;var k=new f.JobListItemView({model:m,expanded:true});k.$el.appendTo(j.$el);j._jobLiMap[m.id]=k});return j.jobs},layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobHeight:308,jobWidthSpacing:320,linkAdjX:4,linkAdjY:0},_createLayout:function(l){l=_.defaults(_.clone(l||{}),this.layoutDefaults);var j=this,k=_.values(j.component.vertices),m=_.extend(l,{nodeMap:{},links:[],el:{width:0,height:0},svg:{width:0,height:0,top:0,left:0}});k.forEach(function(n,o){var p={name:n.name,x:0,y:0};m.nodeMap[n.name]=p});j.component.edges(function(o){var n={source:o.source,target:o.target};m.links.push(n)});return m},_updateLayout:function(){var k=this,l=k.layout;l.svg.height=l.paddingTop+(l.linkSpacing*_.size(l.nodeMap));l.el.height=l.svg.height+l.jobHeight;var j=l.paddingLeft,m=l.svg.height;_.each(l.nodeMap,function(o,n){o.x=j;o.y=m;j+=l.jobWidthSpacing});l.el.width=l.svg.width=Math.max(l.el.width,j);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY});return this.layout},render:function(k){this.log(this+".renderComponent:",k);var j=this;j.component.eachVertex(function(m){var l=j._jobLiMap[m.name];if(!l.$el.is(":visible")){l.render(0)}});j._updateLayout();j.$el.width(j.layout.el.width).height(j.layout.el.height);this.renderSVG();j.component.eachVertex(function(n){var m=j._jobLiMap[n.name],l=j.layout.nodeMap[m.model.id];m.$el.css({top:l.y,left:l.x})});return this},renderSVG:function(){var j=this,o=j.layout;var k=d3.select(this.el).select("svg");if(k.empty()){k=d3.select(this.el).append("svg")}k.attr("width",o.svg.width).attr("height",o.svg.height);function m(p){d3.select(this).classed("highlighted",true);j._jobLiMap[p.source].$el.addClass("highlighted");j._jobLiMap[p.target].$el.addClass("highlighted")}function l(p){d3.select(this).classed("highlighted",false);j._jobLiMap[p.source].$el.removeClass("highlighted");j._jobLiMap[p.target].$el.removeClass("highlighted")}var n=k.selectAll(".connection").data(o.links);n.enter().append("path").attr("class","connection").attr("id",function(p){return p.source+"-"+p.target}).on("mouseover",m).on("mouseout",l);n.transition().attr("d",function(p){return j._connectionPath(p)});return k.node()},_connectionPath:function(k){var l=0,j=((k.x2-k.x1)/this.layout.svg.width)*this.layout.svg.height;return["M",k.x1,",",k.y1," ","C",k.x1+l,",",k.y1-j," ",k.x2-l,",",k.y2-j," ",k.x2,",",k.y2].join("")},events:{"mouseover .job.list-item":function(j){this.highlightConnected(j.currentTarget,true)},"mouseout .job.list-item":function(j){this.highlightConnected(j.currentTarget,false)}},highlightConnected:function(p,k){k=k!==undefined?k:true;var j=this,l=j.component,n=k?jQuery.prototype.addClass:jQuery.prototype.removeClass,m=k?"connection highlighted":"connection";var o=n.call($(p),"highlighted"),q=o.attr("id").replace("job-","");l.edges({target:q}).forEach(function(r){n.call(j.$("#job-"+r.source),"highlighted");j.$("#"+r.source+"-"+q).attr("class",m)});l.vertices[q].eachEdge(function(r){n.call(j.$("#job-"+r.target),"highlighted");j.$("#"+q+"-"+r.target).attr("class",m)})},toString:function(){return"HistoryStructureComponent("+this.model.id+")"}});var g=b.extend({logger:console,className:b.prototype.className+" vertical",layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobWidth:308,jobHeight:308,initialSpacing:64,jobSpacing:16,linkAdjX:0,linkAdjY:4},_updateLayout:function(){var k=this,l=k.layout;l.svg.width=l.paddingLeft+(l.linkSpacing*_.size(l.nodeMap));l.el.width=l.svg.width+l.jobWidth;l.el.height=0;var j=l.svg.width,m=l.paddingTop;_.each(l.nodeMap,function(p,o){p.x=j;p.y=m;var n=k._jobLiMap[o];if(n.$el.is(":visible")){m+=n.$el.height()+l.jobSpacing}else{m+=l.initialSpacing+l.jobSpacing}});l.el.height=l.svg.height=Math.max(l.el.height,m);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY;k.debug("link:",n.x1,n.y1,n.x2,n.y2,n)});k.debug("el:",l.el);k.debug("svg:",l.svg);return l},_connectionPath:function(l){var k=0,j=((l.y2-l.y1)/this.layout.svg.height)*this.layout.svg.width;return["M",l.x1,",",l.y1," ","C",l.x1-j,",",l.y1+k," ",l.x2-j,",",l.y2-k," ",l.x2,",",l.y2].join("")},toString:function(){return"VerticalHistoryStructureComponent("+this.model.id+")"}});var d=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure",initialize:function(j){this.log(this+"(HistoryStructureView).initialize:",j,this.model);this.jobs=j.jobs;this._createDAG()},_createDAG:function(){this.dag=new c({historyContents:this.model.contents.toJSON(),jobs:this.jobs,excludeSetMetadata:true,excludeErroredJobs:true});this.log(this+".dag:",this.dag);this._createComponents()},_createComponents:function(){this.log(this+"._createComponents");var j=this;j.componentViews=j.dag.weakComponentGraphArray().map(function(k){return j._createComponent(k)})},_createComponent:function(j){this.log(this+"._createComponent:",j);return new b({model:this.model,component:j})},render:function(k){this.log(this+".render:",k);var j=this;j.$el.html(['<div class="controls"></div>','<div class="components"></div>'].join(""));j.componentViews.forEach(function(l){l.render().$el.appendTo(j.$components())});return j},$components:function(){return this.$(".components")},toString:function(){return"HistoryStructureView()"}});return d});
\ No newline at end of file
+define(["mvc/job/job-model","mvc/job/job-li","mvc/history/history-content-model","mvc/history/job-dag","mvc/base-mvc","utils/localization","libs/d3"],function(e,f,i,c,h,a){var b=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure-component",initialize:function(j){this.log(this+"(HistoryStructureComponent).initialize:",j);this.component=j.component;this._jobLiMap={};this._createJobModels();this.layout=this._createLayout(j.layoutOptions)},_createJobModels:function(){var j=this;j.component.eachVertex(function(o){var n=o.data.job,m=new e.Job(n);var l=_.map(m.get("outputs"),function(p){var r=p.src==="hda"?"dataset":"dataset_collection",q=i.typeIdStr(r,p.id);return j.model.contents.get(q)});m.outputCollection.reset(l);m.outputCollection.historyId=j.model.id;var k=new f.JobListItemView({model:m});k.render(0).$el.appendTo(j.$el);j._jobLiMap[m.id]=k;j._setUpJobListeners(k)});return j.jobs},_setUpJobListeners:function(j){j.on("expanding expanded collapsing collapsed",this.render,this);j.foldout.on("view:expanding view:expanded view:collapsing view:collapsed",this.render,this)},layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobHeight:308,jobWidthSpacing:320,linkAdjX:4,linkAdjY:0},_createLayout:function(l){l=_.defaults(_.clone(l||{}),this.layoutDefaults);var j=this,k=_.values(j.component.vertices),m=_.extend(l,{nodeMap:{},links:[],el:{width:0,height:0},svg:{width:0,height:0,top:0,left:0}});k.forEach(function(n,o){var p={name:n.name,x:0,y:0};m.nodeMap[n.name]=p});j.component.edges(function(o){var n={source:o.source,target:o.target};m.links.push(n)});return m},_updateLayout:function(){var k=this,l=k.layout;l.svg.height=l.paddingTop+(l.linkSpacing*_.size(l.nodeMap));l.el.height=l.svg.height+l.jobHeight;var j=l.paddingLeft,m=l.svg.height;_.each(l.nodeMap,function(o,n){o.x=j;o.y=m;j+=l.jobWidthSpacing});l.el.width=l.svg.width=Math.max(l.el.width,j);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY});return this.layout},render:function(k){this.debug(this+".render:",k);var j=this;function l(){j._updateLayout();j.$el.width(j.layout.el.width).height(j.layout.el.height);j.renderSVG();j.component.eachVertex(function(o){var n=j._jobLiMap[o.name],m=j.layout.nodeMap[n.model.id];n.$el.css({top:m.y,left:m.x})})}if(!this.$el.is(":visible")){_.delay(l,0)}else{l()}return this},renderSVG:function(){var j=this,o=j.layout;var k=d3.select(this.el).select("svg");if(k.empty()){k=d3.select(this.el).append("svg")}k.attr("width",o.svg.width).attr("height",o.svg.height);function m(p){d3.select(this).classed("highlighted",true);j._jobLiMap[p.source].$el.addClass("highlighted");j._jobLiMap[p.target].$el.addClass("highlighted")}function l(p){d3.select(this).classed("highlighted",false);j._jobLiMap[p.source].$el.removeClass("highlighted");j._jobLiMap[p.target].$el.removeClass("highlighted")}var n=k.selectAll(".connection").data(o.links);n.enter().append("path").attr("class","connection").attr("id",function(p){return p.source+"-"+p.target}).on("mouseover",m).on("mouseout",l);n.attr("d",function(p){return j._connectionPath(p)});return k.node()},_connectionPath:function(k){var l=0,j=((k.x2-k.x1)/this.layout.svg.width)*this.layout.svg.height;return["M",k.x1,",",k.y1," ","C",k.x1+l,",",k.y1-j," ",k.x2-l,",",k.y2-j," ",k.x2,",",k.y2].join("")},events:{"mouseover .job.list-item":function(j){this.highlightConnected(j.currentTarget,true)},"mouseout .job.list-item":function(j){this.highlightConnected(j.currentTarget,false)}},highlightConnected:function(p,k){k=k!==undefined?k:true;var j=this,l=j.component,n=k?jQuery.prototype.addClass:jQuery.prototype.removeClass,m=k?"connection highlighted":"connection";var o=n.call($(p),"highlighted"),q=o.attr("id").replace("job-","");l.edges({target:q}).forEach(function(r){n.call(j.$("#job-"+r.source),"highlighted");j.$("#"+r.source+"-"+q).attr("class",m)});l.vertices[q].eachEdge(function(r){n.call(j.$("#job-"+r.target),"highlighted");j.$("#"+q+"-"+r.target).attr("class",m)})},toString:function(){return"HistoryStructureComponent("+this.model.id+")"}});var g=b.extend({className:b.prototype.className+" vertical",layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobWidth:308,jobHeight:308,initialSpacing:64,jobSpacing:16,linkAdjX:0,linkAdjY:4},_updateLayout:function(){var k=this,l=k.layout;l.svg.width=l.paddingLeft+(l.linkSpacing*_.size(l.nodeMap));l.el.width=l.svg.width+l.jobWidth;l.el.height=0;var j=l.svg.width,m=l.paddingTop;_.each(l.nodeMap,function(p,o){p.x=j;p.y=m;var n=k._jobLiMap[o];if(n.$el.is(":visible")){m+=n.$el.height()+l.jobSpacing}else{m+=l.initialSpacing+l.jobSpacing}});l.el.height=l.svg.height=Math.max(l.el.height,m);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY;k.debug("link:",n.x1,n.y1,n.x2,n.y2,n)});k.debug("el:",l.el);k.debug("svg:",l.svg);return l},_connectionPath:function(l){var k=0,j=((l.y2-l.y1)/this.layout.svg.height)*this.layout.svg.width;return["M",l.x1,",",l.y1," ","C",l.x1-j,",",l.y1+k," ",l.x2-j,",",l.y2-k," ",l.x2,",",l.y2].join("")},toString:function(){return"VerticalHistoryStructureComponent("+this.model.id+")"}});var d=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure",initialize:function(j){this.log(this+"(HistoryStructureView).initialize:",j,this.model);this.jobs=j.jobs;this._createDAG()},_createDAG:function(){this.dag=new c({historyContents:this.model.contents.toJSON(),jobs:this.jobs,excludeSetMetadata:true,excludeErroredJobs:true});this.log(this+".dag:",this.dag);this._createComponents()},_createComponents:function(){this.log(this+"._createComponents");var j=this;j.componentViews=j.dag.weakComponentGraphArray().map(function(k){return j._createComponent(k)})},_createComponent:function(j){this.log(this+"._createComponent:",j);return new g({model:this.model,component:j})},render:function(k){this.log(this+".render:",k);var j=this;j.$el.html(['<div class="controls"></div>','<div class="components"></div>'].join(""));j.componentViews.forEach(function(l){l.render().$el.appendTo(j.$components())});return j},$components:function(){return this.$(".components")},toString:function(){return"HistoryStructureView()"}});return d});
\ No newline at end of file
diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 static/scripts/packed/mvc/job/job-li.js
--- a/static/scripts/packed/mvc/job/job-li.js
+++ b/static/scripts/packed/mvc/job/job-li.js
@@ -1,1 +1,1 @@
-define(["mvc/list/list-item","mvc/dataset/dataset-list","mvc/base-mvc","utils/localization"],function(d,f,b,c){var e=d.FoldoutListItemView;var a=e.extend({className:e.prototype.className+" job",id:function(){return["job",this.model.get("id")].join("-")},foldoutPanelClass:f.DatasetList,initialize:function(g){if(g.logger){this.logger=this.model.logger=g.logger}this.log(this+".initialize:",g);e.prototype.initialize.call(this,g);this.linkTarget=g.linkTarget||"_blank";this._setUpListeners()},_swapNewRender:function(g){e.prototype._swapNewRender.call(this,g);if(this.model.has("state")){this.$el.addClass("state-"+this.model.get("state"))}return this.$el},_getFoldoutPanelOptions:function(){var g=e.prototype._getFoldoutPanelOptions.call(this);return _.extend(g,{collection:this.model.outputCollection,selecting:false})},toString:function(){return"JobListItemView("+this.model+")"}});a.prototype.templates=(function(){var g=b.wrapTemplate(['<div class="list-element">','<div class="id"><%= model.id %></div>','<div class="warnings"></div>','<div class="selector">','<span class="fa fa-2x fa-square-o"></span>',"</div>",'<div class="primary-actions"></div>','<div class="title-bar"></div>','<div class="details"></div>',"</div>"]);var h=b.wrapTemplate(['<div class="title-bar clear" tabindex="0">','<div class="title">','<span class="name"><%- job.tool_id %></span>',"</div>",'<div class="subtitle"></div>',"</div>"],"job");return _.extend({},e.prototype.templates,{titleBar:h,})}());return{JobListItemView:a}});
\ No newline at end of file
+define(["mvc/list/list-item","mvc/dataset/dataset-list","mvc/base-mvc","utils/localization"],function(d,f,b,c){var e=d.FoldoutListItemView;var a=e.extend({className:e.prototype.className+" job",id:function(){return["job",this.model.get("id")].join("-")},foldoutPanelClass:f.DatasetList,initialize:function(g){if(g.logger){this.logger=this.model.logger=g.logger}this.log(this+".initialize:",g);e.prototype.initialize.call(this,g);this.linkTarget=g.linkTarget||"_blank"},_swapNewRender:function(g){e.prototype._swapNewRender.call(this,g);if(this.model.has("state")){this.$el.addClass("state-"+this.model.get("state"))}return this.$el},_getFoldoutPanelOptions:function(){var g=e.prototype._getFoldoutPanelOptions.call(this);return _.extend(g,{collection:this.model.outputCollection,selecting:false})},toString:function(){return"JobListItemView("+this.model+")"}});a.prototype.templates=(function(){var g=b.wrapTemplate(['<div class="list-element">','<div class="id"><%= model.id %></div>','<div class="warnings"></div>','<div class="selector">','<span class="fa fa-2x fa-square-o"></span>',"</div>",'<div class="primary-actions"></div>','<div class="title-bar"></div>','<div class="details"></div>',"</div>"]);var h=b.wrapTemplate(['<div class="title-bar clear" tabindex="0">','<div class="title">','<span class="name"><%- job.tool_id %></span>',"</div>",'<div class="subtitle"></div>',"</div>"],"job");return _.extend({},e.prototype.templates,{titleBar:h,})}());return{JobListItemView:a}});
\ No newline at end of file
diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 static/scripts/packed/mvc/list/list-item.js
--- a/static/scripts/packed/mvc/list/list-item.js
+++ b/static/scripts/packed/mvc/list/list-item.js
@@ -1,1 +1,1 @@
-define(["mvc/base-mvc","utils/localization"],function(b,d){var e=Backbone.View.extend(b.LoggableMixin).extend({initialize:function(f){this.expanded=f.expanded||false;this.log("\t expanded:",this.expanded);this.fxSpeed=f.fxSpeed||this.fxSpeed},fxSpeed:"fast",render:function(g){var f=this._buildNewRender();this._setUpBehaviors(f);this._queueNewRender(f,g);return this},_buildNewRender:function(){var f=$(this.templates.el(this.model.toJSON(),this));if(this.expanded){this.$details(f).replaceWith(this._renderDetails().show())}return f},_queueNewRender:function(g,h){h=(h===undefined)?(this.fxSpeed):(h);var f=this;$(f).queue("fx",[function(i){this.$el.fadeOut(h,i)},function(i){f._swapNewRender(g);i()},function(i){this.$el.fadeIn(h,i)},function(i){this.trigger("rendered",f);i()}])},_swapNewRender:function(f){return this.$el.empty().attr("class",_.isFunction(this.className)?this.className():this.className).append(f.children())},_setUpBehaviors:function(f){f=f||this.$el;f.find("[title]").tooltip({placement:"bottom"})},$details:function(f){f=f||this.$el;return f.find("> .details")},_renderDetails:function(){var f=$(this.templates.details(this.model.toJSON(),this));this._setUpBehaviors(f);return f},toggleExpanded:function(f){f=(f===undefined)?(!this.expanded):(f);if(f){this.expand()}else{this.collapse()}return this},expand:function(){var f=this;return f._fetchModelDetails().always(function(){var g=f._renderDetails();f.$details().replaceWith(g);f.expanded=true;f.$details().slideDown(f.fxSpeed,function(){f.trigger("expanded",f)})})},_fetchModelDetails:function(){if(!this.model.hasDetails()){return this.model.fetch()}return jQuery.when()},collapse:function(){this.debug(this+"(ExpandableView).collapse");var f=this;f.expanded=false;this.$details().slideUp(f.fxSpeed,function(){f.trigger("collapsed",f)})}});var a=e.extend(b.mixin(b.SelectableViewMixin,b.DraggableViewMixin,{tagName:"div",className:"list-item",initialize:function(f){e.prototype.initialize.call(this,f);b.SelectableViewMixin.initialize.call(this,f);b.DraggableViewMixin.initialize.call(this,f);this._setUpListeners()},_setUpListeners:function(){this.on("selectable",function(f){if(f){this.$(".primary-actions").hide()}else{this.$(".primary-actions").show()}},this);return this},_buildNewRender:function(){var f=e.prototype._buildNewRender.call(this);f.children(".warnings").replaceWith(this._renderWarnings());f.children(".title-bar").replaceWith(this._renderTitleBar());f.children(".primary-actions").append(this._renderPrimaryActions());f.find("> .title-bar .subtitle").replaceWith(this._renderSubtitle());return f},_swapNewRender:function(f){e.prototype._swapNewRender.call(this,f);if(this.selectable){this.showSelector(0)}if(this.draggable){this.draggableOn()}return this.$el},_renderWarnings:function(){var f=this,h=$('<div class="warnings"></div>'),g=f.model.toJSON();_.each(f.templates.warnings,function(i){h.append($(i(g,f)))});return h},_renderTitleBar:function(){return $(this.templates.titleBar(this.model.toJSON(),this))},_renderPrimaryActions:function(){return[]},_renderSubtitle:function(){return $(this.templates.subtitle(this.model.toJSON(),this))},events:{"click .title-bar":"_clickTitleBar","keydown .title-bar":"_keyDownTitleBar","click .selector":"toggleSelect"},_clickTitleBar:function(f){f.stopPropagation();if(f.altKey){this.toggleSelect(f);if(!this.selectable){this.showSelector()}}else{this.toggleExpanded()}},_keyDownTitleBar:function(h){var f=32,g=13;if(h&&(h.type==="keydown")&&(h.keyCode===f||h.keyCode===g)){this.toggleExpanded();h.stopPropagation();return false}return true},toString:function(){var f=(this.model)?(this.model+""):("(no model)");return"ListItemView("+f+")"}}));a.prototype.templates=(function(){var h=b.wrapTemplate(['<div class="list-element">','<div class="warnings"></div>','<div class="selector">','<span class="fa fa-2x fa-square-o"></span>',"</div>",'<div class="primary-actions"></div>','<div class="title-bar"></div>','<div class="details"></div>',"</div>"]);var f={};var i=b.wrapTemplate(['<div class="title-bar clear" tabindex="0">','<span class="state-icon"></span>','<div class="title">','<span class="name"><%- element.name %></span>',"</div>",'<div class="subtitle"></div>',"</div>"],"element");var j=b.wrapTemplate(['<div class="subtitle"></div>']);var g=b.wrapTemplate(['<div class="details"></div>']);return{el:h,warnings:f,titleBar:i,subtitle:j,details:g}}());var c=a.extend({foldoutStyle:"foldout",foldoutPanelClass:null,initialize:function(f){a.prototype.initialize.call(this,f);if(this.foldoutStyle==="drilldown"){this.expanded=false}this.foldoutStyle=f.foldoutStyle||this.foldoutStyle;this.foldoutPanelClass=f.foldoutPanelClass||this.foldoutPanelClass},_renderDetails:function(){if(this.foldoutStyle==="drilldown"){return $()}var f=a.prototype._renderDetails.call(this);return this._attachFoldout(this._createFoldoutPanel(),f)},_createFoldoutPanel:function(){var h=this.model;var i=this._getFoldoutPanelClass(h),g=this._getFoldoutPanelOptions(h),f=new i(_.extend(g,{model:h}));return f},_getFoldoutPanelClass:function(){return this.foldoutPanelClass},_getFoldoutPanelOptions:function(){return{foldoutStyle:this.foldoutStyle}},_attachFoldout:function(f,g){g=g||this.$("> .details");this.foldout=f.render(0);f.$("> .controls").hide();return g.append(f.$el)},expand:function(){var f=this;return f._fetchModelDetails().always(function(){if(f.foldoutStyle==="foldout"){f._expandByFoldout()}else{if(f.foldoutStyle==="drilldown"){f._expandByDrilldown()}}})},_expandByFoldout:function(){var f=this;var g=f._renderDetails();f.$details().replaceWith(g);f.expanded=true;f.$details().slideDown(f.fxSpeed,function(){f.trigger("expanded",f)})},_expandByDrilldown:function(){var f=this;f.foldout=this._createFoldoutPanel();f.foldout.on("close",function(){f.trigger("collapsed:drilldown",f,f.foldout)});f.trigger("expanded:drilldown",f,f.foldout)}});c.prototype.templates=(function(){var f=b.wrapTemplate(['<div class="details">',"</div>"],"collection");return _.extend({},a.prototype.templates,{details:f})}());return{ExpandableView:e,ListItemView:a,FoldoutListItemView:c}});
\ No newline at end of file
+define(["mvc/base-mvc","utils/localization"],function(b,d){var e=Backbone.View.extend(b.LoggableMixin).extend({initialize:function(f){this.expanded=f.expanded||false;this.log("\t expanded:",this.expanded);this.fxSpeed=f.fxSpeed!==undefined?f.fxSpeed:this.fxSpeed},fxSpeed:"fast",render:function(g){var f=this._buildNewRender();this._setUpBehaviors(f);this._queueNewRender(f,g);return this},_buildNewRender:function(){var f=$(this.templates.el(this.model.toJSON(),this));if(this.expanded){this.$details(f).replaceWith(this._renderDetails().show())}return f},_queueNewRender:function(g,h){h=(h===undefined)?(this.fxSpeed):(h);var f=this;$(f).queue("fx",[function(i){this.$el.fadeOut(h,i)},function(i){f._swapNewRender(g);i()},function(i){this.$el.fadeIn(h,i)},function(i){this.trigger("rendered",f);i()}])},_swapNewRender:function(f){return this.$el.empty().attr("class",_.isFunction(this.className)?this.className():this.className).append(f.children())},_setUpBehaviors:function(f){f=f||this.$el;f.find("[title]").tooltip({placement:"bottom"})},$details:function(f){f=f||this.$el;return f.find("> .details")},_renderDetails:function(){var f=$(this.templates.details(this.model.toJSON(),this));this._setUpBehaviors(f);return f},toggleExpanded:function(f){f=(f===undefined)?(!this.expanded):(f);if(f){this.expand()}else{this.collapse()}return this},expand:function(){var f=this;return f._fetchModelDetails().always(function(){f._expand()})},_fetchModelDetails:function(){if(!this.model.hasDetails()){return this.model.fetch()}return jQuery.when()},_expand:function(){var f=this,g=f._renderDetails();f.$details().replaceWith(g);f.expanded=true;f.$details().slideDown({duration:f.fxSpeed,step:function(){f.trigger("expanding",f)},complete:function(){f.trigger("expanded",f)}})},collapse:function(){this.debug(this+"(ExpandableView).collapse");var f=this;f.expanded=false;this.$details().slideUp({duration:f.fxSpeed,step:function(){f.trigger("collapsing",f)},complete:function(){f.trigger("collapsed",f)}})}});var a=e.extend(b.mixin(b.SelectableViewMixin,b.DraggableViewMixin,{tagName:"div",className:"list-item",initialize:function(f){e.prototype.initialize.call(this,f);b.SelectableViewMixin.initialize.call(this,f);b.DraggableViewMixin.initialize.call(this,f);this._setUpListeners()},_setUpListeners:function(){this.on("selectable",function(f){if(f){this.$(".primary-actions").hide()}else{this.$(".primary-actions").show()}},this);return this},_buildNewRender:function(){var f=e.prototype._buildNewRender.call(this);f.children(".warnings").replaceWith(this._renderWarnings());f.children(".title-bar").replaceWith(this._renderTitleBar());f.children(".primary-actions").append(this._renderPrimaryActions());f.find("> .title-bar .subtitle").replaceWith(this._renderSubtitle());return f},_swapNewRender:function(f){e.prototype._swapNewRender.call(this,f);if(this.selectable){this.showSelector(0)}if(this.draggable){this.draggableOn()}return this.$el},_renderWarnings:function(){var f=this,h=$('<div class="warnings"></div>'),g=f.model.toJSON();_.each(f.templates.warnings,function(i){h.append($(i(g,f)))});return h},_renderTitleBar:function(){return $(this.templates.titleBar(this.model.toJSON(),this))},_renderPrimaryActions:function(){return[]},_renderSubtitle:function(){return $(this.templates.subtitle(this.model.toJSON(),this))},events:{"click .title-bar":"_clickTitleBar","keydown .title-bar":"_keyDownTitleBar","click .selector":"toggleSelect"},_clickTitleBar:function(f){f.stopPropagation();if(f.altKey){this.toggleSelect(f);if(!this.selectable){this.showSelector()}}else{this.toggleExpanded()}},_keyDownTitleBar:function(h){var f=32,g=13;if(h&&(h.type==="keydown")&&(h.keyCode===f||h.keyCode===g)){this.toggleExpanded();h.stopPropagation();return false}return true},toString:function(){var f=(this.model)?(this.model+""):("(no model)");return"ListItemView("+f+")"}}));a.prototype.templates=(function(){var h=b.wrapTemplate(['<div class="list-element">','<div class="warnings"></div>','<div class="selector">','<span class="fa fa-2x fa-square-o"></span>',"</div>",'<div class="primary-actions"></div>','<div class="title-bar"></div>','<div class="details"></div>',"</div>"]);var f={};var i=b.wrapTemplate(['<div class="title-bar clear" tabindex="0">','<span class="state-icon"></span>','<div class="title">','<span class="name"><%- element.name %></span>',"</div>",'<div class="subtitle"></div>',"</div>"],"element");var j=b.wrapTemplate(['<div class="subtitle"></div>']);var g=b.wrapTemplate(['<div class="details"></div>']);return{el:h,warnings:f,titleBar:i,subtitle:j,details:g}}());var c=a.extend({foldoutStyle:"foldout",foldoutPanelClass:null,initialize:function(f){if(this.foldoutStyle==="drilldown"){this.expanded=false}this.foldoutStyle=f.foldoutStyle||this.foldoutStyle;this.foldoutPanelClass=f.foldoutPanelClass||this.foldoutPanelClass;a.prototype.initialize.call(this,f);this.foldout=this._createFoldoutPanel()},_renderDetails:function(){if(this.foldoutStyle==="drilldown"){return $()}var f=a.prototype._renderDetails.call(this);return this._attachFoldout(this.foldout,f)},_createFoldoutPanel:function(){var h=this.model;var i=this._getFoldoutPanelClass(h),g=this._getFoldoutPanelOptions(h),f=new i(_.extend(g,{model:h}));return f},_getFoldoutPanelClass:function(){return this.foldoutPanelClass},_getFoldoutPanelOptions:function(){return{foldoutStyle:this.foldoutStyle,fxSpeed:this.fxSpeed}},_attachFoldout:function(f,g){g=g||this.$("> .details");this.foldout=f.render(0);f.$("> .controls").hide();return g.append(f.$el)},expand:function(){var f=this;return f._fetchModelDetails().always(function(){if(f.foldoutStyle==="foldout"){f._expand()}else{if(f.foldoutStyle==="drilldown"){f._expandByDrilldown()}}})},_expandByDrilldown:function(){var f=this;f.foldout.on("close",function(){f.trigger("collapsed:drilldown",f,f.foldout)});f.trigger("expanded:drilldown",f,f.foldout)}});c.prototype.templates=(function(){var f=b.wrapTemplate(['<div class="details">',"</div>"],"collection");return _.extend({},a.prototype.templates,{details:f})}());return{ExpandableView:e,ListItemView:a,FoldoutListItemView:c}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: guerler: ToolForm: Activate rerun feature for new tool form, fix sliders, fix content labels
by commits-noreply@bitbucket.org 15 Oct '14
by commits-noreply@bitbucket.org 15 Oct '14
15 Oct '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/78124761ad55/
Changeset: 78124761ad55
User: guerler
Date: 2014-10-15 20:06:03+00:00
Summary: ToolForm: Activate rerun feature for new tool form, fix sliders, fix content labels
Affected #: 20 files
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd client/galaxy/scripts/mvc/tools/tools-form.js
--- a/client/galaxy/scripts/mvc/tools/tools-form.js
+++ b/client/galaxy/scripts/mvc/tools/tools-form.js
@@ -97,7 +97,7 @@
// finalize data
var current_state = this.tree.finalize({
data : function(dict) {
- if (dict.values.length > 0 && dict.values[0].src === 'hda') {
+ if (dict.values.length > 0 && dict.values[0] && dict.values[0].src === 'hda') {
return self.content.get({id: dict.values[0].id}).dataset_id;
}
return null;
@@ -185,11 +185,11 @@
});
// switch to classic tool form mako if the form definition is incompatible
- //if (this.incompatible) {
+ if (this.incompatible) {
this.$el.hide();
$('#tool-form-classic').show();
return;
- //}
+ }
// create portlet
this.portlet = new Portlet.View({
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd client/galaxy/scripts/mvc/tools/tools-jobs.js
--- a/client/galaxy/scripts/mvc/tools/tools-jobs.js
+++ b/client/galaxy/scripts/mvc/tools/tools-jobs.js
@@ -37,7 +37,7 @@
console.debug(job_def);
// show progress modal
- this.app.modal.show({title: 'Please wait...', body: 'progress', buttons: { 'Close' : function () {self.app.modal.hide();} }});
+ this.app.modal.show({title: 'Please wait...', body: 'progress', closing_events: true, buttons: { 'Close' : function () {self.app.modal.hide();} }});
// post job
Utils.request({
@@ -59,6 +59,15 @@
} else {
// show error message with details
console.debug(response);
+ self.app.modal.show({
+ title : 'Job submission failed',
+ body : ToolTemplate.error(job_def),
+ buttons : {
+ 'Close' : function() {
+ self.app.modal.hide();
+ }
+ }
+ });
}
}
});
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd client/galaxy/scripts/mvc/tools/tools-section.js
--- a/client/galaxy/scripts/mvc/tools/tools-section.js
+++ b/client/galaxy/scripts/mvc/tools/tools-section.js
@@ -529,22 +529,12 @@
/** Slider field
*/
_fieldSlider: function(input_def) {
- // set min/max
- input_def.min = input_def.min || 0;
- input_def.max = input_def.max || 100000;
-
- // calculate step size
- var step = 1;
- if (input_def.type == 'float') {
- step = (input_def.max - input_def.min) / 10000;
- }
-
// create slider
return new Ui.Slider.View({
id : 'field-' + input_def.id,
+ precise : input_def.type == 'float',
min : input_def.min,
- max : input_def.max,
- step : step
+ max : input_def.max
});
},
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd client/galaxy/scripts/mvc/tools/tools-select-content.js
--- a/client/galaxy/scripts/mvc/tools/tools-select-content.js
+++ b/client/galaxy/scripts/mvc/tools/tools-select-content.js
@@ -40,7 +40,7 @@
var dataset_options = [];
for (var i in datasets) {
dataset_options.push({
- label: datasets[i].name,
+ label: datasets[i].hid + ': ' + datasets[i].name,
value: datasets[i].id
});
}
@@ -67,7 +67,7 @@
var collection_options = [];
for (var i in collections) {
collection_options.push({
- label: collections[i].name,
+ label: collections[i].hid + ': ' + collections[i].name,
value: collections[i].id
});
}
@@ -103,20 +103,34 @@
},
/** Return the currently selected dataset values */
- value : function () {
- // identify select element
- var select = null;
- switch(this.current) {
- case 'hda':
- select = this.select_datasets;
- break;
- case 'hdca':
- select = this.select_collection;
- break;
+ value : function (dict) {
+ // update current value
+ if (dict !== undefined) {
+ if (dict.values.length > 0 && dict.values[0] && dict.values[0].src) {
+ // create list
+ var list = [];
+ for (var i in dict.values) {
+ list.push(dict.values[i].id);
+ }
+
+ // set source
+ this.current = dict.values[0].src;
+ this.refresh();
+
+ // identify select element
+ switch(this.current) {
+ case 'hda':
+ this.select_datasets.value(list);
+ break;
+ case 'hdca':
+ this.select_collection.value(list[0]);
+ break;
+ }
+ }
}
// transform into an array
- var id_list = select.value();
+ var id_list = this._select().value();
if (!(id_list instanceof Array)) {
id_list = [id_list];
}
@@ -142,12 +156,7 @@
/** Validate current selection
*/
validate: function() {
- switch(this.current) {
- case 'hda':
- return this.select_datasets.validate();
- case 'hdca':
- return this.select_collection.validate();
- }
+ return this._select().validate();
},
/** Refreshes data selection view */
@@ -162,6 +171,16 @@
this.select_collection.$el.fadeIn();
break;
}
+ },
+
+ /** Assists in selecting the current field */
+ _select: function() {
+ switch(this.current) {
+ case 'hdca':
+ return this.select_collection;
+ default:
+ return this.select_datasets;
+ }
}
});
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd client/galaxy/scripts/mvc/tools/tools-template.js
--- a/client/galaxy/scripts/mvc/tools/tools-template.js
+++ b/client/galaxy/scripts/mvc/tools/tools-template.js
@@ -49,7 +49,7 @@
error: function(job_def) {
return '<div>' +
'<p>' +
- 'Sorry, the server could not complete the request. Please contact the Galaxy Team if this error is persistent.' +
+ 'The server could not complete the request. Please contact the Galaxy Team if this error persists.' +
'</p>' +
'<textarea class="ui-textarea" disabled style="color: black;" rows="6">' +
JSON.stringify(job_def, undefined, 4) +
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd client/galaxy/scripts/mvc/ui/ui-slider.js
--- a/client/galaxy/scripts/mvc/ui/ui-slider.js
+++ b/client/galaxy/scripts/mvc/ui/ui-slider.js
@@ -6,9 +6,11 @@
// options
optionsDefault: {
value : '',
- min : 1,
- max : 100,
- step : 1
+ min : null,
+ max : null,
+ step : null,
+ precise : false,
+ split : 10000
},
// initialize
@@ -18,19 +20,35 @@
// configure options
this.options = Utils.merge(options, this.optionsDefault);
-
+
// create new element
this.setElement(this._template(this.options));
- // backup slider
- this.$slider = this.$el.find('#slider');
+ // determine wether to use the slider
+ this.useslider = this.options.max !== null && this.options.min !== null && this.options.max > this.options.min;
+
+ // set default step size
+ if (this.options.step === null) {
+ this.options.step = 1;
+ if (this.options.precise && this.useslider) {
+ this.options.step = (this.options.max - this.options.min) / this.options.split;
+ }
+ }
+
+ // create slider if min and max are defined properly
+ if (this.useslider) {
+ this.$slider = this.$el.find('#slider');
+ this.$slider.slider(this.options);
+ this.$slider.on('slide', function (event, ui) {
+ self.value(ui.value);
+ });
+ } else {
+ this.$el.find('.ui-form-slider-text').css('width', '100%');
+ }
// backup integer field
this.$text = this.$el.find('#text');
- // load slider plugin
- this.$slider.slider(this.options);
-
// add text field event
this.$text.on('change', function () {
self.value($(this).val());
@@ -39,24 +57,23 @@
// add text field event
this.$text.on('keydown', function (event) {
var v = event.which;
- console.log(v);
if (!(v == 8 || v == 9 || v == 13 || v == 37 || v == 39 || v == 189 || (v >= 48 && v <= 57)
- || (self.options.step != 1 && $(this).val().indexOf('.') == -1) && v == 190)) {
+ || (self.options.precise && $(this).val().indexOf('.') == -1) && v == 190)) {
event.preventDefault();
}
});
-
- // add slider event
- this.$slider.on('slide', function (event, ui) {
- self.value(ui.value);
- });
},
// value
value : function (new_val) {
if (new_val !== undefined) {
- // limit
- new_val = Math.max(Math.min(new_val, this.options.max), this.options.min);
+ // apply limit
+ if (this.options.max !== null) {
+ new_val = Math.min(new_val, this.options.max);
+ }
+ if (this.options.min !== null) {
+ new_val = Math.max(new_val, this.options.min);
+ }
// trigger on change event
if (this.options.onchange) {
@@ -64,7 +81,7 @@
}
// set values
- this.$slider.slider('value', new_val);
+ this.$slider && this.$slider.slider('value', new_val);
this.$text.val(new_val);
}
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -1278,7 +1278,7 @@
def to_dict( self, trans, view='collection', value_mapper=None, other_values={} ):
# call parent to_dict
- d = super( ColumnListParameter, self ).to_dict( trans, other_values )
+ d = super( ColumnListParameter, self ).to_dict( trans, other_values=other_values)
# add data reference
d['data_ref'] = self.data_ref
@@ -1432,7 +1432,10 @@
call_other_values = { '__trans__': trans, '__value__': value }
if other_values:
call_other_values.update( other_values.dict )
- return eval( self.dynamic_options, self.tool.code_namespace, call_other_values )
+ try:
+ return eval( self.dynamic_options, self.tool.code_namespace, call_other_values )
+ except Exception:
+ return []
def get_options( self, trans=None, value=None, other_values={} ):
if self.is_dynamic:
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd static/scripts/mvc/tools/tools-form.js
--- a/static/scripts/mvc/tools/tools-form.js
+++ b/static/scripts/mvc/tools/tools-form.js
@@ -97,7 +97,7 @@
// finalize data
var current_state = this.tree.finalize({
data : function(dict) {
- if (dict.values.length > 0 && dict.values[0].src === 'hda') {
+ if (dict.values.length > 0 && dict.values[0] && dict.values[0].src === 'hda') {
return self.content.get({id: dict.values[0].id}).dataset_id;
}
return null;
@@ -185,11 +185,11 @@
});
// switch to classic tool form mako if the form definition is incompatible
- //if (this.incompatible) {
+ if (this.incompatible) {
this.$el.hide();
$('#tool-form-classic').show();
return;
- //}
+ }
// create portlet
this.portlet = new Portlet.View({
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd static/scripts/mvc/tools/tools-jobs.js
--- a/static/scripts/mvc/tools/tools-jobs.js
+++ b/static/scripts/mvc/tools/tools-jobs.js
@@ -37,7 +37,7 @@
console.debug(job_def);
// show progress modal
- this.app.modal.show({title: 'Please wait...', body: 'progress', buttons: { 'Close' : function () {self.app.modal.hide();} }});
+ this.app.modal.show({title: 'Please wait...', body: 'progress', closing_events: true, buttons: { 'Close' : function () {self.app.modal.hide();} }});
// post job
Utils.request({
@@ -59,6 +59,15 @@
} else {
// show error message with details
console.debug(response);
+ self.app.modal.show({
+ title : 'Job submission failed',
+ body : ToolTemplate.error(job_def),
+ buttons : {
+ 'Close' : function() {
+ self.app.modal.hide();
+ }
+ }
+ });
}
}
});
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd static/scripts/mvc/tools/tools-section.js
--- a/static/scripts/mvc/tools/tools-section.js
+++ b/static/scripts/mvc/tools/tools-section.js
@@ -529,22 +529,12 @@
/** Slider field
*/
_fieldSlider: function(input_def) {
- // set min/max
- input_def.min = input_def.min || 0;
- input_def.max = input_def.max || 100000;
-
- // calculate step size
- var step = 1;
- if (input_def.type == 'float') {
- step = (input_def.max - input_def.min) / 10000;
- }
-
// create slider
return new Ui.Slider.View({
id : 'field-' + input_def.id,
+ precise : input_def.type == 'float',
min : input_def.min,
- max : input_def.max,
- step : step
+ max : input_def.max
});
},
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd static/scripts/mvc/tools/tools-select-content.js
--- a/static/scripts/mvc/tools/tools-select-content.js
+++ b/static/scripts/mvc/tools/tools-select-content.js
@@ -40,7 +40,7 @@
var dataset_options = [];
for (var i in datasets) {
dataset_options.push({
- label: datasets[i].name,
+ label: datasets[i].hid + ': ' + datasets[i].name,
value: datasets[i].id
});
}
@@ -67,7 +67,7 @@
var collection_options = [];
for (var i in collections) {
collection_options.push({
- label: collections[i].name,
+ label: collections[i].hid + ': ' + collections[i].name,
value: collections[i].id
});
}
@@ -103,20 +103,34 @@
},
/** Return the currently selected dataset values */
- value : function () {
- // identify select element
- var select = null;
- switch(this.current) {
- case 'hda':
- select = this.select_datasets;
- break;
- case 'hdca':
- select = this.select_collection;
- break;
+ value : function (dict) {
+ // update current value
+ if (dict !== undefined) {
+ if (dict.values.length > 0 && dict.values[0] && dict.values[0].src) {
+ // create list
+ var list = [];
+ for (var i in dict.values) {
+ list.push(dict.values[i].id);
+ }
+
+ // set source
+ this.current = dict.values[0].src;
+ this.refresh();
+
+ // identify select element
+ switch(this.current) {
+ case 'hda':
+ this.select_datasets.value(list);
+ break;
+ case 'hdca':
+ this.select_collection.value(list[0]);
+ break;
+ }
+ }
}
// transform into an array
- var id_list = select.value();
+ var id_list = this._select().value();
if (!(id_list instanceof Array)) {
id_list = [id_list];
}
@@ -142,12 +156,7 @@
/** Validate current selection
*/
validate: function() {
- switch(this.current) {
- case 'hda':
- return this.select_datasets.validate();
- case 'hdca':
- return this.select_collection.validate();
- }
+ return this._select().validate();
},
/** Refreshes data selection view */
@@ -162,6 +171,16 @@
this.select_collection.$el.fadeIn();
break;
}
+ },
+
+ /** Assists in selecting the current field */
+ _select: function() {
+ switch(this.current) {
+ case 'hdca':
+ return this.select_collection;
+ default:
+ return this.select_datasets;
+ }
}
});
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd static/scripts/mvc/tools/tools-template.js
--- a/static/scripts/mvc/tools/tools-template.js
+++ b/static/scripts/mvc/tools/tools-template.js
@@ -49,7 +49,7 @@
error: function(job_def) {
return '<div>' +
'<p>' +
- 'Sorry, the server could not complete the request. Please contact the Galaxy Team if this error is persistent.' +
+ 'The server could not complete the request. Please contact the Galaxy Team if this error persists.' +
'</p>' +
'<textarea class="ui-textarea" disabled style="color: black;" rows="6">' +
JSON.stringify(job_def, undefined, 4) +
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd static/scripts/mvc/ui/ui-slider.js
--- a/static/scripts/mvc/ui/ui-slider.js
+++ b/static/scripts/mvc/ui/ui-slider.js
@@ -6,9 +6,11 @@
// options
optionsDefault: {
value : '',
- min : 1,
- max : 100,
- step : 1
+ min : null,
+ max : null,
+ step : null,
+ precise : false,
+ split : 10000
},
// initialize
@@ -18,19 +20,35 @@
// configure options
this.options = Utils.merge(options, this.optionsDefault);
-
+
// create new element
this.setElement(this._template(this.options));
- // backup slider
- this.$slider = this.$el.find('#slider');
+ // determine wether to use the slider
+ this.useslider = this.options.max !== null && this.options.min !== null && this.options.max > this.options.min;
+
+ // set default step size
+ if (this.options.step === null) {
+ this.options.step = 1;
+ if (this.options.precise && this.useslider) {
+ this.options.step = (this.options.max - this.options.min) / this.options.split;
+ }
+ }
+
+ // create slider if min and max are defined properly
+ if (this.useslider) {
+ this.$slider = this.$el.find('#slider');
+ this.$slider.slider(this.options);
+ this.$slider.on('slide', function (event, ui) {
+ self.value(ui.value);
+ });
+ } else {
+ this.$el.find('.ui-form-slider-text').css('width', '100%');
+ }
// backup integer field
this.$text = this.$el.find('#text');
- // load slider plugin
- this.$slider.slider(this.options);
-
// add text field event
this.$text.on('change', function () {
self.value($(this).val());
@@ -39,24 +57,23 @@
// add text field event
this.$text.on('keydown', function (event) {
var v = event.which;
- console.log(v);
if (!(v == 8 || v == 9 || v == 13 || v == 37 || v == 39 || v == 189 || (v >= 48 && v <= 57)
- || (self.options.step != 1 && $(this).val().indexOf('.') == -1) && v == 190)) {
+ || (self.options.precise && $(this).val().indexOf('.') == -1) && v == 190)) {
event.preventDefault();
}
});
-
- // add slider event
- this.$slider.on('slide', function (event, ui) {
- self.value(ui.value);
- });
},
// value
value : function (new_val) {
if (new_val !== undefined) {
- // limit
- new_val = Math.max(Math.min(new_val, this.options.max), this.options.min);
+ // apply limit
+ if (this.options.max !== null) {
+ new_val = Math.min(new_val, this.options.max);
+ }
+ if (this.options.min !== null) {
+ new_val = Math.max(new_val, this.options.min);
+ }
// trigger on change event
if (this.options.onchange) {
@@ -64,7 +81,7 @@
}
// set values
- this.$slider.slider('value', new_val);
+ this.$slider && this.$slider.slider('value', new_val);
this.$text.val(new_val);
}
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd static/scripts/packed/mvc/tools/tools-form.js
--- a/static/scripts/packed/mvc/tools/tools-form.js
+++ b/static/scripts/packed/mvc/tools/tools-form.js
@@ -1,1 +1,1 @@
-define(["utils/utils","mvc/ui/ui-portlet","mvc/ui/ui-misc","mvc/citation/citation-model","mvc/citation/citation-view","mvc/tools","mvc/tools/tools-template","mvc/tools/tools-content","mvc/tools/tools-section","mvc/tools/tools-tree","mvc/tools/tools-jobs"],function(i,h,l,j,a,e,d,f,k,c,g){var b=Backbone.View.extend({container:"body",initialize:function(n){console.debug(n);var m=this;if(parent.Galaxy&&parent.Galaxy.modal){this.modal=parent.Galaxy.modal}else{this.modal=new l.Modal.View()}this.options=n;this.model=n.model;this.inputs=n.model.inputs;this.setElement("<div/>");$(this.container).append(this.$el);this.tree=new c(this);this.job_handler=new g(this);this.field_list={};this.input_list={};this.element_list={};this.content=new f({history_id:this.options.history_id,success:function(){m._buildForm()}})},message:function(m){$(this.container).empty();$(this.container).append(m)},reset:function(){for(var m in this.element_list){this.element_list[m].reset()}},refresh:function(){this.tree.refresh();for(var m in this.field_list){this.field_list[m].trigger("change")}console.debug("tools-form::refresh() - Recreated data structure. Refresh.")},_refreshForm:function(){var m=this;var n=this.tree.finalize({data:function(o){if(o.values.length>0&&o.values[0].src==="hda"){return m.content.get({id:o.values[0].id}).dataset_id}return null}});console.debug("tools-form::_refreshForm() - Refreshing inputs/states.");console.debug(n);i.request({type:"GET",url:galaxy_config.root+"tool_runner/index?tool_id="+this.options.id+"&form_refresh=True",data:n,success:function(o){console.debug("tools-form::_refreshForm() - Refreshed inputs/states.");console.debug(o)},error:function(o){console.debug("tools-form::_refreshForm() - Refresh request failed.");console.debug(o)}})},_buildForm:function(){var n=this;var p=new l.ButtonMenu({icon:"fa-gear",tooltip:"Click to see a list of available operations."});p.addMenu({icon:"fa-question-circle",title:"Question?",tooltip:"Ask a question about this tool (Biostar)",onclick:function(){window.open(n.options.biostar_url+"/p/new/post/")}});p.addMenu({icon:"fa-search",title:"Search",tooltip:"Search help for this tool (Biostar)",onclick:function(){window.open(n.options.biostar_url+"/t/"+n.options.id+"/")}});p.addMenu({icon:"fa-share",title:"Share",tooltip:"Share this tool",onclick:function(){prompt("Copy to clipboard: Ctrl+C, Enter",window.location.origin+galaxy_config.root+"root?tool_id="+n.options.id)}});if(Galaxy.currUser.get("is_admin")){p.addMenu({icon:"fa-download",title:"Download",tooltip:"Download this tool",onclick:function(){window.location.href=galaxy_config.root+"api/tools/"+n.options.id+"/download"}})}this.section=new k.View(n,{inputs:this.inputs,cls:"ui-table-plain"});this.$el.hide();$("#tool-form-classic").show();return;this.portlet=new h.View({icon:"fa-wrench",title:"<b>"+this.model.name+"</b> "+this.model.description,operations:{menu:p},buttons:{execute:new l.Button({icon:"fa-check",tooltip:"Execute the tool",title:"Execute",cls:"btn btn-primary",floating:"clear",onclick:function(){n.job_handler.submit()}})}});if(!this.options.biostar_url){button_question.$el.hide();button_search.$el.hide()}this.$el.append(this.portlet.$el);if(this.options.help!=""){this.$el.append(d.help(this.options.help))}if(this.options.citations){this.$el.append(d.citations());var m=new j.ToolCitationCollection();m.tool_id=this.options.id;var o=new a.CitationListView({collection:m});o.render();m.fetch()}this.portlet.append(this.section.$el);this.refresh()}});return{View:b}});
\ No newline at end of file
+define(["utils/utils","mvc/ui/ui-portlet","mvc/ui/ui-misc","mvc/citation/citation-model","mvc/citation/citation-view","mvc/tools","mvc/tools/tools-template","mvc/tools/tools-content","mvc/tools/tools-section","mvc/tools/tools-tree","mvc/tools/tools-jobs"],function(i,h,l,j,a,e,d,f,k,c,g){var b=Backbone.View.extend({container:"body",initialize:function(n){console.debug(n);var m=this;if(parent.Galaxy&&parent.Galaxy.modal){this.modal=parent.Galaxy.modal}else{this.modal=new l.Modal.View()}this.options=n;this.model=n.model;this.inputs=n.model.inputs;this.setElement("<div/>");$(this.container).append(this.$el);this.tree=new c(this);this.job_handler=new g(this);this.field_list={};this.input_list={};this.element_list={};this.content=new f({history_id:this.options.history_id,success:function(){m._buildForm()}})},message:function(m){$(this.container).empty();$(this.container).append(m)},reset:function(){for(var m in this.element_list){this.element_list[m].reset()}},refresh:function(){this.tree.refresh();for(var m in this.field_list){this.field_list[m].trigger("change")}console.debug("tools-form::refresh() - Recreated data structure. Refresh.")},_refreshForm:function(){var m=this;var n=this.tree.finalize({data:function(o){if(o.values.length>0&&o.values[0]&&o.values[0].src==="hda"){return m.content.get({id:o.values[0].id}).dataset_id}return null}});console.debug("tools-form::_refreshForm() - Refreshing inputs/states.");console.debug(n);i.request({type:"GET",url:galaxy_config.root+"tool_runner/index?tool_id="+this.options.id+"&form_refresh=True",data:n,success:function(o){console.debug("tools-form::_refreshForm() - Refreshed inputs/states.");console.debug(o)},error:function(o){console.debug("tools-form::_refreshForm() - Refresh request failed.");console.debug(o)}})},_buildForm:function(){var n=this;var p=new l.ButtonMenu({icon:"fa-gear",tooltip:"Click to see a list of available operations."});p.addMenu({icon:"fa-question-circle",title:"Question?",tooltip:"Ask a question about this tool (Biostar)",onclick:function(){window.open(n.options.biostar_url+"/p/new/post/")}});p.addMenu({icon:"fa-search",title:"Search",tooltip:"Search help for this tool (Biostar)",onclick:function(){window.open(n.options.biostar_url+"/t/"+n.options.id+"/")}});p.addMenu({icon:"fa-share",title:"Share",tooltip:"Share this tool",onclick:function(){prompt("Copy to clipboard: Ctrl+C, Enter",window.location.origin+galaxy_config.root+"root?tool_id="+n.options.id)}});if(Galaxy.currUser.get("is_admin")){p.addMenu({icon:"fa-download",title:"Download",tooltip:"Download this tool",onclick:function(){window.location.href=galaxy_config.root+"api/tools/"+n.options.id+"/download"}})}this.section=new k.View(n,{inputs:this.inputs,cls:"ui-table-plain"});if(this.incompatible){this.$el.hide();$("#tool-form-classic").show();return}this.portlet=new h.View({icon:"fa-wrench",title:"<b>"+this.model.name+"</b> "+this.model.description,operations:{menu:p},buttons:{execute:new l.Button({icon:"fa-check",tooltip:"Execute the tool",title:"Execute",cls:"btn btn-primary",floating:"clear",onclick:function(){n.job_handler.submit()}})}});if(!this.options.biostar_url){button_question.$el.hide();button_search.$el.hide()}this.$el.append(this.portlet.$el);if(this.options.help!=""){this.$el.append(d.help(this.options.help))}if(this.options.citations){this.$el.append(d.citations());var m=new j.ToolCitationCollection();m.tool_id=this.options.id;var o=new a.CitationListView({collection:m});o.render();m.fetch()}this.portlet.append(this.section.$el);this.refresh()}});return{View:b}});
\ No newline at end of file
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd static/scripts/packed/mvc/tools/tools-jobs.js
--- a/static/scripts/packed/mvc/tools/tools-jobs.js
+++ b/static/scripts/packed/mvc/tools/tools-jobs.js
@@ -1,1 +1,1 @@
-define(["utils/utils","mvc/tools/tools-template"],function(b,a){return Backbone.Model.extend({initialize:function(d,c){this.app=d;this.options=b.merge(c,this.optionsDefault)},submit:function(){var c=this;var d={tool_id:this.app.options.id,inputs:this.app.tree.finalize()};this.app.reset();if(!this._validation(d)){console.debug("tools-jobs::submit - Submission canceled. Validation failed.");return}console.debug(d);this.app.modal.show({title:"Please wait...",body:"progress",buttons:{Close:function(){c.app.modal.hide()}}});b.request({type:"POST",url:galaxy_config.root+"api/tools",data:d,success:function(e){c.app.modal.hide();c.app.message(a.success(e));c._refreshHdas()},error:function(e,g){c.app.modal.hide();if(e&&e.message&&e.message.data){var h=c.app.tree.matchResponse(e.message.data);for(var f in h){c._foundError(f,h[f])}}else{console.debug(e)}}})},_foundError:function(c,d){var e=this.app.element_list[c];e.error(d||"Please verify this parameter.");$(this.app.container).animate({scrollTop:e.$el.offset().top-20},500)},_validation:function(g){var c=g.inputs;var k=-1;for(var i in c){var e=c[i];var j=this.app.tree.match(i);var d=this.app.field_list[j];var h=this.app.input_list[j];if(h&&d&&d.validate&&!d.validate()){this._foundError(j);return false}if(e.batch){var f=e.values.length;if(k===-1){k=f}else{if(k!==f){this._foundError(j,"Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>"+f+"</b> selection(s) while a previous field contains <b>"+k+"</b>.");return false}}}}return true},_refreshHdas:function(){if(parent.Galaxy&&parent.Galaxy.currHistoryPanel){parent.Galaxy.currHistoryPanel.refreshContents()}}})});
\ No newline at end of file
+define(["utils/utils","mvc/tools/tools-template"],function(b,a){return Backbone.Model.extend({initialize:function(d,c){this.app=d;this.options=b.merge(c,this.optionsDefault)},submit:function(){var c=this;var d={tool_id:this.app.options.id,inputs:this.app.tree.finalize()};this.app.reset();if(!this._validation(d)){console.debug("tools-jobs::submit - Submission canceled. Validation failed.");return}console.debug(d);this.app.modal.show({title:"Please wait...",body:"progress",closing_events:true,buttons:{Close:function(){c.app.modal.hide()}}});b.request({type:"POST",url:galaxy_config.root+"api/tools",data:d,success:function(e){c.app.modal.hide();c.app.message(a.success(e));c._refreshHdas()},error:function(e,g){c.app.modal.hide();if(e&&e.message&&e.message.data){var h=c.app.tree.matchResponse(e.message.data);for(var f in h){c._foundError(f,h[f])}}else{console.debug(e);c.app.modal.show({title:"Job submission failed",body:a.error(d),buttons:{Close:function(){c.app.modal.hide()}}})}}})},_foundError:function(c,d){var e=this.app.element_list[c];e.error(d||"Please verify this parameter.");$(this.app.container).animate({scrollTop:e.$el.offset().top-20},500)},_validation:function(g){var c=g.inputs;var k=-1;for(var i in c){var e=c[i];var j=this.app.tree.match(i);var d=this.app.field_list[j];var h=this.app.input_list[j];if(h&&d&&d.validate&&!d.validate()){this._foundError(j);return false}if(e.batch){var f=e.values.length;if(k===-1){k=f}else{if(k!==f){this._foundError(j,"Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>"+f+"</b> selection(s) while a previous field contains <b>"+k+"</b>.");return false}}}}return true},_refreshHdas:function(){if(parent.Galaxy&&parent.Galaxy.currHistoryPanel){parent.Galaxy.currHistoryPanel.refreshContents()}}})});
\ No newline at end of file
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd static/scripts/packed/mvc/tools/tools-section.js
--- a/static/scripts/packed/mvc/tools/tools-section.js
+++ b/static/scripts/packed/mvc/tools/tools-section.js
@@ -1,1 +1,1 @@
-define(["utils/utils","mvc/ui/ui-table","mvc/ui/ui-misc","mvc/tools/tools-repeat","mvc/tools/tools-select-content","mvc/tools/tools-input"],function(d,b,g,c,a,e){var f=Backbone.View.extend({initialize:function(i,h){this.app=i;this.inputs=h.inputs;h.cls_tr="section-row";this.table=new b.View(h);this.setElement(this.table.$el);this.render()},render:function(){this.table.delAll();for(var h in this.inputs){this._add(this.inputs[h])}},_add:function(j){var i=this;var h=jQuery.extend(true,{},j);h.id=d.uuid();this.app.input_list[h.id]=h;var k=h.type;switch(k){case"conditional":this._addConditional(h);break;case"repeat":this._addRepeat(h);break;default:this._addRow(k,h)}},_addConditional:function(h){h.label=h.test_param.label;h.value=h.test_param.value;var j=this._addRow("conditional",h);for(var l in h.cases){var k=h.id+"-section-"+l;var m=new f(this.app,{inputs:h.cases[l].inputs,cls:"ui-table-plain"});m.$el.addClass("ui-table-form-section");this.table.add(m.$el);this.table.append(k)}},_addRepeat:function(o){var r=this;var p=0;function m(i,t){var s=o.id+"-section-"+(p++);var u=null;if(t){u=function(){k.del(s);k.retitle(o.title);r.app.refresh()}}var v=new f(r.app,{inputs:i,cls:"ui-table-plain"});k.add({id:s,title:o.title,$el:v.$el,ondel:u});k.retitle(o.title)}var k=new c.View({title_new:o.title,max:o.max,onnew:function(){m(o.inputs,true);r.app.refresh()}});var h=o.min;var q=_.size(o.cache);for(var l=0;l<Math.max(q,h);l++){var n=null;if(l<q){n=o.cache[l]}else{n=o.inputs}m(n,l>=h)}var j=new e({label:o.title,help:o.help,field:k});j.$el.addClass("ui-table-form-section");this.table.add(j.$el);this.table.append(o.id)},_addRow:function(j,h){var l=h.id;var i=null;switch(j){case"text":i=this._fieldText(h);break;case"select":i=this._fieldSelect(h);break;case"data":i=this._fieldData(h);break;case"data_column":h.is_dynamic=false;i=this._fieldSelect(h);break;case"conditional":i=this._fieldConditional(h);break;case"hidden":i=this._fieldHidden(h);break;case"integer":i=this._fieldSlider(h);break;case"float":i=this._fieldSlider(h);break;case"boolean":i=this._fieldBoolean(h);break;case"genomebuild":i=this._fieldSelect(h);break;default:this.app.incompatible=true;if(h.options){i=this._fieldSelect(h)}else{i=this._fieldText(h)}console.debug("tools-form::_addRow() : Auto matched field type ("+j+").")}if(h.value!==undefined){i.value(h.value)}this.app.field_list[l]=i;var k=new e({label:h.label,optional:h.optional,help:h.help,field:i});this.app.element_list[l]=k;this.table.add(k.$el);this.table.append(l);return this.table.get(l)},_fieldConditional:function(h){var j=this;var k=[];for(var l in h.test_param.options){var m=h.test_param.options[l];k.push({label:m[0],value:m[1]})}return new g.Select.View({id:"field-"+h.id,data:k,onchange:function(u){for(var s in h.cases){var o=h.cases[s];var r=h.id+"-section-"+s;var n=j.table.get(r);var q=false;for(var p in o.inputs){var t=o.inputs[p].type;if(t&&t!=="hidden"){q=true;break}}if(o.value==u&&q){n.fadeIn("fast")}else{n.hide()}}}})},_fieldData:function(h){var i=this;var j=h.id;return new a.View(this.app,{id:"field-"+j,extensions:h.extensions,multiple:h.multiple,onchange:function(q){var o=q.values[0];var m=o.id;var p=o.src;var l=i.app.tree.references(j,"data_column");if(l.length<=0){console.debug("tool-form::field_data() - Data column parameters unavailable.");return}for(var n in l){var k=i.app.field_list[l[n]];k.wait&&k.wait()}i.app.content.getDetails({id:m,src:p,success:function(y){var B=null;if(y){console.debug("tool-form::field_data() - Selected content "+m+".");if(p=="hdca"&&y.elements&&y.elements.length>0){y=y.elements[0].object}B=y.metadata_column_types;if(!B){console.debug("tool-form::field_data() - FAILED: Could not find metadata for content "+m+".")}}else{console.debug("tool-form::field_data() - FAILED: Could not find content "+m+".")}for(var u in l){var w=i.app.input_list[l[u]];var x=i.app.field_list[l[u]];if(!w||!x){console.debug("tool-form::field_data() - FAILED: Column not found.")}var t=w.numerical;var s=[];for(var A in B){var z=B[A];var r=(parseInt(A)+1);var v="Text";if(z=="int"||z=="float"){v="Number"}if(z=="int"||z=="float"||!t){s.push({label:"Column: "+r+" ["+v+"]",value:r})}}if(x){x.update(s);if(!x.exists(x.value())){x.value(x.first())}x.show()}}}})}})},_fieldSelect:function(h){if(h.is_dynamic){this.app.incompatible=true}var j=[];for(var k in h.options){var l=h.options[k];j.push({label:l[0],value:l[1]})}var m=g.Select;switch(h.display){case"checkboxes":m=g.Checkbox;break;case"radio":m=g.Radio;break}return new m.View({id:"field-"+h.id,data:j,multiple:h.multiple})},_fieldText:function(h){return new g.Input({id:"field-"+h.id,area:h.area})},_fieldSlider:function(h){h.min=h.min||0;h.max=h.max||100000;var i=1;if(h.type=="float"){i=(h.max-h.min)/10000}return new g.Slider.View({id:"field-"+h.id,min:h.min,max:h.max,step:i})},_fieldHidden:function(h){return new g.Hidden({id:"field-"+h.id})},_fieldBoolean:function(h){return new g.RadioButton.View({id:"field-"+h.id,data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]})}});return{View:f}});
\ No newline at end of file
+define(["utils/utils","mvc/ui/ui-table","mvc/ui/ui-misc","mvc/tools/tools-repeat","mvc/tools/tools-select-content","mvc/tools/tools-input"],function(d,b,g,c,a,e){var f=Backbone.View.extend({initialize:function(i,h){this.app=i;this.inputs=h.inputs;h.cls_tr="section-row";this.table=new b.View(h);this.setElement(this.table.$el);this.render()},render:function(){this.table.delAll();for(var h in this.inputs){this._add(this.inputs[h])}},_add:function(j){var i=this;var h=jQuery.extend(true,{},j);h.id=d.uuid();this.app.input_list[h.id]=h;var k=h.type;switch(k){case"conditional":this._addConditional(h);break;case"repeat":this._addRepeat(h);break;default:this._addRow(k,h)}},_addConditional:function(h){h.label=h.test_param.label;h.value=h.test_param.value;var j=this._addRow("conditional",h);for(var l in h.cases){var k=h.id+"-section-"+l;var m=new f(this.app,{inputs:h.cases[l].inputs,cls:"ui-table-plain"});m.$el.addClass("ui-table-form-section");this.table.add(m.$el);this.table.append(k)}},_addRepeat:function(o){var r=this;var p=0;function m(i,t){var s=o.id+"-section-"+(p++);var u=null;if(t){u=function(){k.del(s);k.retitle(o.title);r.app.refresh()}}var v=new f(r.app,{inputs:i,cls:"ui-table-plain"});k.add({id:s,title:o.title,$el:v.$el,ondel:u});k.retitle(o.title)}var k=new c.View({title_new:o.title,max:o.max,onnew:function(){m(o.inputs,true);r.app.refresh()}});var h=o.min;var q=_.size(o.cache);for(var l=0;l<Math.max(q,h);l++){var n=null;if(l<q){n=o.cache[l]}else{n=o.inputs}m(n,l>=h)}var j=new e({label:o.title,help:o.help,field:k});j.$el.addClass("ui-table-form-section");this.table.add(j.$el);this.table.append(o.id)},_addRow:function(j,h){var l=h.id;var i=null;switch(j){case"text":i=this._fieldText(h);break;case"select":i=this._fieldSelect(h);break;case"data":i=this._fieldData(h);break;case"data_column":h.is_dynamic=false;i=this._fieldSelect(h);break;case"conditional":i=this._fieldConditional(h);break;case"hidden":i=this._fieldHidden(h);break;case"integer":i=this._fieldSlider(h);break;case"float":i=this._fieldSlider(h);break;case"boolean":i=this._fieldBoolean(h);break;case"genomebuild":i=this._fieldSelect(h);break;default:this.app.incompatible=true;if(h.options){i=this._fieldSelect(h)}else{i=this._fieldText(h)}console.debug("tools-form::_addRow() : Auto matched field type ("+j+").")}if(h.value!==undefined){i.value(h.value)}this.app.field_list[l]=i;var k=new e({label:h.label,optional:h.optional,help:h.help,field:i});this.app.element_list[l]=k;this.table.add(k.$el);this.table.append(l);return this.table.get(l)},_fieldConditional:function(h){var j=this;var k=[];for(var l in h.test_param.options){var m=h.test_param.options[l];k.push({label:m[0],value:m[1]})}return new g.Select.View({id:"field-"+h.id,data:k,onchange:function(u){for(var s in h.cases){var o=h.cases[s];var r=h.id+"-section-"+s;var n=j.table.get(r);var q=false;for(var p in o.inputs){var t=o.inputs[p].type;if(t&&t!=="hidden"){q=true;break}}if(o.value==u&&q){n.fadeIn("fast")}else{n.hide()}}}})},_fieldData:function(h){var i=this;var j=h.id;return new a.View(this.app,{id:"field-"+j,extensions:h.extensions,multiple:h.multiple,onchange:function(q){var o=q.values[0];var m=o.id;var p=o.src;var l=i.app.tree.references(j,"data_column");if(l.length<=0){console.debug("tool-form::field_data() - Data column parameters unavailable.");return}for(var n in l){var k=i.app.field_list[l[n]];k.wait&&k.wait()}i.app.content.getDetails({id:m,src:p,success:function(y){var B=null;if(y){console.debug("tool-form::field_data() - Selected content "+m+".");if(p=="hdca"&&y.elements&&y.elements.length>0){y=y.elements[0].object}B=y.metadata_column_types;if(!B){console.debug("tool-form::field_data() - FAILED: Could not find metadata for content "+m+".")}}else{console.debug("tool-form::field_data() - FAILED: Could not find content "+m+".")}for(var u in l){var w=i.app.input_list[l[u]];var x=i.app.field_list[l[u]];if(!w||!x){console.debug("tool-form::field_data() - FAILED: Column not found.")}var t=w.numerical;var s=[];for(var A in B){var z=B[A];var r=(parseInt(A)+1);var v="Text";if(z=="int"||z=="float"){v="Number"}if(z=="int"||z=="float"||!t){s.push({label:"Column: "+r+" ["+v+"]",value:r})}}if(x){x.update(s);if(!x.exists(x.value())){x.value(x.first())}x.show()}}}})}})},_fieldSelect:function(h){if(h.is_dynamic){this.app.incompatible=true}var j=[];for(var k in h.options){var l=h.options[k];j.push({label:l[0],value:l[1]})}var m=g.Select;switch(h.display){case"checkboxes":m=g.Checkbox;break;case"radio":m=g.Radio;break}return new m.View({id:"field-"+h.id,data:j,multiple:h.multiple})},_fieldText:function(h){return new g.Input({id:"field-"+h.id,area:h.area})},_fieldSlider:function(h){return new g.Slider.View({id:"field-"+h.id,precise:h.type=="float",min:h.min,max:h.max})},_fieldHidden:function(h){return new g.Hidden({id:"field-"+h.id})},_fieldBoolean:function(h){return new g.RadioButton.View({id:"field-"+h.id,data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]})}});return{View:f}});
\ No newline at end of file
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd static/scripts/packed/mvc/tools/tools-select-content.js
--- a/static/scripts/packed/mvc/tools/tools-select-content.js
+++ b/static/scripts/packed/mvc/tools/tools-select-content.js
@@ -1,1 +1,1 @@
-define(["utils/utils","mvc/ui/ui-misc","mvc/ui/ui-tabs","mvc/tools/tools-template"],function(c,e,b,a){var d=Backbone.View.extend({initialize:function(n,h){this.options=h;var g=this;this.setElement("<div/>");this.current="hda";this.button_new=new e.RadioButton.View({value:this.current,data:[{icon:"fa-file-o",label:"Select datasets",value:"hda"},{icon:"fa-files-o",label:"Select a collection",value:"hdca"}],onchange:function(i){g.current=i;g.refresh();g.trigger("change")}});var l=n.content.filterType({src:"hda",extensions:h.extensions});var k=[];for(var j in l){k.push({label:l[j].name,value:l[j].id})}this.select_datasets=new e.Select.View({multiple:true,data:k,value:k[0]&&k[0].value,onchange:function(){g.trigger("change")}});var m=n.content.filterType({src:"hdca",extensions:h.extensions});var f=[];for(var j in m){f.push({label:m[j].name,value:m[j].id})}this.select_collection=new e.Select.View({data:f,value:f[0]&&f[0].value,onchange:function(){g.trigger("change")}});this.$el.append(c.wrap(this.button_new.$el));this.$el.append(this.select_datasets.$el);this.$el.append(this.select_collection.$el);if(!this.options.multiple){this.$el.append(a.batchMode())}this.refresh();this.on("change",function(){if(h.onchange){h.onchange(g.value())}})},value:function(){var g=null;switch(this.current){case"hda":g=this.select_datasets;break;case"hdca":g=this.select_collection;break}var j=g.value();if(!(j instanceof Array)){j=[j]}var f={batch:!this.options.multiple,values:[]};for(var h in j){f.values.push({id:j[h],src:this.current})}return f},validate:function(){switch(this.current){case"hda":return this.select_datasets.validate();case"hdca":return this.select_collection.validate()}},refresh:function(){switch(this.current){case"hda":this.select_datasets.$el.fadeIn();this.select_collection.$el.hide();break;case"hdca":this.select_datasets.$el.hide();this.select_collection.$el.fadeIn();break}}});return{View:d}});
\ No newline at end of file
+define(["utils/utils","mvc/ui/ui-misc","mvc/ui/ui-tabs","mvc/tools/tools-template"],function(c,e,b,a){var d=Backbone.View.extend({initialize:function(n,h){this.options=h;var g=this;this.setElement("<div/>");this.current="hda";this.button_new=new e.RadioButton.View({value:this.current,data:[{icon:"fa-file-o",label:"Select datasets",value:"hda"},{icon:"fa-files-o",label:"Select a collection",value:"hdca"}],onchange:function(i){g.current=i;g.refresh();g.trigger("change")}});var l=n.content.filterType({src:"hda",extensions:h.extensions});var k=[];for(var j in l){k.push({label:l[j].hid+": "+l[j].name,value:l[j].id})}this.select_datasets=new e.Select.View({multiple:true,data:k,value:k[0]&&k[0].value,onchange:function(){g.trigger("change")}});var m=n.content.filterType({src:"hdca",extensions:h.extensions});var f=[];for(var j in m){f.push({label:m[j].hid+": "+m[j].name,value:m[j].id})}this.select_collection=new e.Select.View({data:f,value:f[0]&&f[0].value,onchange:function(){g.trigger("change")}});this.$el.append(c.wrap(this.button_new.$el));this.$el.append(this.select_datasets.$el);this.$el.append(this.select_collection.$el);if(!this.options.multiple){this.$el.append(a.batchMode())}this.refresh();this.on("change",function(){if(h.onchange){h.onchange(g.value())}})},value:function(k){if(k!==undefined){if(k.values.length>0&&k.values[0]&&k.values[0].src){var j=[];for(var g in k.values){j.push(k.values[g].id)}this.current=k.values[0].src;this.refresh();switch(this.current){case"hda":this.select_datasets.value(j);break;case"hdca":this.select_collection.value(j[0]);break}}}var h=this._select().value();if(!(h instanceof Array)){h=[h]}var f={batch:!this.options.multiple,values:[]};for(var g in h){f.values.push({id:h[g],src:this.current})}return f},validate:function(){return this._select().validate()},refresh:function(){switch(this.current){case"hda":this.select_datasets.$el.fadeIn();this.select_collection.$el.hide();break;case"hdca":this.select_datasets.$el.hide();this.select_collection.$el.fadeIn();break}},_select:function(){switch(this.current){case"hdca":return this.select_collection;default:return this.select_datasets}}});return{View:d}});
\ No newline at end of file
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd static/scripts/packed/mvc/tools/tools-template.js
--- a/static/scripts/packed/mvc/tools/tools-template.js
+++ b/static/scripts/packed/mvc/tools/tools-template.js
@@ -1,1 +1,1 @@
-define([],function(){return{help:function(a){return'<div class="toolHelp"><div class="toolHelpBody">'+a+"</div></div>"},citations:function(){return'<div id="citations"></div>'},success:function(c){if(!c.jobs||!c.jobs.length){console.debug("tools-template::success() - Failed jobs.");return}var a=c.jobs.length;var d="";if(a==1){d="1 job has"}else{d=a+" jobs have been"}var b='<div class="donemessagelarge"><p>'+d+" been successfully added to the queue - resulting in the following datasets:</p>";for(var e in c.outputs){b+='<p style="padding: 10px 20px;"><b>'+(parseInt(e)+1)+": "+c.outputs[e].name+"</b></p>"}b+="<p>You can check the status of queued jobs and view the resulting data by refreshing the History pane. When the job has been run the status will change from 'running' to 'finished' if completed successfully or 'error' if problems were encountered.</p></div>";return b},error:function(a){return'<div><p>Sorry, the server could not complete the request. Please contact the Galaxy Team if this error is persistent.</p><textarea class="ui-textarea" disabled style="color: black;" rows="6">'+JSON.stringify(a,undefined,4)+"</textarea></div>"},batchMode:function(){return'<div class="ui-table-form-info"><i class="fa fa-sitemap" style="font-size: 1.2em; padding: 2px 5px;"/>This is a batch mode input field. A separate job will be triggered for each dataset.';"</div>"}}});
\ No newline at end of file
+define([],function(){return{help:function(a){return'<div class="toolHelp"><div class="toolHelpBody">'+a+"</div></div>"},citations:function(){return'<div id="citations"></div>'},success:function(c){if(!c.jobs||!c.jobs.length){console.debug("tools-template::success() - Failed jobs.");return}var a=c.jobs.length;var d="";if(a==1){d="1 job has"}else{d=a+" jobs have been"}var b='<div class="donemessagelarge"><p>'+d+" been successfully added to the queue - resulting in the following datasets:</p>";for(var e in c.outputs){b+='<p style="padding: 10px 20px;"><b>'+(parseInt(e)+1)+": "+c.outputs[e].name+"</b></p>"}b+="<p>You can check the status of queued jobs and view the resulting data by refreshing the History pane. When the job has been run the status will change from 'running' to 'finished' if completed successfully or 'error' if problems were encountered.</p></div>";return b},error:function(a){return'<div><p>The server could not complete the request. Please contact the Galaxy Team if this error persists.</p><textarea class="ui-textarea" disabled style="color: black;" rows="6">'+JSON.stringify(a,undefined,4)+"</textarea></div>"},batchMode:function(){return'<div class="ui-table-form-info"><i class="fa fa-sitemap" style="font-size: 1.2em; padding: 2px 5px;"/>This is a batch mode input field. A separate job will be triggered for each dataset.';"</div>"}}});
\ No newline at end of file
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd static/scripts/packed/mvc/ui/ui-slider.js
--- a/static/scripts/packed/mvc/ui/ui-slider.js
+++ b/static/scripts/packed/mvc/ui/ui-slider.js
@@ -1,1 +1,1 @@
-define(["utils/utils"],function(a){var b=Backbone.View.extend({optionsDefault:{value:"",min:1,max:100,step:1},initialize:function(d){var c=this;this.options=a.merge(d,this.optionsDefault);this.setElement(this._template(this.options));this.$slider=this.$el.find("#slider");this.$text=this.$el.find("#text");this.$slider.slider(this.options);this.$text.on("change",function(){c.value($(this).val())});this.$text.on("keydown",function(f){var e=f.which;console.log(e);if(!(e==8||e==9||e==13||e==37||e==39||e==189||(e>=48&&e<=57)||(c.options.step!=1&&$(this).val().indexOf(".")==-1)&&e==190)){f.preventDefault()}});this.$slider.on("slide",function(e,f){c.value(f.value)})},value:function(c){if(c!==undefined){c=Math.max(Math.min(c,this.options.max),this.options.min);if(this.options.onchange){this.options.onchange(c)}this.$slider.slider("value",c);this.$text.val(c)}return this.$text.val()},_template:function(c){return'<div id="'+c.id+'" class="ui-form-slider"><input id="text" type="text" class="ui-form-slider-text"/><div id="slider" class="ui-form-slider-element"/></div>'}});return{View:b}});
\ No newline at end of file
+define(["utils/utils"],function(a){var b=Backbone.View.extend({optionsDefault:{value:"",min:null,max:null,step:null,precise:false,split:10000},initialize:function(d){var c=this;this.options=a.merge(d,this.optionsDefault);this.setElement(this._template(this.options));this.useslider=this.options.max!==null&&this.options.min!==null&&this.options.max>this.options.min;if(this.options.step===null){this.options.step=1;if(this.options.precise&&this.useslider){this.options.step=(this.options.max-this.options.min)/this.options.split}}if(this.useslider){this.$slider=this.$el.find("#slider");this.$slider.slider(this.options);this.$slider.on("slide",function(e,f){c.value(f.value)})}else{this.$el.find(".ui-form-slider-text").css("width","100%")}this.$text=this.$el.find("#text");this.$text.on("change",function(){c.value($(this).val())});this.$text.on("keydown",function(f){var e=f.which;if(!(e==8||e==9||e==13||e==37||e==39||e==189||(e>=48&&e<=57)||(c.options.precise&&$(this).val().indexOf(".")==-1)&&e==190)){f.preventDefault()}})},value:function(c){if(c!==undefined){if(this.options.max!==null){c=Math.min(c,this.options.max)}if(this.options.min!==null){c=Math.max(c,this.options.min)}if(this.options.onchange){this.options.onchange(c)}this.$slider&&this.$slider.slider("value",c);this.$text.val(c)}return this.$text.val()},_template:function(c){return'<div id="'+c.id+'" class="ui-form-slider"><input id="text" type="text" class="ui-form-slider-text"/><div id="slider" class="ui-form-slider-element"/></div>'}});return{View:b}});
\ No newline at end of file
diff -r a83921df0dc2047c6874c6daeddbe3047c86cd4e -r 78124761ad558e4670c338fdbdbf371acb6b91bd templates/webapps/galaxy/tool_form_refresh.mako
--- a/templates/webapps/galaxy/tool_form_refresh.mako
+++ b/templates/webapps/galaxy/tool_form_refresh.mako
@@ -4,30 +4,55 @@
tool_model = tool.to_dict(trans)
tool_model['inputs'] = {}
- ## sanitize
+ ## convert value to jsonifiable value
+ def convert(v):
+ # check if value is numeric
+ isnumber = False
+ try:
+ float(v)
+ isnumber = True
+ except Exception:
+ pass
+
+ ## fix hda parsing
+ if isinstance(v, HistoryDatasetAssociation):
+ return {
+ 'id' : trans.security.encode_id(v.id),
+ 'src' : 'hda'
+ }
+ elif isinstance(v, basestring) or isnumber:
+ return v
+ else:
+ return None
+
+ ## ensures that input dictionary is jsonifiable
from collections import Iterable
from galaxy.model import HistoryDatasetAssociation
def sanitize(dict):
- # quotations for Infinity so its jsonifiable
+ ## quotations for Infinity so its jsonifiable
for name in dict:
if dict[name] == Infinity:
dict[name] = 'Infinity'
- value = dict['value'] if 'value' in dict else None;
- # check if value is numeric
- def isnumber(s):
- try:
- float(s)
- return True
- except Exception:
- return False
- # fix hda parsing
- if isinstance(value, HistoryDatasetAssociation):
+
+ ## get current value
+ value = dict['value'] if 'value' in dict else None
+
+ ## identify lists
+ if dict['type'] == 'data':
+ if isinstance(value, list):
+ value = [ convert(v) for v in value ]
+ else:
+ value = [ convert(value) ]
value = {
- 'id' : trans.security.encode_id(value.dataset_id),
- 'src' : 'hda'
+ 'batch' : dict['multiple'],
+ 'values' : value
}
- elif value and not (isinstance(value, basestring) or isnumber(value) or isinstance(value, Iterable)):
- value = 'Unsupported'
+ elif isinstance(value, list):
+ value = [ convert(v) for v in value ]
+ else:
+ value = convert(value)
+
+ ## update and return
dict['value'] = value
return dict
@@ -52,7 +77,7 @@
elif input.type == "conditional":
try:
test_param = group_inputs[input_index]['test_param']
- test_param['value'] = tool_state[test_param['name']]
+ test_param['value'] = group_state[test_param['name']]
except Exception:
pass
i = group_state['__current_case__']
@@ -65,7 +90,7 @@
except Exception:
pass
- ## update input value from tool state and sanitize dictionary
+ ## update input value from tool state
try:
group_inputs[input_index]['value'] = tool_state[group_inputs[input_index]['name']]
group_inputs[input_index] = sanitize(group_inputs[input_index])
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
15 Oct '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a83921df0dc2/
Changeset: a83921df0dc2
User: carlfeberhard
Date: 2014-10-15 18:12:14+00:00
Summary: Fix to 8f8341a
Affected #: 3 files
diff -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 -r a83921df0dc2047c6874c6daeddbe3047c86cd4e client/galaxy/scripts/mvc/history/history-structure-view.js
--- a/client/galaxy/scripts/mvc/history/history-structure-view.js
+++ b/client/galaxy/scripts/mvc/history/history-structure-view.js
@@ -48,7 +48,7 @@
});
// set the collection (HistoryContents) for the job to that json (setting historyId for proper ajax urls)
job.outputCollection.reset( outputModels );
- job.outputCollection.historyId = structure.model.id;
+ job.outputCollection.historyId = view.model.id;
//this.debug( job.outputCollection );
// create the bbone view for the job (to be positioned later accrd. to the layout) and cache
diff -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 -r a83921df0dc2047c6874c6daeddbe3047c86cd4e static/scripts/mvc/history/history-structure-view.js
--- a/static/scripts/mvc/history/history-structure-view.js
+++ b/static/scripts/mvc/history/history-structure-view.js
@@ -48,7 +48,7 @@
});
// set the collection (HistoryContents) for the job to that json (setting historyId for proper ajax urls)
job.outputCollection.reset( outputModels );
- job.outputCollection.historyId = structure.model.id;
+ job.outputCollection.historyId = view.model.id;
//this.debug( job.outputCollection );
// create the bbone view for the job (to be positioned later accrd. to the layout) and cache
diff -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 -r a83921df0dc2047c6874c6daeddbe3047c86cd4e static/scripts/packed/mvc/history/history-structure-view.js
--- a/static/scripts/packed/mvc/history/history-structure-view.js
+++ b/static/scripts/packed/mvc/history/history-structure-view.js
@@ -1,1 +1,1 @@
-define(["mvc/job/job-model","mvc/job/job-li","mvc/history/history-content-model","mvc/history/job-dag","mvc/base-mvc","utils/localization","libs/d3"],function(e,f,i,c,h,a){var b=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure-component",initialize:function(j){this.log(this+"(HistoryStructureComponent).initialize:",j);this.component=j.component;this._jobLiMap={};this._createJobModels();this.layout=this._createLayout(j.layoutOptions)},_createJobModels:function(){var j=this;j.component.eachVertex(function(o){var n=o.data.job,m=new e.Job(n);var l=_.map(m.get("outputs"),function(p){var r=p.src==="hda"?"dataset":"dataset_collection",q=i.typeIdStr(r,p.id);return j.model.contents.get(q)});m.outputCollection.reset(l);m.outputCollection.historyId=structure.model.id;var k=new f.JobListItemView({model:m,expanded:true});k.$el.appendTo(j.$el);j._jobLiMap[m.id]=k});return j.jobs},layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobHeight:308,jobWidthSpacing:320,linkAdjX:4,linkAdjY:0},_createLayout:function(l){l=_.defaults(_.clone(l||{}),this.layoutDefaults);var j=this,k=_.values(j.component.vertices),m=_.extend(l,{nodeMap:{},links:[],el:{width:0,height:0},svg:{width:0,height:0,top:0,left:0}});k.forEach(function(n,o){var p={name:n.name,x:0,y:0};m.nodeMap[n.name]=p});j.component.edges(function(o){var n={source:o.source,target:o.target};m.links.push(n)});return m},_updateLayout:function(){var k=this,l=k.layout;l.svg.height=l.paddingTop+(l.linkSpacing*_.size(l.nodeMap));l.el.height=l.svg.height+l.jobHeight;var j=l.paddingLeft,m=l.svg.height;_.each(l.nodeMap,function(o,n){o.x=j;o.y=m;j+=l.jobWidthSpacing});l.el.width=l.svg.width=Math.max(l.el.width,j);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY});return this.layout},render:function(k){this.log(this+".renderComponent:",k);var j=this;j.component.eachVertex(function(m){var l=j._jobLiMap[m.name];if(!l.$el.is(":visible")){l.render(0)}});j._updateLayout();j.$el.width(j.layout.el.width).height(j.layout.el.height);this.renderSVG();j.component.eachVertex(function(n){var m=j._jobLiMap[n.name],l=j.layout.nodeMap[m.model.id];m.$el.css({top:l.y,left:l.x})});return this},renderSVG:function(){var j=this,o=j.layout;var k=d3.select(this.el).select("svg");if(k.empty()){k=d3.select(this.el).append("svg")}k.attr("width",o.svg.width).attr("height",o.svg.height);function m(p){d3.select(this).classed("highlighted",true);j._jobLiMap[p.source].$el.addClass("highlighted");j._jobLiMap[p.target].$el.addClass("highlighted")}function l(p){d3.select(this).classed("highlighted",false);j._jobLiMap[p.source].$el.removeClass("highlighted");j._jobLiMap[p.target].$el.removeClass("highlighted")}var n=k.selectAll(".connection").data(o.links);n.enter().append("path").attr("class","connection").attr("id",function(p){return p.source+"-"+p.target}).on("mouseover",m).on("mouseout",l);n.transition().attr("d",function(p){return j._connectionPath(p)});return k.node()},_connectionPath:function(k){var l=0,j=((k.x2-k.x1)/this.layout.svg.width)*this.layout.svg.height;return["M",k.x1,",",k.y1," ","C",k.x1+l,",",k.y1-j," ",k.x2-l,",",k.y2-j," ",k.x2,",",k.y2].join("")},events:{"mouseover .job.list-item":function(j){this.highlightConnected(j.currentTarget,true)},"mouseout .job.list-item":function(j){this.highlightConnected(j.currentTarget,false)}},highlightConnected:function(p,k){k=k!==undefined?k:true;var j=this,l=j.component,n=k?jQuery.prototype.addClass:jQuery.prototype.removeClass,m=k?"connection highlighted":"connection";var o=n.call($(p),"highlighted"),q=o.attr("id").replace("job-","");l.edges({target:q}).forEach(function(r){n.call(j.$("#job-"+r.source),"highlighted");j.$("#"+r.source+"-"+q).attr("class",m)});l.vertices[q].eachEdge(function(r){n.call(j.$("#job-"+r.target),"highlighted");j.$("#"+q+"-"+r.target).attr("class",m)})},toString:function(){return"HistoryStructureComponent("+this.model.id+")"}});var g=b.extend({logger:console,className:b.prototype.className+" vertical",layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobWidth:308,jobHeight:308,initialSpacing:64,jobSpacing:16,linkAdjX:0,linkAdjY:4},_updateLayout:function(){var k=this,l=k.layout;l.svg.width=l.paddingLeft+(l.linkSpacing*_.size(l.nodeMap));l.el.width=l.svg.width+l.jobWidth;l.el.height=0;var j=l.svg.width,m=l.paddingTop;_.each(l.nodeMap,function(p,o){p.x=j;p.y=m;var n=k._jobLiMap[o];if(n.$el.is(":visible")){m+=n.$el.height()+l.jobSpacing}else{m+=l.initialSpacing+l.jobSpacing}});l.el.height=l.svg.height=Math.max(l.el.height,m);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY;k.debug("link:",n.x1,n.y1,n.x2,n.y2,n)});k.debug("el:",l.el);k.debug("svg:",l.svg);return l},_connectionPath:function(l){var k=0,j=((l.y2-l.y1)/this.layout.svg.height)*this.layout.svg.width;return["M",l.x1,",",l.y1," ","C",l.x1-j,",",l.y1+k," ",l.x2-j,",",l.y2-k," ",l.x2,",",l.y2].join("")},toString:function(){return"VerticalHistoryStructureComponent("+this.model.id+")"}});var d=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure",initialize:function(j){this.log(this+"(HistoryStructureView).initialize:",j,this.model);this.jobs=j.jobs;this._createDAG()},_createDAG:function(){this.dag=new c({historyContents:this.model.contents.toJSON(),jobs:this.jobs,excludeSetMetadata:true,excludeErroredJobs:true});this.log(this+".dag:",this.dag);this._createComponents()},_createComponents:function(){this.log(this+"._createComponents");var j=this;j.componentViews=j.dag.weakComponentGraphArray().map(function(k){return j._createComponent(k)})},_createComponent:function(j){this.log(this+"._createComponent:",j);return new b({model:this.model,component:j})},render:function(k){this.log(this+".render:",k);var j=this;j.$el.html(['<div class="controls"></div>','<div class="components"></div>'].join(""));j.componentViews.forEach(function(l){l.render().$el.appendTo(j.$components())});return j},$components:function(){return this.$(".components")},toString:function(){return"HistoryStructureView()"}});return d});
\ No newline at end of file
+define(["mvc/job/job-model","mvc/job/job-li","mvc/history/history-content-model","mvc/history/job-dag","mvc/base-mvc","utils/localization","libs/d3"],function(e,f,i,c,h,a){var b=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure-component",initialize:function(j){this.log(this+"(HistoryStructureComponent).initialize:",j);this.component=j.component;this._jobLiMap={};this._createJobModels();this.layout=this._createLayout(j.layoutOptions)},_createJobModels:function(){var j=this;j.component.eachVertex(function(o){var n=o.data.job,m=new e.Job(n);var l=_.map(m.get("outputs"),function(p){var r=p.src==="hda"?"dataset":"dataset_collection",q=i.typeIdStr(r,p.id);return j.model.contents.get(q)});m.outputCollection.reset(l);m.outputCollection.historyId=j.model.id;var k=new f.JobListItemView({model:m,expanded:true});k.$el.appendTo(j.$el);j._jobLiMap[m.id]=k});return j.jobs},layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobHeight:308,jobWidthSpacing:320,linkAdjX:4,linkAdjY:0},_createLayout:function(l){l=_.defaults(_.clone(l||{}),this.layoutDefaults);var j=this,k=_.values(j.component.vertices),m=_.extend(l,{nodeMap:{},links:[],el:{width:0,height:0},svg:{width:0,height:0,top:0,left:0}});k.forEach(function(n,o){var p={name:n.name,x:0,y:0};m.nodeMap[n.name]=p});j.component.edges(function(o){var n={source:o.source,target:o.target};m.links.push(n)});return m},_updateLayout:function(){var k=this,l=k.layout;l.svg.height=l.paddingTop+(l.linkSpacing*_.size(l.nodeMap));l.el.height=l.svg.height+l.jobHeight;var j=l.paddingLeft,m=l.svg.height;_.each(l.nodeMap,function(o,n){o.x=j;o.y=m;j+=l.jobWidthSpacing});l.el.width=l.svg.width=Math.max(l.el.width,j);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY});return this.layout},render:function(k){this.log(this+".renderComponent:",k);var j=this;j.component.eachVertex(function(m){var l=j._jobLiMap[m.name];if(!l.$el.is(":visible")){l.render(0)}});j._updateLayout();j.$el.width(j.layout.el.width).height(j.layout.el.height);this.renderSVG();j.component.eachVertex(function(n){var m=j._jobLiMap[n.name],l=j.layout.nodeMap[m.model.id];m.$el.css({top:l.y,left:l.x})});return this},renderSVG:function(){var j=this,o=j.layout;var k=d3.select(this.el).select("svg");if(k.empty()){k=d3.select(this.el).append("svg")}k.attr("width",o.svg.width).attr("height",o.svg.height);function m(p){d3.select(this).classed("highlighted",true);j._jobLiMap[p.source].$el.addClass("highlighted");j._jobLiMap[p.target].$el.addClass("highlighted")}function l(p){d3.select(this).classed("highlighted",false);j._jobLiMap[p.source].$el.removeClass("highlighted");j._jobLiMap[p.target].$el.removeClass("highlighted")}var n=k.selectAll(".connection").data(o.links);n.enter().append("path").attr("class","connection").attr("id",function(p){return p.source+"-"+p.target}).on("mouseover",m).on("mouseout",l);n.transition().attr("d",function(p){return j._connectionPath(p)});return k.node()},_connectionPath:function(k){var l=0,j=((k.x2-k.x1)/this.layout.svg.width)*this.layout.svg.height;return["M",k.x1,",",k.y1," ","C",k.x1+l,",",k.y1-j," ",k.x2-l,",",k.y2-j," ",k.x2,",",k.y2].join("")},events:{"mouseover .job.list-item":function(j){this.highlightConnected(j.currentTarget,true)},"mouseout .job.list-item":function(j){this.highlightConnected(j.currentTarget,false)}},highlightConnected:function(p,k){k=k!==undefined?k:true;var j=this,l=j.component,n=k?jQuery.prototype.addClass:jQuery.prototype.removeClass,m=k?"connection highlighted":"connection";var o=n.call($(p),"highlighted"),q=o.attr("id").replace("job-","");l.edges({target:q}).forEach(function(r){n.call(j.$("#job-"+r.source),"highlighted");j.$("#"+r.source+"-"+q).attr("class",m)});l.vertices[q].eachEdge(function(r){n.call(j.$("#job-"+r.target),"highlighted");j.$("#"+q+"-"+r.target).attr("class",m)})},toString:function(){return"HistoryStructureComponent("+this.model.id+")"}});var g=b.extend({logger:console,className:b.prototype.className+" vertical",layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobWidth:308,jobHeight:308,initialSpacing:64,jobSpacing:16,linkAdjX:0,linkAdjY:4},_updateLayout:function(){var k=this,l=k.layout;l.svg.width=l.paddingLeft+(l.linkSpacing*_.size(l.nodeMap));l.el.width=l.svg.width+l.jobWidth;l.el.height=0;var j=l.svg.width,m=l.paddingTop;_.each(l.nodeMap,function(p,o){p.x=j;p.y=m;var n=k._jobLiMap[o];if(n.$el.is(":visible")){m+=n.$el.height()+l.jobSpacing}else{m+=l.initialSpacing+l.jobSpacing}});l.el.height=l.svg.height=Math.max(l.el.height,m);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY;k.debug("link:",n.x1,n.y1,n.x2,n.y2,n)});k.debug("el:",l.el);k.debug("svg:",l.svg);return l},_connectionPath:function(l){var k=0,j=((l.y2-l.y1)/this.layout.svg.height)*this.layout.svg.width;return["M",l.x1,",",l.y1," ","C",l.x1-j,",",l.y1+k," ",l.x2-j,",",l.y2-k," ",l.x2,",",l.y2].join("")},toString:function(){return"VerticalHistoryStructureComponent("+this.model.id+")"}});var d=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure",initialize:function(j){this.log(this+"(HistoryStructureView).initialize:",j,this.model);this.jobs=j.jobs;this._createDAG()},_createDAG:function(){this.dag=new c({historyContents:this.model.contents.toJSON(),jobs:this.jobs,excludeSetMetadata:true,excludeErroredJobs:true});this.log(this+".dag:",this.dag);this._createComponents()},_createComponents:function(){this.log(this+"._createComponents");var j=this;j.componentViews=j.dag.weakComponentGraphArray().map(function(k){return j._createComponent(k)})},_createComponent:function(j){this.log(this+"._createComponent:",j);return new b({model:this.model,component:j})},render:function(k){this.log(this+".render:",k);var j=this;j.$el.html(['<div class="controls"></div>','<div class="components"></div>'].join(""));j.componentViews.forEach(function(l){l.render().$el.appendTo(j.$components())});return j},$components:function(){return this.$(".components")},toString:function(){return"HistoryStructureView()"}});return d});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: History structure: documentation and clean up
by commits-noreply@bitbucket.org 15 Oct '14
by commits-noreply@bitbucket.org 15 Oct '14
15 Oct '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/8f8341ac47cd/
Changeset: 8f8341ac47cd
User: carlfeberhard
Date: 2014-10-15 18:05:18+00:00
Summary: History structure: documentation and clean up
Affected #: 13 files
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 client/galaxy/scripts/mvc/history/history-structure-view.js
--- a/client/galaxy/scripts/mvc/history/history-structure-view.js
+++ b/client/galaxy/scripts/mvc/history/history-structure-view.js
@@ -13,6 +13,9 @@
*/
// ============================================================================
+/**
+ *
+ */
var HistoryStructureComponent = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({
//logger : console,
@@ -151,7 +154,6 @@
//TODO:? liMap needed - can't we attach to vertex?
var li = view._jobLiMap[ v.name ],
position = view.layout.nodeMap[ li.model.id ];
- console.debug( li.$el.is( ':visible' ) );
//this.debug( position );
li.$el.css({ top: position.y, left: position.x });
});
@@ -249,6 +251,9 @@
// ============================================================================
+/**
+ *
+ */
var VerticalHistoryStructureComponent = HistoryStructureComponent.extend({
logger : console,
@@ -288,7 +293,6 @@
node.y = y;
var li = view._jobLiMap[ jobId ];
if( li.$el.is( ':visible' ) ){
- console.debug( 'li is visible, adj. by:', li.$el.height() );
y += li.$el.height() + layout.jobSpacing;
} else {
y += layout.initialSpacing + layout.jobSpacing;
@@ -331,6 +335,9 @@
// ============================================================================
+/**
+ *
+ */
var HistoryStructureView = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({
//logger : console,
@@ -351,7 +358,7 @@
excludeSetMetadata : true,
excludeErroredJobs : true
});
-window.dag = this.dag;
+//window.dag = this.dag;
this.log( this + '.dag:', this.dag );
this._createComponents();
@@ -360,7 +367,7 @@
_createComponents : function(){
this.log( this + '._createComponents' );
var structure = this;
-window.structure = structure;
+//window.structure = structure;
structure.componentViews = structure.dag.weakComponentGraphArray().map( function( componentGraph ){
return structure._createComponent( componentGraph );
@@ -402,8 +409,5 @@
// ============================================================================
- //return {
- // HistoryStructureView : HistoryStructureView
- //};
return HistoryStructureView;
});
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 client/galaxy/scripts/mvc/history/job-dag.js
--- a/client/galaxy/scripts/mvc/history/job-dag.js
+++ b/client/galaxy/scripts/mvc/history/job-dag.js
@@ -4,6 +4,10 @@
],function( GRAPH, addLogging ){
// ============================================================================
var _super = GRAPH.Graph;
+/** A Directed acyclic Graph built from a history's job data.
+ * Reads in job json, filters and process that json, and builds a graph
+ * using the connections between job inputs and outputs.
+ */
var JobDAG = function( options ){
var self = this;
//this.logger = console;
@@ -25,9 +29,12 @@
};
JobDAG.prototype = new GRAPH.Graph();
JobDAG.prototype.constructor = JobDAG;
+
+// add logging ability - turn off/on using the this.logger statement above
addLogging( JobDAG );
// ----------------------------------------------------------------------------
+/** process jobs, options, filters, and any history data, then create the graph */
JobDAG.prototype.init = function _init( options ){
options = options || {};
_super.prototype.init.call( this, options );
@@ -52,6 +59,7 @@
return self;
};
+/** add job filters based on options */
JobDAG.prototype._initFilters = function __initFilters(){
var self = this,
filters = [];
@@ -84,6 +92,7 @@
return filters;
};
+/** sort the jobs and cache any that pass all filters into _idMap by job.id */
JobDAG.prototype.preprocessJobs = function _preprocessJobs( jobs ){
this.info( 'processing jobs' );
@@ -98,6 +107,7 @@
return self;
};
+/** sort the jobs based on update time */
JobDAG.prototype.sort = function _sort( jobs ){
function cmpCreate( a, b ){
if( a.update_time > b.update_time ){ return 1; }
@@ -107,6 +117,7 @@
return jobs.sort( cmpCreate );
};
+/** proces input/output, filter based on job data returning data if passing, null if not */
JobDAG.prototype.preprocessJob = function _preprocessJob( job, index ){
//this.info( 'preprocessJob', job, index );
var self = this,
@@ -138,6 +149,9 @@
return jobData;
};
+/** make verbose input/output lists more concise, sanity checking along the way
+ * processFn is called on each input/output and passed the dataset obj (id,src) and the input/output name.
+ */
JobDAG.prototype.datasetMapToIdArray = function _datasetMapToIdArray( datasetMap, processFn ){
var self = this;
return _.map( datasetMap, function( dataset, nameInJob ){
@@ -152,6 +166,9 @@
});
};
+/** Walk all the jobs (vertices), attempting to find connections
+ * between datasets used as both inputs and outputs (edges)
+ */
JobDAG.prototype.createGraph = function _createGraph(){
var self = this;
self.debug( 'connections:' );
@@ -183,6 +200,7 @@
return self;
};
+/** Override to re-sort (ugh) jobs in each component by update time */
Graph.prototype.weakComponentGraphArray = function(){
return this.weakComponents().map( function( component ){
//TODO: this seems to belong above (in sort) - why isn't it preserved?
@@ -198,7 +216,4 @@
// ============================================================================
return JobDAG;
- //return {
- // jobDAG : jobDAG
- //};
});
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 client/galaxy/scripts/mvc/job/job-li.js
--- a/client/galaxy/scripts/mvc/job/job-li.js
+++ b/client/galaxy/scripts/mvc/job/job-li.js
@@ -6,7 +6,8 @@
], function( LIST_ITEM, DATASET_LIST, BASE_MVC, _l ){
//==============================================================================
var _super = LIST_ITEM.FoldoutListItemView;
-/** @class
+/** @class A job view used from within a larger list of jobs.
+ * Each job itself is a foldout panel of history contents displaying the outputs of this job.
*/
var JobListItemView = _super.extend(/** @lends JobListItemView.prototype */{
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 client/galaxy/scripts/mvc/job/job-model.js
--- a/client/galaxy/scripts/mvc/job/job-model.js
+++ b/client/galaxy/scripts/mvc/job/job-model.js
@@ -7,7 +7,7 @@
], function( HISTORY_CONTENTS, STATES, AJAX_QUEUE, BASE_MVC, _l ){
//==============================================================================
var searchableMixin = BASE_MVC.SearchableModelMixin;
-/** @class
+/** @class Represents a job running or ran on the server job handlers.
*/
var Job = Backbone.Model.extend( BASE_MVC.LoggableMixin ).extend(
BASE_MVC.mixin( searchableMixin, /** @lends Job.prototype */{
@@ -126,6 +126,7 @@
},
// ........................................................................ ajax
+ /** fetches all details for each job in the collection using a queue */
queueDetailFetching : function(){
var collection = this,
queue = new AJAX_QUEUE.AjaxQueue( this.map( function( job ){
@@ -179,7 +180,7 @@
//----------------------------------------------------------------------------- class vars
}, {
-
+ /** class level fn for fetching the job details for all jobs in a history */
fromHistory : function( historyId ){
console.debug( this );
var Collection = this,
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 client/galaxy/scripts/utils/graph.js
--- a/client/galaxy/scripts/utils/graph.js
+++ b/client/galaxy/scripts/utils/graph.js
@@ -2,10 +2,10 @@
],function(){
/* ============================================================================
TODO:
- edges can't retain data
============================================================================ */
//TODO: go ahead and move to underscore...
+/** call fn on each key/value in d */
function each( d, fn ){
for( var k in d ){
if( d.hasOwnProperty( k ) ){
@@ -14,6 +14,7 @@
}
}
+/** copy key/values from d2 to d overwriting if present */
function extend( d, d2 ){
for( var k in d2 ){
if( d2.hasOwnProperty( k ) ){
@@ -23,6 +24,7 @@
return d;
}
+/** deep equal of two dictionaries */
function matches( d, d2 ){
for( var k in d2 ){
if( d2.hasOwnProperty( k ) ){
@@ -34,6 +36,10 @@
return true;
}
+/** map key/values in obj
+ * if propsOrFn is an object, return only those k/v that match the object
+ * if propsOrFn is function, call the fn and returned the mapped values from it
+ */
function iterate( obj, propsOrFn ){
var fn = typeof propsOrFn === 'function'? propsOrFn : undefined,
props = typeof propsOrFn === 'object'? propsOrFn : undefined,
@@ -60,6 +66,8 @@
// ============================================================================
+/** A graph edge containing the name/id of both source and target and optional data
+ */
function Edge( source, target, data ){
var self = this;
self.source = source !== undefined? source : null;
@@ -70,10 +78,12 @@
//}
return self;
}
+/** String representation */
Edge.prototype.toString = function(){
return this.source + '->' + this.target;
};
+/** Return a plain object representing this edge */
Edge.prototype.toJSON = function(){
//TODO: this is safe in most browsers (fns will be stripped) - alter tests to incorporate this in order to pass data
//return this;
@@ -88,6 +98,9 @@
};
// ============================================================================
+/** A graph vertex with a (unique) name/id and optional data.
+ * A vertex contains a list of Edges (whose sources are this vertex) and maintains the degree.
+ */
function Vertex( name, data ){
var self = this;
self.name = name !== undefined? name : '(unnamed)';
@@ -96,16 +109,19 @@
self.degree = 0;
return self;
}
-window.Vertex = Vertex;
+
+/** String representation */
Vertex.prototype.toString = function(){
return 'Vertex(' + this.name + ')';
};
//TODO: better name w no collision for either this.eachEdge or this.edges
+/** Iterate over each edge from this vertex */
Vertex.prototype.eachEdge = function( propsOrFn ){
return iterate( this.edges, propsOrFn );
};
+/** Return a plain object representing this vertex */
Vertex.prototype.toJSON = function(){
//return this;
return {
@@ -116,6 +132,10 @@
// ============================================================================
+/** Base (abstract) class for Graph search algorithms.
+ * Pass in the graph to search
+ * and an optional dictionary containing the 3 vertex/edge processing fns listed below.
+ */
var GraphSearch = function( graph, processFns ){
var self = this;
self.graph = graph;
@@ -136,6 +156,9 @@
return self;
};
+/** Search interface where start is the vertex (or the name/id of the vertex) to begin the search at
+ * This public interface caches searches and returns the cached version if it's already been done.
+ */
GraphSearch.prototype.search = function _search( start ){
var self = this;
if( start in self._cache ){ return self._cache[ start ]; }
@@ -143,6 +166,22 @@
return ( self._cache[ start.name ] = self._search( start ) );
};
+/** Actual search (private) function (abstract here) */
+GraphSearch.prototype._search = function __search( start, search ){
+ search = search || {
+ discovered : {},
+ //parents : {},
+ edges : []
+ };
+ return search;
+};
+
+/** Searches graph from start and returns a search tree of the results */
+GraphSearch.prototype.searchTree = function _searchTree( start ){
+ return this._searchTree( this.search( start ) );
+};
+
+/** Helper fn that returns a graph (a search tree) based on the search object passed in (does not actually search) */
GraphSearch.prototype._searchTree = function __searchTree( search ){
var self = this;
return new Graph( true, {
@@ -153,11 +192,10 @@
});
};
-GraphSearch.prototype.searchTree = function _searchTree( start ){
- return this._searchTree( this.search( start ) );
-};
// ============================================================================
+/** Breadth first search algo.
+ */
var BreadthFirstSearch = function( graph, processFns ){
var self = this;
GraphSearch.call( this, graph, processFns );
@@ -166,6 +204,7 @@
BreadthFirstSearch.prototype = new GraphSearch();
BreadthFirstSearch.prototype.constructor = BreadthFirstSearch;
+/** (Private) implementation of BFS */
BreadthFirstSearch.prototype._search = function __search( start, search ){
search = search || {
discovered : {},
@@ -205,6 +244,8 @@
// ============================================================================
+/** Depth first search algorithm.
+ */
var DepthFirstSearch = function( graph, processFns ){
var self = this;
GraphSearch.call( this, graph, processFns );
@@ -213,6 +254,7 @@
DepthFirstSearch.prototype = new GraphSearch();
DepthFirstSearch.prototype.constructor = DepthFirstSearch;
+/** (Private) implementation of DFS */
DepthFirstSearch.prototype._search = function( start, search ){
//console.debug( 'depthFirstSearch:', start );
search = search || {
@@ -258,6 +300,8 @@
// ============================================================================
+/** A directed/non-directed graph object.
+ */
function Graph( directed, data, options ){
//TODO: move directed to options
this.directed = directed || false;
@@ -265,7 +309,7 @@
}
window.Graph = Graph;
-
+/** Set up options and instance variables */
Graph.prototype.init = function( options ){
options = options || {};
var self = this;
@@ -277,6 +321,7 @@
return self;
};
+/** Read data from the plain object data - both in d3 form (nodes and links) or vertices and edges */
Graph.prototype.read = function( data ){
if( !data ){ return this; }
var self = this;
@@ -286,6 +331,7 @@
};
//TODO: the next two could be combined
+/** Create the graph using a list of nodes and a list of edges (where source and target are indeces into nodes) */
Graph.prototype.readNodesAndLinks = function( data ){
if( !( data && data.hasOwnProperty( 'nodes' ) ) ){ return this; }
//console.debug( 'readNodesAndLinks:', data );
@@ -306,6 +352,7 @@
return self;
};
+/** Create the graph using a list of nodes and a list of edges (where source and target are names of nodes) */
Graph.prototype.readVerticesAndEdges = function( data ){
if( !( data && data.hasOwnProperty( 'vertices' ) ) ){ return this; }
//console.debug( 'readVerticesAndEdges:', data );
@@ -324,12 +371,16 @@
return self;
};
+/** Return the vertex with name, creating it if necessary */
Graph.prototype.createVertex = function( name, data ){
//console.debug( 'createVertex:', name, data );
if( this.vertices[ name ] ){ return this.vertices[ name ]; }
return ( this.vertices[ name ] = new Vertex( name, data ) );
};
+/** Create an edge in vertex named sourceName to targetName (optionally adding data to it)
+ * If directed is false, create a second edge from targetName to sourceName.
+ */
Graph.prototype.createEdge = function( sourceName, targetName, directed, data ){
//note: allows multiple 'equivalent' edges (to/from same source/target)
//console.debug( 'createEdge:', source, target, directed );
@@ -359,16 +410,19 @@
return edge;
};
+/** Walk over all the edges of the graph using the vertex.eachEdge iterator */
Graph.prototype.edges = function( propsOrFn ){
return Array.prototype.concat.apply( [], this.eachVertex( function( vertex ){
return vertex.eachEdge( propsOrFn );
}));
};
+/** Iterate over all the vertices in the graph */
Graph.prototype.eachVertex = function( propsOrFn ){
return iterate( this.vertices, propsOrFn );
};
+/** Return a list of the vertices adjacent to vertex */
Graph.prototype.adjacent = function( vertex ){
var self = this;
return iterate( vertex.edges, function( edge ){
@@ -376,6 +430,7 @@
});
};
+/** Call fn on each vertex adjacent to vertex */
Graph.prototype.eachAdjacent = function( vertex, fn ){
var self = this;
return iterate( vertex.edges, function( edge ){
@@ -384,6 +439,7 @@
});
};
+/** Print the graph to the console (debugging) */
Graph.prototype.print = function(){
var self = this;
console.log( 'Graph has ' + Object.keys( self.vertices ).length + ' vertices' );
@@ -396,6 +452,7 @@
return self;
};
+/** Return a DOT format string of this graph */
Graph.prototype.toDOT = function(){
var self = this,
strings = [];
@@ -407,6 +464,7 @@
return strings.join( '\n' );
};
+/** Return vertices and edges of this graph in d3 node/link format */
Graph.prototype.toNodesAndLinks = function(){
var self = this,
indeces = {};
@@ -424,6 +482,7 @@
};
};
+/** Return vertices and edges of this graph where edges use the name/id as source and target */
Graph.prototype.toVerticesAndEdges = function(){
var self = this;
return {
@@ -436,18 +495,22 @@
};
};
+/** Search this graph using BFS */
Graph.prototype.breadthFirstSearch = function( start, processFns ){
return new BreadthFirstSearch( this ).search( start );
};
+/** Return a searchtree of this graph using BFS */
Graph.prototype.breadthFirstSearchTree = function( start, processFns ){
return new BreadthFirstSearch( this ).searchTree( start );
};
+/** Search this graph using DFS */
Graph.prototype.depthFirstSearch = function( start, processFns ){
return new DepthFirstSearch( this ).search( start );
};
+/** Return a searchtree of this graph using DFS */
Graph.prototype.depthFirstSearchTree = function( start, processFns ){
return new DepthFirstSearch( this ).searchTree( start );
};
@@ -465,6 +528,7 @@
//Graph.prototype.isBipartite = function(){
//};
+/** Return an array of weakly connected (no edges between) sub-graphs in this graph */
Graph.prototype.weakComponents = function(){
//TODO: alternately, instead of returning graph-like objects:
// - could simply decorate the vertices (vertex.component = componentIndex), or clone the graph and do that
@@ -515,6 +579,7 @@
return components;
};
+/** Return a single graph containing the weakly connected components in this graph */
Graph.prototype.weakComponentGraph = function(){
//note: although this can often look like the original graph - edges can be lost
var components = this.weakComponents();
@@ -528,6 +593,7 @@
});
};
+/** Return an array of graphs of the weakly connected components in this graph */
Graph.prototype.weakComponentGraphArray = function(){
//note: although this can often look like the original graph - edges can be lost
return this.weakComponents().map( function( component ){
@@ -537,10 +603,8 @@
// ============================================================================
-
-
-
-// ============================================================================
+/** Create a random graph with numVerts vertices and numEdges edges (for testing)
+ */
function randGraph( directed, numVerts, numEdges ){
//console.debug( 'randGraph', directed, numVerts, numEdges );
var data = { nodes : [], links : [] };
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 static/scripts/mvc/history/history-structure-view.js
--- a/static/scripts/mvc/history/history-structure-view.js
+++ b/static/scripts/mvc/history/history-structure-view.js
@@ -13,6 +13,9 @@
*/
// ============================================================================
+/**
+ *
+ */
var HistoryStructureComponent = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({
//logger : console,
@@ -151,7 +154,6 @@
//TODO:? liMap needed - can't we attach to vertex?
var li = view._jobLiMap[ v.name ],
position = view.layout.nodeMap[ li.model.id ];
- console.debug( li.$el.is( ':visible' ) );
//this.debug( position );
li.$el.css({ top: position.y, left: position.x });
});
@@ -249,6 +251,9 @@
// ============================================================================
+/**
+ *
+ */
var VerticalHistoryStructureComponent = HistoryStructureComponent.extend({
logger : console,
@@ -288,7 +293,6 @@
node.y = y;
var li = view._jobLiMap[ jobId ];
if( li.$el.is( ':visible' ) ){
- console.debug( 'li is visible, adj. by:', li.$el.height() );
y += li.$el.height() + layout.jobSpacing;
} else {
y += layout.initialSpacing + layout.jobSpacing;
@@ -331,6 +335,9 @@
// ============================================================================
+/**
+ *
+ */
var HistoryStructureView = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({
//logger : console,
@@ -351,7 +358,7 @@
excludeSetMetadata : true,
excludeErroredJobs : true
});
-window.dag = this.dag;
+//window.dag = this.dag;
this.log( this + '.dag:', this.dag );
this._createComponents();
@@ -360,7 +367,7 @@
_createComponents : function(){
this.log( this + '._createComponents' );
var structure = this;
-window.structure = structure;
+//window.structure = structure;
structure.componentViews = structure.dag.weakComponentGraphArray().map( function( componentGraph ){
return structure._createComponent( componentGraph );
@@ -402,8 +409,5 @@
// ============================================================================
- //return {
- // HistoryStructureView : HistoryStructureView
- //};
return HistoryStructureView;
});
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 static/scripts/mvc/history/job-dag.js
--- a/static/scripts/mvc/history/job-dag.js
+++ b/static/scripts/mvc/history/job-dag.js
@@ -4,6 +4,10 @@
],function( GRAPH, addLogging ){
// ============================================================================
var _super = GRAPH.Graph;
+/** A Directed acyclic Graph built from a history's job data.
+ * Reads in job json, filters and process that json, and builds a graph
+ * using the connections between job inputs and outputs.
+ */
var JobDAG = function( options ){
var self = this;
//this.logger = console;
@@ -25,9 +29,12 @@
};
JobDAG.prototype = new GRAPH.Graph();
JobDAG.prototype.constructor = JobDAG;
+
+// add logging ability - turn off/on using the this.logger statement above
addLogging( JobDAG );
// ----------------------------------------------------------------------------
+/** process jobs, options, filters, and any history data, then create the graph */
JobDAG.prototype.init = function _init( options ){
options = options || {};
_super.prototype.init.call( this, options );
@@ -52,6 +59,7 @@
return self;
};
+/** add job filters based on options */
JobDAG.prototype._initFilters = function __initFilters(){
var self = this,
filters = [];
@@ -84,6 +92,7 @@
return filters;
};
+/** sort the jobs and cache any that pass all filters into _idMap by job.id */
JobDAG.prototype.preprocessJobs = function _preprocessJobs( jobs ){
this.info( 'processing jobs' );
@@ -98,6 +107,7 @@
return self;
};
+/** sort the jobs based on update time */
JobDAG.prototype.sort = function _sort( jobs ){
function cmpCreate( a, b ){
if( a.update_time > b.update_time ){ return 1; }
@@ -107,6 +117,7 @@
return jobs.sort( cmpCreate );
};
+/** proces input/output, filter based on job data returning data if passing, null if not */
JobDAG.prototype.preprocessJob = function _preprocessJob( job, index ){
//this.info( 'preprocessJob', job, index );
var self = this,
@@ -138,6 +149,9 @@
return jobData;
};
+/** make verbose input/output lists more concise, sanity checking along the way
+ * processFn is called on each input/output and passed the dataset obj (id,src) and the input/output name.
+ */
JobDAG.prototype.datasetMapToIdArray = function _datasetMapToIdArray( datasetMap, processFn ){
var self = this;
return _.map( datasetMap, function( dataset, nameInJob ){
@@ -152,6 +166,9 @@
});
};
+/** Walk all the jobs (vertices), attempting to find connections
+ * between datasets used as both inputs and outputs (edges)
+ */
JobDAG.prototype.createGraph = function _createGraph(){
var self = this;
self.debug( 'connections:' );
@@ -183,6 +200,7 @@
return self;
};
+/** Override to re-sort (ugh) jobs in each component by update time */
Graph.prototype.weakComponentGraphArray = function(){
return this.weakComponents().map( function( component ){
//TODO: this seems to belong above (in sort) - why isn't it preserved?
@@ -198,7 +216,4 @@
// ============================================================================
return JobDAG;
- //return {
- // jobDAG : jobDAG
- //};
});
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 static/scripts/mvc/job/job-li.js
--- a/static/scripts/mvc/job/job-li.js
+++ b/static/scripts/mvc/job/job-li.js
@@ -6,7 +6,8 @@
], function( LIST_ITEM, DATASET_LIST, BASE_MVC, _l ){
//==============================================================================
var _super = LIST_ITEM.FoldoutListItemView;
-/** @class
+/** @class A job view used from within a larger list of jobs.
+ * Each job itself is a foldout panel of history contents displaying the outputs of this job.
*/
var JobListItemView = _super.extend(/** @lends JobListItemView.prototype */{
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 static/scripts/mvc/job/job-model.js
--- a/static/scripts/mvc/job/job-model.js
+++ b/static/scripts/mvc/job/job-model.js
@@ -7,7 +7,7 @@
], function( HISTORY_CONTENTS, STATES, AJAX_QUEUE, BASE_MVC, _l ){
//==============================================================================
var searchableMixin = BASE_MVC.SearchableModelMixin;
-/** @class
+/** @class Represents a job running or ran on the server job handlers.
*/
var Job = Backbone.Model.extend( BASE_MVC.LoggableMixin ).extend(
BASE_MVC.mixin( searchableMixin, /** @lends Job.prototype */{
@@ -126,6 +126,7 @@
},
// ........................................................................ ajax
+ /** fetches all details for each job in the collection using a queue */
queueDetailFetching : function(){
var collection = this,
queue = new AJAX_QUEUE.AjaxQueue( this.map( function( job ){
@@ -179,7 +180,7 @@
//----------------------------------------------------------------------------- class vars
}, {
-
+ /** class level fn for fetching the job details for all jobs in a history */
fromHistory : function( historyId ){
console.debug( this );
var Collection = this,
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 static/scripts/packed/mvc/history/history-structure-view.js
--- a/static/scripts/packed/mvc/history/history-structure-view.js
+++ b/static/scripts/packed/mvc/history/history-structure-view.js
@@ -1,1 +1,1 @@
-define(["mvc/job/job-model","mvc/job/job-li","mvc/history/history-content-model","mvc/history/job-dag","mvc/base-mvc","utils/localization","libs/d3"],function(e,f,i,c,h,a){var b=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure-component",initialize:function(j){this.log(this+"(HistoryStructureComponent).initialize:",j);this.component=j.component;this._jobLiMap={};this._createJobModels();this.layout=this._createLayout(j.layoutOptions)},_createJobModels:function(){var j=this;j.component.eachVertex(function(o){var n=o.data.job,m=new e.Job(n);var l=_.map(m.get("outputs"),function(p){var r=p.src==="hda"?"dataset":"dataset_collection",q=i.typeIdStr(r,p.id);return j.model.contents.get(q)});m.outputCollection.reset(l);m.outputCollection.historyId=structure.model.id;var k=new f.JobListItemView({model:m,expanded:true});k.$el.appendTo(j.$el);j._jobLiMap[m.id]=k});return j.jobs},layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobHeight:308,jobWidthSpacing:320,linkAdjX:4,linkAdjY:0},_createLayout:function(l){l=_.defaults(_.clone(l||{}),this.layoutDefaults);var j=this,k=_.values(j.component.vertices),m=_.extend(l,{nodeMap:{},links:[],el:{width:0,height:0},svg:{width:0,height:0,top:0,left:0}});k.forEach(function(n,o){var p={name:n.name,x:0,y:0};m.nodeMap[n.name]=p});j.component.edges(function(o){var n={source:o.source,target:o.target};m.links.push(n)});return m},_updateLayout:function(){var k=this,l=k.layout;l.svg.height=l.paddingTop+(l.linkSpacing*_.size(l.nodeMap));l.el.height=l.svg.height+l.jobHeight;var j=l.paddingLeft,m=l.svg.height;_.each(l.nodeMap,function(o,n){o.x=j;o.y=m;j+=l.jobWidthSpacing});l.el.width=l.svg.width=Math.max(l.el.width,j);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY});return this.layout},render:function(k){this.log(this+".renderComponent:",k);var j=this;j.component.eachVertex(function(m){var l=j._jobLiMap[m.name];if(!l.$el.is(":visible")){l.render(0)}});j._updateLayout();j.$el.width(j.layout.el.width).height(j.layout.el.height);this.renderSVG();j.component.eachVertex(function(n){var m=j._jobLiMap[n.name],l=j.layout.nodeMap[m.model.id];console.debug(m.$el.is(":visible"));m.$el.css({top:l.y,left:l.x})});return this},renderSVG:function(){var j=this,o=j.layout;var k=d3.select(this.el).select("svg");if(k.empty()){k=d3.select(this.el).append("svg")}k.attr("width",o.svg.width).attr("height",o.svg.height);function m(p){d3.select(this).classed("highlighted",true);j._jobLiMap[p.source].$el.addClass("highlighted");j._jobLiMap[p.target].$el.addClass("highlighted")}function l(p){d3.select(this).classed("highlighted",false);j._jobLiMap[p.source].$el.removeClass("highlighted");j._jobLiMap[p.target].$el.removeClass("highlighted")}var n=k.selectAll(".connection").data(o.links);n.enter().append("path").attr("class","connection").attr("id",function(p){return p.source+"-"+p.target}).on("mouseover",m).on("mouseout",l);n.transition().attr("d",function(p){return j._connectionPath(p)});return k.node()},_connectionPath:function(k){var l=0,j=((k.x2-k.x1)/this.layout.svg.width)*this.layout.svg.height;return["M",k.x1,",",k.y1," ","C",k.x1+l,",",k.y1-j," ",k.x2-l,",",k.y2-j," ",k.x2,",",k.y2].join("")},events:{"mouseover .job.list-item":function(j){this.highlightConnected(j.currentTarget,true)},"mouseout .job.list-item":function(j){this.highlightConnected(j.currentTarget,false)}},highlightConnected:function(p,k){k=k!==undefined?k:true;var j=this,l=j.component,n=k?jQuery.prototype.addClass:jQuery.prototype.removeClass,m=k?"connection highlighted":"connection";var o=n.call($(p),"highlighted"),q=o.attr("id").replace("job-","");l.edges({target:q}).forEach(function(r){n.call(j.$("#job-"+r.source),"highlighted");j.$("#"+r.source+"-"+q).attr("class",m)});l.vertices[q].eachEdge(function(r){n.call(j.$("#job-"+r.target),"highlighted");j.$("#"+q+"-"+r.target).attr("class",m)})},toString:function(){return"HistoryStructureComponent("+this.model.id+")"}});var g=b.extend({logger:console,className:b.prototype.className+" vertical",layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobWidth:308,jobHeight:308,initialSpacing:64,jobSpacing:16,linkAdjX:0,linkAdjY:4},_updateLayout:function(){var k=this,l=k.layout;l.svg.width=l.paddingLeft+(l.linkSpacing*_.size(l.nodeMap));l.el.width=l.svg.width+l.jobWidth;l.el.height=0;var j=l.svg.width,m=l.paddingTop;_.each(l.nodeMap,function(p,o){p.x=j;p.y=m;var n=k._jobLiMap[o];if(n.$el.is(":visible")){console.debug("li is visible, adj. by:",n.$el.height());m+=n.$el.height()+l.jobSpacing}else{m+=l.initialSpacing+l.jobSpacing}});l.el.height=l.svg.height=Math.max(l.el.height,m);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY;k.debug("link:",n.x1,n.y1,n.x2,n.y2,n)});k.debug("el:",l.el);k.debug("svg:",l.svg);return l},_connectionPath:function(l){var k=0,j=((l.y2-l.y1)/this.layout.svg.height)*this.layout.svg.width;return["M",l.x1,",",l.y1," ","C",l.x1-j,",",l.y1+k," ",l.x2-j,",",l.y2-k," ",l.x2,",",l.y2].join("")},toString:function(){return"VerticalHistoryStructureComponent("+this.model.id+")"}});var d=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure",initialize:function(j){this.log(this+"(HistoryStructureView).initialize:",j,this.model);this.jobs=j.jobs;this._createDAG()},_createDAG:function(){this.dag=new c({historyContents:this.model.contents.toJSON(),jobs:this.jobs,excludeSetMetadata:true,excludeErroredJobs:true});window.dag=this.dag;this.log(this+".dag:",this.dag);this._createComponents()},_createComponents:function(){this.log(this+"._createComponents");var j=this;window.structure=j;j.componentViews=j.dag.weakComponentGraphArray().map(function(k){return j._createComponent(k)})},_createComponent:function(j){this.log(this+"._createComponent:",j);return new b({model:this.model,component:j})},render:function(k){this.log(this+".render:",k);var j=this;j.$el.html(['<div class="controls"></div>','<div class="components"></div>'].join(""));j.componentViews.forEach(function(l){l.render().$el.appendTo(j.$components())});return j},$components:function(){return this.$(".components")},toString:function(){return"HistoryStructureView()"}});return d});
\ No newline at end of file
+define(["mvc/job/job-model","mvc/job/job-li","mvc/history/history-content-model","mvc/history/job-dag","mvc/base-mvc","utils/localization","libs/d3"],function(e,f,i,c,h,a){var b=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure-component",initialize:function(j){this.log(this+"(HistoryStructureComponent).initialize:",j);this.component=j.component;this._jobLiMap={};this._createJobModels();this.layout=this._createLayout(j.layoutOptions)},_createJobModels:function(){var j=this;j.component.eachVertex(function(o){var n=o.data.job,m=new e.Job(n);var l=_.map(m.get("outputs"),function(p){var r=p.src==="hda"?"dataset":"dataset_collection",q=i.typeIdStr(r,p.id);return j.model.contents.get(q)});m.outputCollection.reset(l);m.outputCollection.historyId=structure.model.id;var k=new f.JobListItemView({model:m,expanded:true});k.$el.appendTo(j.$el);j._jobLiMap[m.id]=k});return j.jobs},layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobHeight:308,jobWidthSpacing:320,linkAdjX:4,linkAdjY:0},_createLayout:function(l){l=_.defaults(_.clone(l||{}),this.layoutDefaults);var j=this,k=_.values(j.component.vertices),m=_.extend(l,{nodeMap:{},links:[],el:{width:0,height:0},svg:{width:0,height:0,top:0,left:0}});k.forEach(function(n,o){var p={name:n.name,x:0,y:0};m.nodeMap[n.name]=p});j.component.edges(function(o){var n={source:o.source,target:o.target};m.links.push(n)});return m},_updateLayout:function(){var k=this,l=k.layout;l.svg.height=l.paddingTop+(l.linkSpacing*_.size(l.nodeMap));l.el.height=l.svg.height+l.jobHeight;var j=l.paddingLeft,m=l.svg.height;_.each(l.nodeMap,function(o,n){o.x=j;o.y=m;j+=l.jobWidthSpacing});l.el.width=l.svg.width=Math.max(l.el.width,j);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY});return this.layout},render:function(k){this.log(this+".renderComponent:",k);var j=this;j.component.eachVertex(function(m){var l=j._jobLiMap[m.name];if(!l.$el.is(":visible")){l.render(0)}});j._updateLayout();j.$el.width(j.layout.el.width).height(j.layout.el.height);this.renderSVG();j.component.eachVertex(function(n){var m=j._jobLiMap[n.name],l=j.layout.nodeMap[m.model.id];m.$el.css({top:l.y,left:l.x})});return this},renderSVG:function(){var j=this,o=j.layout;var k=d3.select(this.el).select("svg");if(k.empty()){k=d3.select(this.el).append("svg")}k.attr("width",o.svg.width).attr("height",o.svg.height);function m(p){d3.select(this).classed("highlighted",true);j._jobLiMap[p.source].$el.addClass("highlighted");j._jobLiMap[p.target].$el.addClass("highlighted")}function l(p){d3.select(this).classed("highlighted",false);j._jobLiMap[p.source].$el.removeClass("highlighted");j._jobLiMap[p.target].$el.removeClass("highlighted")}var n=k.selectAll(".connection").data(o.links);n.enter().append("path").attr("class","connection").attr("id",function(p){return p.source+"-"+p.target}).on("mouseover",m).on("mouseout",l);n.transition().attr("d",function(p){return j._connectionPath(p)});return k.node()},_connectionPath:function(k){var l=0,j=((k.x2-k.x1)/this.layout.svg.width)*this.layout.svg.height;return["M",k.x1,",",k.y1," ","C",k.x1+l,",",k.y1-j," ",k.x2-l,",",k.y2-j," ",k.x2,",",k.y2].join("")},events:{"mouseover .job.list-item":function(j){this.highlightConnected(j.currentTarget,true)},"mouseout .job.list-item":function(j){this.highlightConnected(j.currentTarget,false)}},highlightConnected:function(p,k){k=k!==undefined?k:true;var j=this,l=j.component,n=k?jQuery.prototype.addClass:jQuery.prototype.removeClass,m=k?"connection highlighted":"connection";var o=n.call($(p),"highlighted"),q=o.attr("id").replace("job-","");l.edges({target:q}).forEach(function(r){n.call(j.$("#job-"+r.source),"highlighted");j.$("#"+r.source+"-"+q).attr("class",m)});l.vertices[q].eachEdge(function(r){n.call(j.$("#job-"+r.target),"highlighted");j.$("#"+q+"-"+r.target).attr("class",m)})},toString:function(){return"HistoryStructureComponent("+this.model.id+")"}});var g=b.extend({logger:console,className:b.prototype.className+" vertical",layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobWidth:308,jobHeight:308,initialSpacing:64,jobSpacing:16,linkAdjX:0,linkAdjY:4},_updateLayout:function(){var k=this,l=k.layout;l.svg.width=l.paddingLeft+(l.linkSpacing*_.size(l.nodeMap));l.el.width=l.svg.width+l.jobWidth;l.el.height=0;var j=l.svg.width,m=l.paddingTop;_.each(l.nodeMap,function(p,o){p.x=j;p.y=m;var n=k._jobLiMap[o];if(n.$el.is(":visible")){m+=n.$el.height()+l.jobSpacing}else{m+=l.initialSpacing+l.jobSpacing}});l.el.height=l.svg.height=Math.max(l.el.height,m);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY;k.debug("link:",n.x1,n.y1,n.x2,n.y2,n)});k.debug("el:",l.el);k.debug("svg:",l.svg);return l},_connectionPath:function(l){var k=0,j=((l.y2-l.y1)/this.layout.svg.height)*this.layout.svg.width;return["M",l.x1,",",l.y1," ","C",l.x1-j,",",l.y1+k," ",l.x2-j,",",l.y2-k," ",l.x2,",",l.y2].join("")},toString:function(){return"VerticalHistoryStructureComponent("+this.model.id+")"}});var d=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure",initialize:function(j){this.log(this+"(HistoryStructureView).initialize:",j,this.model);this.jobs=j.jobs;this._createDAG()},_createDAG:function(){this.dag=new c({historyContents:this.model.contents.toJSON(),jobs:this.jobs,excludeSetMetadata:true,excludeErroredJobs:true});this.log(this+".dag:",this.dag);this._createComponents()},_createComponents:function(){this.log(this+"._createComponents");var j=this;j.componentViews=j.dag.weakComponentGraphArray().map(function(k){return j._createComponent(k)})},_createComponent:function(j){this.log(this+"._createComponent:",j);return new b({model:this.model,component:j})},render:function(k){this.log(this+".render:",k);var j=this;j.$el.html(['<div class="controls"></div>','<div class="components"></div>'].join(""));j.componentViews.forEach(function(l){l.render().$el.appendTo(j.$components())});return j},$components:function(){return this.$(".components")},toString:function(){return"HistoryStructureView()"}});return d});
\ No newline at end of file
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 static/scripts/packed/utils/graph.js
--- a/static/scripts/packed/utils/graph.js
+++ b/static/scripts/packed/utils/graph.js
@@ -1,1 +1,1 @@
-define([],function(){function l(r,q){for(var p in r){if(r.hasOwnProperty(p)){q(r[p],p,r)}}}function i(r,q){for(var p in q){if(q.hasOwnProperty(p)){r[p]=q[p]}}return r}function f(r,q){for(var p in q){if(q.hasOwnProperty(p)){if(!r.hasOwnProperty(p)||r[p]!==q[p]){return false}}}return true}function c(w,p){var t=typeof p==="function"?p:undefined,s=typeof p==="object"?p:undefined,u=[],q=0;for(var r in w){if(w.hasOwnProperty(r)){var v=w[r];if(t){u.push(t.call(v,v,r,q))}else{if(s){if(typeof v==="object"&&f(v,s)){u.push(v)}}else{u.push(v)}}q+=1}}return u}function g(r,s,q){var p=this;p.source=r!==undefined?r:null;p.target=s!==undefined?s:null;p.data=q||null;return p}g.prototype.toString=function(){return this.source+"->"+this.target};g.prototype.toJSON=function(){var p={source:this.source,target:this.target};if(this.data){p.data=this.data}return p};function m(q,r){var p=this;p.name=q!==undefined?q:"(unnamed)";p.data=r||null;p.edges={};p.degree=0;return p}window.Vertex=m;m.prototype.toString=function(){return"Vertex("+this.name+")"};m.prototype.eachEdge=function(p){return c(this.edges,p)};m.prototype.toJSON=function(){return{name:this.name,data:this.data}};var k=function(r,q){var p=this;p.graph=r;p.processFns=q||{vertexEarly:function(t,s){},edge:function(u,t,s){},vertexLate:function(t,s){}};p._cache={};return p};k.prototype.search=function e(q){var p=this;if(q in p._cache){return p._cache[q]}if(!(q instanceof m)){q=p.graph.vertices[q]}return(p._cache[q.name]=p._search(q))};k.prototype._searchTree=function a(q){var p=this;return new j(true,{edges:q.edges,vertices:Object.keys(q.discovered).map(function(r){return p.graph.vertices[r].toJSON()})})};k.prototype.searchTree=function b(p){return this._searchTree(this.search(p))};var n=function(r,q){var p=this;k.call(this,r,q);return p};n.prototype=new k();n.prototype.constructor=n;n.prototype._search=function d(u,s){s=s||{discovered:{},edges:[]};var r=this,p=[];function q(v,w){var x=this;if(r.processFns.edge){r.processFns.edge.call(r,x,w,s)}if(!s.discovered[v.name]){s.discovered[v.name]=true;s.edges.push({source:x.name,target:v.name});p.push(v)}}s.discovered[u.name]=true;p.push(u);while(p.length){var t=p.shift();if(r.processFns.vertexEarly){r.processFns.vertexEarly.call(r,t,s)}r.graph.eachAdjacent(t,q);if(r.processFns.vertexLate){r.processFns.vertexLate.call(r,t,s)}}return s};var h=function(r,q){var p=this;k.call(this,r,q);return p};h.prototype=new k();h.prototype.constructor=h;h.prototype._search=function(u,q){q=q||{discovered:{},edges:[],entryTimes:{},exitTimes:{}};var p=this,t=0;function s(w,x){var v=this;if(p.processFns.edge){p.processFns.edge.call(p,v,x,q)}if(!q.discovered[w.name]){q.edges.push({source:v.name,target:w.name});r(w)}}function r(v){q.discovered[v.name]=true;if(p.processFns.vertexEarly){p.processFns.vertexEarly.call(p,v,q)}q.entryTimes[v.name]=t++;p.graph.eachAdjacent(v,s);if(p.processFns.vertexLate){p.processFns.vertexLate.call(p,v,q)}q.exitTimes[v.name]=t++}r(u);return q};function j(q,r,p){this.directed=q||false;return this.init(p).read(r)}window.Graph=j;j.prototype.init=function(q){q=q||{};var p=this;p.allowReflexiveEdges=q.allowReflexiveEdges||false;p.vertices={};p.numEdges=0;return p};j.prototype.read=function(q){if(!q){return this}var p=this;if(q.hasOwnProperty("nodes")){return p.readNodesAndLinks(q)}if(q.hasOwnProperty("vertices")){return p.readVerticesAndEdges(q)}return p};j.prototype.readNodesAndLinks=function(q){if(!(q&&q.hasOwnProperty("nodes"))){return this}var p=this;q.nodes.forEach(function(r){p.createVertex(r.name,r.data)});(q.links||[]).forEach(function(u,s){var r=q.nodes[u.source].name,t=q.nodes[u.target].name;p.createEdge(r,t,p.directed)});return p};j.prototype.readVerticesAndEdges=function(q){if(!(q&&q.hasOwnProperty("vertices"))){return this}var p=this;q.vertices.forEach(function(r){p.createVertex(r.name,r.data)});(q.edges||[]).forEach(function(s,r){p.createEdge(s.source,s.target,p.directed)});return p};j.prototype.createVertex=function(p,q){if(this.vertices[p]){return this.vertices[p]}return(this.vertices[p]=new m(p,q))};j.prototype.createEdge=function(q,r,t,u){var v=q===r;if(!this.allowReflexiveEdges&&v){return null}sourceVertex=this.vertices[q];targetVertex=this.vertices[r];if(!(sourceVertex&&targetVertex)){return null}var p=this,s=new g(q,r,u);sourceVertex.edges[r]=s;sourceVertex.degree+=1;p.numEdges+=1;if(!v&&!t){p.createEdge(r,q,true)}return s};j.prototype.edges=function(p){return Array.prototype.concat.apply([],this.eachVertex(function(q){return q.eachEdge(p)}))};j.prototype.eachVertex=function(p){return c(this.vertices,p)};j.prototype.adjacent=function(q){var p=this;return c(q.edges,function(r){return p.vertices[r.target]})};j.prototype.eachAdjacent=function(r,q){var p=this;return c(r.edges,function(t){var s=p.vertices[t.target];return q.call(r,s,t)})};j.prototype.print=function(){var p=this;console.log("Graph has "+Object.keys(p.vertices).length+" vertices");p.eachVertex(function(q){console.log(q.toString());q.eachEdge(function(r){console.log("\t "+r)})});return p};j.prototype.toDOT=function(){var q=this,p=[];p.push("graph bler {");q.edges(function(r){p.push("\t"+r.from+" -- "+r.to+";")});p.push("}");return p.join("\n")};j.prototype.toNodesAndLinks=function(){var p=this,q={};return{nodes:p.eachVertex(function(t,s,r){q[t.name]=r;return t.toJSON()}),links:p.edges(function(s){var r=s.toJSON();r.source=q[s.source];r.target=q[s.target];return r})}};j.prototype.toVerticesAndEdges=function(){var p=this;return{vertices:p.eachVertex(function(r,q){return r.toJSON()}),edges:p.edges(function(q){return q.toJSON()})}};j.prototype.breadthFirstSearch=function(q,p){return new n(this).search(q)};j.prototype.breadthFirstSearchTree=function(q,p){return new n(this).searchTree(q)};j.prototype.depthFirstSearch=function(q,p){return new h(this).search(q)};j.prototype.depthFirstSearchTree=function(q,p){return new h(this).searchTree(q)};j.prototype.weakComponents=function(){var r=this,u=this,q,t=[];function p(v){var w=new h(u)._search(v);q=q.filter(function(x){return !(x in w.discovered)});return{vertices:Object.keys(w.discovered).map(function(x){return r.vertices[x].toJSON()}),edges:w.edges.map(function(y){var x=r.vertices[y.target].edges[y.source]!==undefined;if(r.directed&&x){var z=y.source;y.source=y.target;y.target=z}return y})}}if(r.directed){u=new j(false,r.toNodesAndLinks())}q=Object.keys(u.vertices);while(q.length){var s=u.vertices[q.shift()];t.push(p(s))}return t};j.prototype.weakComponentGraph=function(){var p=this.weakComponents();return new j(this.directed,{vertices:p.reduce(function(r,q){return r.concat(q.vertices)},[]),edges:p.reduce(function(r,q){return r.concat(q.edges)},[])})};j.prototype.weakComponentGraphArray=function(){return this.weakComponents().map(function(p){return new j(this.directed,p)})};function o(s,p,r){var u={nodes:[],links:[]};function t(v){return Math.floor(Math.random()*v)}for(var q=0;q<p;q++){u.nodes.push({name:q})}for(q=0;q<r;q++){u.links.push({source:t(p),target:t(p)})}return new j(s,u)}return{Vertex:m,Edge:g,BreadthFirstSearch:n,DepthFirstSearch:h,Graph:j,randGraph:o}});
\ No newline at end of file
+define([],function(){function l(r,q){for(var p in r){if(r.hasOwnProperty(p)){q(r[p],p,r)}}}function i(r,q){for(var p in q){if(q.hasOwnProperty(p)){r[p]=q[p]}}return r}function f(r,q){for(var p in q){if(q.hasOwnProperty(p)){if(!r.hasOwnProperty(p)||r[p]!==q[p]){return false}}}return true}function c(w,p){var t=typeof p==="function"?p:undefined,s=typeof p==="object"?p:undefined,u=[],q=0;for(var r in w){if(w.hasOwnProperty(r)){var v=w[r];if(t){u.push(t.call(v,v,r,q))}else{if(s){if(typeof v==="object"&&f(v,s)){u.push(v)}}else{u.push(v)}}q+=1}}return u}function g(r,s,q){var p=this;p.source=r!==undefined?r:null;p.target=s!==undefined?s:null;p.data=q||null;return p}g.prototype.toString=function(){return this.source+"->"+this.target};g.prototype.toJSON=function(){var p={source:this.source,target:this.target};if(this.data){p.data=this.data}return p};function m(q,r){var p=this;p.name=q!==undefined?q:"(unnamed)";p.data=r||null;p.edges={};p.degree=0;return p}m.prototype.toString=function(){return"Vertex("+this.name+")"};m.prototype.eachEdge=function(p){return c(this.edges,p)};m.prototype.toJSON=function(){return{name:this.name,data:this.data}};var k=function(r,q){var p=this;p.graph=r;p.processFns=q||{vertexEarly:function(t,s){},edge:function(u,t,s){},vertexLate:function(t,s){}};p._cache={};return p};k.prototype.search=function e(q){var p=this;if(q in p._cache){return p._cache[q]}if(!(q instanceof m)){q=p.graph.vertices[q]}return(p._cache[q.name]=p._search(q))};k.prototype._search=function d(q,p){p=p||{discovered:{},edges:[]};return p};k.prototype.searchTree=function b(p){return this._searchTree(this.search(p))};k.prototype._searchTree=function a(q){var p=this;return new j(true,{edges:q.edges,vertices:Object.keys(q.discovered).map(function(r){return p.graph.vertices[r].toJSON()})})};var n=function(r,q){var p=this;k.call(this,r,q);return p};n.prototype=new k();n.prototype.constructor=n;n.prototype._search=function d(u,s){s=s||{discovered:{},edges:[]};var r=this,p=[];function q(v,w){var x=this;if(r.processFns.edge){r.processFns.edge.call(r,x,w,s)}if(!s.discovered[v.name]){s.discovered[v.name]=true;s.edges.push({source:x.name,target:v.name});p.push(v)}}s.discovered[u.name]=true;p.push(u);while(p.length){var t=p.shift();if(r.processFns.vertexEarly){r.processFns.vertexEarly.call(r,t,s)}r.graph.eachAdjacent(t,q);if(r.processFns.vertexLate){r.processFns.vertexLate.call(r,t,s)}}return s};var h=function(r,q){var p=this;k.call(this,r,q);return p};h.prototype=new k();h.prototype.constructor=h;h.prototype._search=function(u,q){q=q||{discovered:{},edges:[],entryTimes:{},exitTimes:{}};var p=this,t=0;function s(w,x){var v=this;if(p.processFns.edge){p.processFns.edge.call(p,v,x,q)}if(!q.discovered[w.name]){q.edges.push({source:v.name,target:w.name});r(w)}}function r(v){q.discovered[v.name]=true;if(p.processFns.vertexEarly){p.processFns.vertexEarly.call(p,v,q)}q.entryTimes[v.name]=t++;p.graph.eachAdjacent(v,s);if(p.processFns.vertexLate){p.processFns.vertexLate.call(p,v,q)}q.exitTimes[v.name]=t++}r(u);return q};function j(q,r,p){this.directed=q||false;return this.init(p).read(r)}window.Graph=j;j.prototype.init=function(q){q=q||{};var p=this;p.allowReflexiveEdges=q.allowReflexiveEdges||false;p.vertices={};p.numEdges=0;return p};j.prototype.read=function(q){if(!q){return this}var p=this;if(q.hasOwnProperty("nodes")){return p.readNodesAndLinks(q)}if(q.hasOwnProperty("vertices")){return p.readVerticesAndEdges(q)}return p};j.prototype.readNodesAndLinks=function(q){if(!(q&&q.hasOwnProperty("nodes"))){return this}var p=this;q.nodes.forEach(function(r){p.createVertex(r.name,r.data)});(q.links||[]).forEach(function(u,s){var r=q.nodes[u.source].name,t=q.nodes[u.target].name;p.createEdge(r,t,p.directed)});return p};j.prototype.readVerticesAndEdges=function(q){if(!(q&&q.hasOwnProperty("vertices"))){return this}var p=this;q.vertices.forEach(function(r){p.createVertex(r.name,r.data)});(q.edges||[]).forEach(function(s,r){p.createEdge(s.source,s.target,p.directed)});return p};j.prototype.createVertex=function(p,q){if(this.vertices[p]){return this.vertices[p]}return(this.vertices[p]=new m(p,q))};j.prototype.createEdge=function(q,r,t,u){var v=q===r;if(!this.allowReflexiveEdges&&v){return null}sourceVertex=this.vertices[q];targetVertex=this.vertices[r];if(!(sourceVertex&&targetVertex)){return null}var p=this,s=new g(q,r,u);sourceVertex.edges[r]=s;sourceVertex.degree+=1;p.numEdges+=1;if(!v&&!t){p.createEdge(r,q,true)}return s};j.prototype.edges=function(p){return Array.prototype.concat.apply([],this.eachVertex(function(q){return q.eachEdge(p)}))};j.prototype.eachVertex=function(p){return c(this.vertices,p)};j.prototype.adjacent=function(q){var p=this;return c(q.edges,function(r){return p.vertices[r.target]})};j.prototype.eachAdjacent=function(r,q){var p=this;return c(r.edges,function(t){var s=p.vertices[t.target];return q.call(r,s,t)})};j.prototype.print=function(){var p=this;console.log("Graph has "+Object.keys(p.vertices).length+" vertices");p.eachVertex(function(q){console.log(q.toString());q.eachEdge(function(r){console.log("\t "+r)})});return p};j.prototype.toDOT=function(){var q=this,p=[];p.push("graph bler {");q.edges(function(r){p.push("\t"+r.from+" -- "+r.to+";")});p.push("}");return p.join("\n")};j.prototype.toNodesAndLinks=function(){var p=this,q={};return{nodes:p.eachVertex(function(t,s,r){q[t.name]=r;return t.toJSON()}),links:p.edges(function(s){var r=s.toJSON();r.source=q[s.source];r.target=q[s.target];return r})}};j.prototype.toVerticesAndEdges=function(){var p=this;return{vertices:p.eachVertex(function(r,q){return r.toJSON()}),edges:p.edges(function(q){return q.toJSON()})}};j.prototype.breadthFirstSearch=function(q,p){return new n(this).search(q)};j.prototype.breadthFirstSearchTree=function(q,p){return new n(this).searchTree(q)};j.prototype.depthFirstSearch=function(q,p){return new h(this).search(q)};j.prototype.depthFirstSearchTree=function(q,p){return new h(this).searchTree(q)};j.prototype.weakComponents=function(){var r=this,u=this,q,t=[];function p(v){var w=new h(u)._search(v);q=q.filter(function(x){return !(x in w.discovered)});return{vertices:Object.keys(w.discovered).map(function(x){return r.vertices[x].toJSON()}),edges:w.edges.map(function(y){var x=r.vertices[y.target].edges[y.source]!==undefined;if(r.directed&&x){var z=y.source;y.source=y.target;y.target=z}return y})}}if(r.directed){u=new j(false,r.toNodesAndLinks())}q=Object.keys(u.vertices);while(q.length){var s=u.vertices[q.shift()];t.push(p(s))}return t};j.prototype.weakComponentGraph=function(){var p=this.weakComponents();return new j(this.directed,{vertices:p.reduce(function(r,q){return r.concat(q.vertices)},[]),edges:p.reduce(function(r,q){return r.concat(q.edges)},[])})};j.prototype.weakComponentGraphArray=function(){return this.weakComponents().map(function(p){return new j(this.directed,p)})};function o(s,p,r){var u={nodes:[],links:[]};function t(v){return Math.floor(Math.random()*v)}for(var q=0;q<p;q++){u.nodes.push({name:q})}for(q=0;q<r;q++){u.links.push({source:t(p),target:t(p)})}return new j(s,u)}return{Vertex:m,Edge:g,BreadthFirstSearch:n,DepthFirstSearch:h,Graph:j,randGraph:o}});
\ No newline at end of file
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 static/scripts/utils/graph.js
--- a/static/scripts/utils/graph.js
+++ b/static/scripts/utils/graph.js
@@ -2,10 +2,10 @@
],function(){
/* ============================================================================
TODO:
- edges can't retain data
============================================================================ */
//TODO: go ahead and move to underscore...
+/** call fn on each key/value in d */
function each( d, fn ){
for( var k in d ){
if( d.hasOwnProperty( k ) ){
@@ -14,6 +14,7 @@
}
}
+/** copy key/values from d2 to d overwriting if present */
function extend( d, d2 ){
for( var k in d2 ){
if( d2.hasOwnProperty( k ) ){
@@ -23,6 +24,7 @@
return d;
}
+/** deep equal of two dictionaries */
function matches( d, d2 ){
for( var k in d2 ){
if( d2.hasOwnProperty( k ) ){
@@ -34,6 +36,10 @@
return true;
}
+/** map key/values in obj
+ * if propsOrFn is an object, return only those k/v that match the object
+ * if propsOrFn is function, call the fn and returned the mapped values from it
+ */
function iterate( obj, propsOrFn ){
var fn = typeof propsOrFn === 'function'? propsOrFn : undefined,
props = typeof propsOrFn === 'object'? propsOrFn : undefined,
@@ -60,6 +66,8 @@
// ============================================================================
+/** A graph edge containing the name/id of both source and target and optional data
+ */
function Edge( source, target, data ){
var self = this;
self.source = source !== undefined? source : null;
@@ -70,10 +78,12 @@
//}
return self;
}
+/** String representation */
Edge.prototype.toString = function(){
return this.source + '->' + this.target;
};
+/** Return a plain object representing this edge */
Edge.prototype.toJSON = function(){
//TODO: this is safe in most browsers (fns will be stripped) - alter tests to incorporate this in order to pass data
//return this;
@@ -88,6 +98,9 @@
};
// ============================================================================
+/** A graph vertex with a (unique) name/id and optional data.
+ * A vertex contains a list of Edges (whose sources are this vertex) and maintains the degree.
+ */
function Vertex( name, data ){
var self = this;
self.name = name !== undefined? name : '(unnamed)';
@@ -96,16 +109,19 @@
self.degree = 0;
return self;
}
-window.Vertex = Vertex;
+
+/** String representation */
Vertex.prototype.toString = function(){
return 'Vertex(' + this.name + ')';
};
//TODO: better name w no collision for either this.eachEdge or this.edges
+/** Iterate over each edge from this vertex */
Vertex.prototype.eachEdge = function( propsOrFn ){
return iterate( this.edges, propsOrFn );
};
+/** Return a plain object representing this vertex */
Vertex.prototype.toJSON = function(){
//return this;
return {
@@ -116,6 +132,10 @@
// ============================================================================
+/** Base (abstract) class for Graph search algorithms.
+ * Pass in the graph to search
+ * and an optional dictionary containing the 3 vertex/edge processing fns listed below.
+ */
var GraphSearch = function( graph, processFns ){
var self = this;
self.graph = graph;
@@ -136,6 +156,9 @@
return self;
};
+/** Search interface where start is the vertex (or the name/id of the vertex) to begin the search at
+ * This public interface caches searches and returns the cached version if it's already been done.
+ */
GraphSearch.prototype.search = function _search( start ){
var self = this;
if( start in self._cache ){ return self._cache[ start ]; }
@@ -143,6 +166,22 @@
return ( self._cache[ start.name ] = self._search( start ) );
};
+/** Actual search (private) function (abstract here) */
+GraphSearch.prototype._search = function __search( start, search ){
+ search = search || {
+ discovered : {},
+ //parents : {},
+ edges : []
+ };
+ return search;
+};
+
+/** Searches graph from start and returns a search tree of the results */
+GraphSearch.prototype.searchTree = function _searchTree( start ){
+ return this._searchTree( this.search( start ) );
+};
+
+/** Helper fn that returns a graph (a search tree) based on the search object passed in (does not actually search) */
GraphSearch.prototype._searchTree = function __searchTree( search ){
var self = this;
return new Graph( true, {
@@ -153,11 +192,10 @@
});
};
-GraphSearch.prototype.searchTree = function _searchTree( start ){
- return this._searchTree( this.search( start ) );
-};
// ============================================================================
+/** Breadth first search algo.
+ */
var BreadthFirstSearch = function( graph, processFns ){
var self = this;
GraphSearch.call( this, graph, processFns );
@@ -166,6 +204,7 @@
BreadthFirstSearch.prototype = new GraphSearch();
BreadthFirstSearch.prototype.constructor = BreadthFirstSearch;
+/** (Private) implementation of BFS */
BreadthFirstSearch.prototype._search = function __search( start, search ){
search = search || {
discovered : {},
@@ -205,6 +244,8 @@
// ============================================================================
+/** Depth first search algorithm.
+ */
var DepthFirstSearch = function( graph, processFns ){
var self = this;
GraphSearch.call( this, graph, processFns );
@@ -213,6 +254,7 @@
DepthFirstSearch.prototype = new GraphSearch();
DepthFirstSearch.prototype.constructor = DepthFirstSearch;
+/** (Private) implementation of DFS */
DepthFirstSearch.prototype._search = function( start, search ){
//console.debug( 'depthFirstSearch:', start );
search = search || {
@@ -258,6 +300,8 @@
// ============================================================================
+/** A directed/non-directed graph object.
+ */
function Graph( directed, data, options ){
//TODO: move directed to options
this.directed = directed || false;
@@ -265,7 +309,7 @@
}
window.Graph = Graph;
-
+/** Set up options and instance variables */
Graph.prototype.init = function( options ){
options = options || {};
var self = this;
@@ -277,6 +321,7 @@
return self;
};
+/** Read data from the plain object data - both in d3 form (nodes and links) or vertices and edges */
Graph.prototype.read = function( data ){
if( !data ){ return this; }
var self = this;
@@ -286,6 +331,7 @@
};
//TODO: the next two could be combined
+/** Create the graph using a list of nodes and a list of edges (where source and target are indeces into nodes) */
Graph.prototype.readNodesAndLinks = function( data ){
if( !( data && data.hasOwnProperty( 'nodes' ) ) ){ return this; }
//console.debug( 'readNodesAndLinks:', data );
@@ -306,6 +352,7 @@
return self;
};
+/** Create the graph using a list of nodes and a list of edges (where source and target are names of nodes) */
Graph.prototype.readVerticesAndEdges = function( data ){
if( !( data && data.hasOwnProperty( 'vertices' ) ) ){ return this; }
//console.debug( 'readVerticesAndEdges:', data );
@@ -324,12 +371,16 @@
return self;
};
+/** Return the vertex with name, creating it if necessary */
Graph.prototype.createVertex = function( name, data ){
//console.debug( 'createVertex:', name, data );
if( this.vertices[ name ] ){ return this.vertices[ name ]; }
return ( this.vertices[ name ] = new Vertex( name, data ) );
};
+/** Create an edge in vertex named sourceName to targetName (optionally adding data to it)
+ * If directed is false, create a second edge from targetName to sourceName.
+ */
Graph.prototype.createEdge = function( sourceName, targetName, directed, data ){
//note: allows multiple 'equivalent' edges (to/from same source/target)
//console.debug( 'createEdge:', source, target, directed );
@@ -359,16 +410,19 @@
return edge;
};
+/** Walk over all the edges of the graph using the vertex.eachEdge iterator */
Graph.prototype.edges = function( propsOrFn ){
return Array.prototype.concat.apply( [], this.eachVertex( function( vertex ){
return vertex.eachEdge( propsOrFn );
}));
};
+/** Iterate over all the vertices in the graph */
Graph.prototype.eachVertex = function( propsOrFn ){
return iterate( this.vertices, propsOrFn );
};
+/** Return a list of the vertices adjacent to vertex */
Graph.prototype.adjacent = function( vertex ){
var self = this;
return iterate( vertex.edges, function( edge ){
@@ -376,6 +430,7 @@
});
};
+/** Call fn on each vertex adjacent to vertex */
Graph.prototype.eachAdjacent = function( vertex, fn ){
var self = this;
return iterate( vertex.edges, function( edge ){
@@ -384,6 +439,7 @@
});
};
+/** Print the graph to the console (debugging) */
Graph.prototype.print = function(){
var self = this;
console.log( 'Graph has ' + Object.keys( self.vertices ).length + ' vertices' );
@@ -396,6 +452,7 @@
return self;
};
+/** Return a DOT format string of this graph */
Graph.prototype.toDOT = function(){
var self = this,
strings = [];
@@ -407,6 +464,7 @@
return strings.join( '\n' );
};
+/** Return vertices and edges of this graph in d3 node/link format */
Graph.prototype.toNodesAndLinks = function(){
var self = this,
indeces = {};
@@ -424,6 +482,7 @@
};
};
+/** Return vertices and edges of this graph where edges use the name/id as source and target */
Graph.prototype.toVerticesAndEdges = function(){
var self = this;
return {
@@ -436,18 +495,22 @@
};
};
+/** Search this graph using BFS */
Graph.prototype.breadthFirstSearch = function( start, processFns ){
return new BreadthFirstSearch( this ).search( start );
};
+/** Return a searchtree of this graph using BFS */
Graph.prototype.breadthFirstSearchTree = function( start, processFns ){
return new BreadthFirstSearch( this ).searchTree( start );
};
+/** Search this graph using DFS */
Graph.prototype.depthFirstSearch = function( start, processFns ){
return new DepthFirstSearch( this ).search( start );
};
+/** Return a searchtree of this graph using DFS */
Graph.prototype.depthFirstSearchTree = function( start, processFns ){
return new DepthFirstSearch( this ).searchTree( start );
};
@@ -465,6 +528,7 @@
//Graph.prototype.isBipartite = function(){
//};
+/** Return an array of weakly connected (no edges between) sub-graphs in this graph */
Graph.prototype.weakComponents = function(){
//TODO: alternately, instead of returning graph-like objects:
// - could simply decorate the vertices (vertex.component = componentIndex), or clone the graph and do that
@@ -515,6 +579,7 @@
return components;
};
+/** Return a single graph containing the weakly connected components in this graph */
Graph.prototype.weakComponentGraph = function(){
//note: although this can often look like the original graph - edges can be lost
var components = this.weakComponents();
@@ -528,6 +593,7 @@
});
};
+/** Return an array of graphs of the weakly connected components in this graph */
Graph.prototype.weakComponentGraphArray = function(){
//note: although this can often look like the original graph - edges can be lost
return this.weakComponents().map( function( component ){
@@ -537,10 +603,8 @@
// ============================================================================
-
-
-
-// ============================================================================
+/** Create a random graph with numVerts vertices and numEdges edges (for testing)
+ */
function randGraph( directed, numVerts, numEdges ){
//console.debug( 'randGraph', directed, numVerts, numEdges );
var data = { nodes : [], links : [] };
diff -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 -r 8f8341ac47cd276cf0a2aa3d1c1098f7d3d58c75 templates/webapps/galaxy/history/structure.mako
--- a/templates/webapps/galaxy/history/structure.mako
+++ b/templates/webapps/galaxy/history/structure.mako
@@ -95,9 +95,6 @@
<script type="text/javascript">
/*
TODO:
- components should be graphs?
- as it is they're verts+edges
-
in-panel view of anc desc
show datasets when job not expanded
@@ -119,8 +116,8 @@
controls:
(optionally) filter all deleted
(optionally) filter all hidden
- (optionally) filter __SET_METADATA__
- (optionally) filter error'd jobs
+ //(optionally) filter __SET_METADATA__
+ //(optionally) filter error'd jobs
vertical layout
will not allow expansion of jobs/datasets easily
@@ -141,8 +138,6 @@
would need to update as jquery was expanding foreignObject/dom
*/
-
-
define( 'app', function(){
require([
'mvc/job/job-model',
@@ -151,18 +146,14 @@
], function( JOB, HISTORY, StructureView ){
var historyModel = new HISTORY.History( bootstrapped.history, bootstrapped.hdas );
-window.historymodel = history;
-window.jobs = bootstrapped.jobs;
+//window.historymodel = history;
+//window.jobs = bootstrapped.jobs;
var structure = new StructureView({
model : historyModel,
jobs : bootstrapped.jobs
});
- structure.$el.appendTo( 'body' );
- console.debug( 'structure visible?', structure.$el.is( ':visible' ) );
- structure.render();
-window.structure = structure;
-
+ structure.render().$el.appendTo( 'body' );
});
});
</script>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: History structure: (<root>/history/structure) horizontal layout of job/history DAG separated into components (a work in progress) and associated qunit tests; Job model and list-item; rudimentary js graph library and tests; Qunit: allow import of test-data; minor fixes
by commits-noreply@bitbucket.org 15 Oct '14
by commits-noreply@bitbucket.org 15 Oct '14
15 Oct '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/eacfa5a1a3f2/
Changeset: eacfa5a1a3f2
User: carlfeberhard
Date: 2014-10-15 17:11:30+00:00
Summary: History structure: (<root>/history/structure) horizontal layout of job/history DAG separated into components (a work in progress) and associated qunit tests; Job model and list-item; rudimentary js graph library and tests; Qunit: allow import of test-data; minor fixes
Affected #: 32 files
diff -r 9f5491972e72ee38e011d49e891303e47bca29d9 -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 client/galaxy/scripts/mvc/collection/collection-li.js
--- a/client/galaxy/scripts/mvc/collection/collection-li.js
+++ b/client/galaxy/scripts/mvc/collection/collection-li.js
@@ -100,8 +100,6 @@
/** add the DCE class to the list item */
className : ListItemView.prototype.className + " dataset-collection-element",
- /** jq fx speed for this view */
- fxSpeed : 'fast',
/** set up */
initialize : function( attributes ){
diff -r 9f5491972e72ee38e011d49e891303e47bca29d9 -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 client/galaxy/scripts/mvc/history/history-structure-view.js
--- /dev/null
+++ b/client/galaxy/scripts/mvc/history/history-structure-view.js
@@ -0,0 +1,409 @@
+define([
+ 'mvc/job/job-model',
+ 'mvc/job/job-li',
+ 'mvc/history/history-content-model',
+ 'mvc/history/job-dag',
+ 'mvc/base-mvc',
+ 'utils/localization',
+ 'libs/d3'
+], function( JOB, JOB_LI, HISTORY_CONTENT, JobDAG, BASE_MVC, _l ){
+// ============================================================================
+/* TODO:
+change component = this to something else
+
+*/
+// ============================================================================
+var HistoryStructureComponent = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({
+
+ //logger : console,
+
+ className : 'history-structure-component',
+
+ initialize : function( attributes ){
+ this.log( this + '(HistoryStructureComponent).initialize:', attributes );
+ this.component = attributes.component;
+
+ this._jobLiMap = {};
+ this._createJobModels();
+
+ this.layout = this._createLayout( attributes.layoutOptions );
+ },
+
+ _createJobModels : function(){
+ var view = this;
+ view.component.eachVertex( function( vertex ){
+ var jobJSON = vertex.data.job,
+ job = new JOB.Job( jobJSON );
+
+ // get the models of the outputs for this job from the history
+ var outputModels = _.map( job.get( 'outputs' ), function( output ){
+ //note: output is { src: 'hda/dataset_collection', id: <some id> }
+ // job output doesn't *quite* match up to normal typeId
+ var type = output.src === 'hda'? 'dataset' : 'dataset_collection',
+ typeId = HISTORY_CONTENT.typeIdStr( type, output.id );
+ return view.model.contents.get( typeId );
+ });
+ // set the collection (HistoryContents) for the job to that json (setting historyId for proper ajax urls)
+ job.outputCollection.reset( outputModels );
+ job.outputCollection.historyId = structure.model.id;
+ //this.debug( job.outputCollection );
+
+ // create the bbone view for the job (to be positioned later accrd. to the layout) and cache
+ var li = new JOB_LI.JobListItemView({ model: job, expanded: true });
+ //var li = new JOB_LI.JobListItemView({ model: job });
+ li.$el.appendTo( view.$el );
+ view._jobLiMap[ job.id ] = li;
+ });
+ return view.jobs;
+ },
+
+ layoutDefaults : {
+ paddingTop : 8,
+ paddingLeft : 20,
+ linkSpacing : 16,
+ jobHeight : 308,
+ jobWidthSpacing : 320,
+ linkAdjX : 4,
+ linkAdjY : 0
+ },
+
+ _createLayout : function( options ){
+ options = _.defaults( _.clone( options || {} ), this.layoutDefaults );
+ var view = this,
+ vertices = _.values( view.component.vertices ),
+ layout = _.extend( options, {
+ nodeMap : {},
+ links : [],
+ el : { width: 0, height: 0 },
+ svg : { width: 0, height: 0, top: 0, left: 0 }
+ });
+
+ vertices.forEach( function( v, j ){
+ var node = { name: v.name, x: 0, y: 0 };
+ layout.nodeMap[ v.name ] = node;
+ });
+
+ view.component.edges( function( e ){
+ var link = {
+ source: e.source,
+ target: e.target
+ };
+ layout.links.push( link );
+ });
+ //this.debug( JSON.stringify( layout, null, ' ' ) );
+ return layout;
+ },
+
+ _updateLayout : function(){
+ var view = this,
+ layout = view.layout;
+
+ layout.svg.height = layout.paddingTop + ( layout.linkSpacing * _.size( layout.nodeMap ) );
+ layout.el.height = layout.svg.height + layout.jobHeight;
+
+//TODO:?? can't we just alter the component v and e's directly?
+ // layout the job views putting jobWidthSpacing btwn each
+ var x = layout.paddingLeft,
+ y = layout.svg.height;
+ _.each( layout.nodeMap, function( node, jobId ){
+ //this.debug( node, jobId );
+ node.x = x;
+ node.y = y;
+ x += layout.jobWidthSpacing;
+ });
+ layout.el.width = layout.svg.width = Math.max( layout.el.width, x );
+
+ // layout the links - connecting each job by it's main coords (currently)
+//TODO: somehow adjust the svg height based on the largest distance the longest connection needs
+ layout.links.forEach( function( link ){
+ var source = layout.nodeMap[ link.source ],
+ target = layout.nodeMap[ link.target ];
+ link.x1 = source.x + layout.linkAdjX;
+ link.y1 = source.y + layout.linkAdjY;
+ link.x2 = target.x + layout.linkAdjX;
+ link.y2 = target.y + layout.linkAdjY;
+ });
+ //this.debug( JSON.stringify( layout, null, ' ' ) );
+ return this.layout;
+ },
+
+ render : function( options ){
+ this.log( this + '.renderComponent:', options );
+ var view = this;
+
+ view.component.eachVertex( function( v ){
+ //TODO:? liMap needed - can't we attach to vertex?
+ var li = view._jobLiMap[ v.name ];
+ if( !li.$el.is( ':visible' ) ){
+ li.render( 0 );
+ }
+ });
+
+ view._updateLayout();
+ // set up the display containers
+ view.$el
+ .width( view.layout.el.width )
+ .height( view.layout.el.height );
+ this.renderSVG();
+
+ // position the job views accrd. to the layout
+ view.component.eachVertex( function( v ){
+ //TODO:? liMap needed - can't we attach to vertex?
+ var li = view._jobLiMap[ v.name ],
+ position = view.layout.nodeMap[ li.model.id ];
+ console.debug( li.$el.is( ':visible' ) );
+ //this.debug( position );
+ li.$el.css({ top: position.y, left: position.x });
+ });
+ return this;
+ },
+
+ renderSVG : function(){
+ var view = this,
+ layout = view.layout;
+
+ var svg = d3.select( this.el ).select( 'svg' );
+ if( svg.empty() ){
+ svg = d3.select( this.el ).append( 'svg' );
+ }
+
+ svg
+ .attr( 'width', layout.svg.width )
+ .attr( 'height', layout.svg.height );
+
+ function highlightConnect( d ){
+ d3.select( this ).classed( 'highlighted', true );
+ view._jobLiMap[ d.source ].$el.addClass( 'highlighted' );
+ view._jobLiMap[ d.target ].$el.addClass( 'highlighted' );
+ }
+
+ function unhighlightConnect( d ){
+ d3.select( this ).classed( 'highlighted', false );
+ view._jobLiMap[ d.source ].$el.removeClass( 'highlighted' );
+ view._jobLiMap[ d.target ].$el.removeClass( 'highlighted' );
+ }
+
+ var connections = svg.selectAll( '.connection' )
+ .data( layout.links );
+
+ connections
+ .enter().append( 'path' )
+ .attr( 'class', 'connection' )
+ .attr( 'id', function( d ){ return d.source + '-' + d.target; })
+ .on( 'mouseover', highlightConnect )
+ .on( 'mouseout', unhighlightConnect );
+
+ connections.transition()
+ .attr( 'd', function( d ){ return view._connectionPath( d ); });
+
+//TODO: ? can we use tick here to update the links?
+
+ return svg.node();
+ },
+
+ _connectionPath : function( d ){
+ var CURVE_X = 0,
+ controlY = ( ( d.x2 - d.x1 ) / this.layout.svg.width ) * this.layout.svg.height;
+ return [
+ 'M', d.x1, ',', d.y1, ' ',
+ 'C',
+ d.x1 + CURVE_X, ',', d.y1 - controlY, ' ',
+ d.x2 - CURVE_X, ',', d.y2 - controlY, ' ',
+ d.x2, ',', d.y2
+ ].join( '' );
+ },
+
+ events : {
+ 'mouseover .job.list-item' : function( ev ){ this.highlightConnected( ev.currentTarget, true ); },
+ 'mouseout .job.list-item' : function( ev ){ this.highlightConnected( ev.currentTarget, false ); }
+ },
+
+ highlightConnected : function( jobElement, highlight ){
+ highlight = highlight !== undefined? highlight : true;
+
+ var view = this,
+ component = view.component,
+ jobClassFn = highlight? jQuery.prototype.addClass : jQuery.prototype.removeClass,
+ connectionClass = highlight? 'connection highlighted' : 'connection';
+
+ //console.debug( 'mouseover', this );
+ var $jobs = jobClassFn.call( $( jobElement ), 'highlighted' ),
+ id = $jobs.attr( 'id' ).replace( 'job-', '' );
+
+ // immed. ancestors
+ component.edges({ target: id }).forEach( function( edge ){
+ jobClassFn.call( view.$( '#job-' + edge.source ), 'highlighted' );
+ view.$( '#' + edge.source + '-' + id ).attr( 'class', connectionClass );
+ });
+ // descendants
+ component.vertices[ id ].eachEdge( function( edge ){
+ jobClassFn.call( view.$( '#job-' + edge.target ), 'highlighted' );
+ view.$( '#' + id + '-' + edge.target ).attr( 'class', connectionClass );
+ });
+ },
+
+ toString : function(){
+ return 'HistoryStructureComponent(' + this.model.id + ')';
+ }
+});
+
+
+// ============================================================================
+var VerticalHistoryStructureComponent = HistoryStructureComponent.extend({
+
+ logger : console,
+
+ className : HistoryStructureComponent.prototype.className + ' vertical',
+
+ layoutDefaults : {
+ paddingTop : 8,
+ paddingLeft : 20,
+ linkSpacing : 16,
+ jobWidth : 308,
+ jobHeight : 308,
+ initialSpacing : 64,
+ jobSpacing : 16,
+ linkAdjX : 0,
+ linkAdjY : 4
+ },
+
+//TODO: how can we use the dom height of the job li's - they're not visible when this is called?
+ _updateLayout : function(){
+ var view = this,
+ layout = view.layout;
+ //this.info( this.cid, '_updateLayout' )
+
+ layout.svg.width = layout.paddingLeft + ( layout.linkSpacing * _.size( layout.nodeMap ) );
+ layout.el.width = layout.svg.width + layout.jobWidth;
+
+ // reset height - we'll get the max Y below to assign to it
+ layout.el.height = 0;
+
+//TODO:?? can't we just alter the component v and e's directly?
+ var x = layout.svg.width,
+ y = layout.paddingTop;
+ _.each( layout.nodeMap, function( node, jobId ){
+ //this.debug( node, jobId );
+ node.x = x;
+ node.y = y;
+ var li = view._jobLiMap[ jobId ];
+ if( li.$el.is( ':visible' ) ){
+ console.debug( 'li is visible, adj. by:', li.$el.height() );
+ y += li.$el.height() + layout.jobSpacing;
+ } else {
+ y += layout.initialSpacing + layout.jobSpacing;
+ }
+ });
+ layout.el.height = layout.svg.height = Math.max( layout.el.height, y );
+
+ // layout the links - connecting each job by it's main coords (currently)
+ layout.links.forEach( function( link ){
+ var source = layout.nodeMap[ link.source ],
+ target = layout.nodeMap[ link.target ];
+ link.x1 = source.x + layout.linkAdjX;
+ link.y1 = source.y + layout.linkAdjY;
+ link.x2 = target.x + layout.linkAdjX;
+ link.y2 = target.y + layout.linkAdjY;
+ view.debug( 'link:', link.x1, link.y1, link.x2, link.y2, link );
+ });
+ //this.debug( JSON.stringify( layout, null, ' ' ) );
+ view.debug( 'el:', layout.el );
+ view.debug( 'svg:', layout.svg );
+ return layout;
+ },
+
+ _connectionPath : function( d ){
+ var CURVE_Y = 0,
+ controlX = ( ( d.y2 - d.y1 ) / this.layout.svg.height ) * this.layout.svg.width;
+ return [
+ 'M', d.x1, ',', d.y1, ' ',
+ 'C',
+ d.x1 - controlX, ',', d.y1 + CURVE_Y, ' ',
+ d.x2 - controlX, ',', d.y2 - CURVE_Y, ' ',
+ d.x2, ',', d.y2
+ ].join( '' );
+ },
+
+ toString : function(){
+ return 'VerticalHistoryStructureComponent(' + this.model.id + ')';
+ }
+});
+
+
+// ============================================================================
+var HistoryStructureView = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({
+
+ //logger : console,
+
+ className : 'history-structure',
+
+ initialize : function( attributes ){
+ this.log( this + '(HistoryStructureView).initialize:', attributes, this.model );
+//TODO: to model
+ this.jobs = attributes.jobs;
+ this._createDAG();
+ },
+
+ _createDAG : function(){
+ this.dag = new JobDAG({
+ historyContents : this.model.contents.toJSON(),
+ jobs : this.jobs,
+ excludeSetMetadata : true,
+ excludeErroredJobs : true
+ });
+window.dag = this.dag;
+ this.log( this + '.dag:', this.dag );
+
+ this._createComponents();
+ },
+
+ _createComponents : function(){
+ this.log( this + '._createComponents' );
+ var structure = this;
+window.structure = structure;
+
+ structure.componentViews = structure.dag.weakComponentGraphArray().map( function( componentGraph ){
+ return structure._createComponent( componentGraph );
+ });
+ },
+
+ _createComponent : function( component ){
+ this.log( this + '._createComponent:', component );
+ return new HistoryStructureComponent({
+ //return new VerticalHistoryStructureComponent({
+ model : this.model,
+ component : component
+ });
+ },
+
+ render : function( options ){
+ this.log( this + '.render:', options );
+ var structure = this;
+
+ structure.$el.html([
+ '<div class="controls"></div>',
+ '<div class="components"></div>'
+ ].join( '' ));
+
+ structure.componentViews.forEach( function( component ){
+ component.render().$el.appendTo( structure.$components() );
+ });
+ return structure;
+ },
+
+ $components : function(){
+ return this.$( '.components' );
+ },
+
+ toString : function(){
+ return 'HistoryStructureView(' + ')';
+ }
+});
+
+
+// ============================================================================
+ //return {
+ // HistoryStructureView : HistoryStructureView
+ //};
+ return HistoryStructureView;
+});
diff -r 9f5491972e72ee38e011d49e891303e47bca29d9 -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 client/galaxy/scripts/mvc/history/job-dag.js
--- /dev/null
+++ b/client/galaxy/scripts/mvc/history/job-dag.js
@@ -0,0 +1,204 @@
+define([
+ 'utils/graph',
+ 'utils/add-logging'
+],function( GRAPH, addLogging ){
+// ============================================================================
+var _super = GRAPH.Graph;
+var JobDAG = function( options ){
+ var self = this;
+ //this.logger = console;
+
+ // instance vars
+//TODO: needed?
+ self._historyContentsMap = {};
+ self.filters = [];
+
+ self._idMap = {};
+ self.noInputJobs = [];
+ self.noOutputJobs = [];
+
+//TODO: save these?
+ self.filteredSetMetadata = [];
+ self.filteredErroredJobs = [];
+
+ _super.call( self, true, null, options );
+};
+JobDAG.prototype = new GRAPH.Graph();
+JobDAG.prototype.constructor = JobDAG;
+addLogging( JobDAG );
+
+// ----------------------------------------------------------------------------
+JobDAG.prototype.init = function _init( options ){
+ options = options || {};
+ _super.prototype.init.call( this, options );
+
+ var self = this,
+ historyContentsJSON = options.historyContents || [];
+ jobsJSON = options.jobs || [];
+
+ historyContentsJSON.forEach( function( content, i ){
+ self._historyContentsMap[ content.id ] = _.clone( content );
+ });
+
+ self.options = _.defaults( _.omit( options, 'historyContents', 'jobs' ), {
+ excludeSetMetadata : false
+ });
+ self.filters = self._initFilters();
+
+//TODO: O( 3N )
+ self.preprocessJobs( _.clone( jobsJSON ) );
+ self.createGraph();
+
+ return self;
+};
+
+JobDAG.prototype._initFilters = function __initFilters(){
+ var self = this,
+ filters = [];
+
+ if( self.options.excludeSetMetadata ){
+ self.filteredSetMetadata = [];
+ filters.push( function filterSetMetadata( jobData ){
+ if( jobData.job.tool_id !== '__SET_METADATA__' ){ return true; }
+ self.filteredSetMetadata.push( jobData.job.id );
+ return false;
+ });
+ }
+
+ if( self.options.excludeErroredJobs ){
+ self.filteredErroredJobs = [];
+ filters.push( function filterErrored( jobData ){
+ if( jobData.job.state !== 'error' ){ return true; }
+ self.filteredErroredJobs.push( jobData.job.id );
+ return false;
+ });
+ }
+
+ // all outputs deleted
+ // all outputs hidden
+
+ if( _.isArray( self.options.filters ) ){
+ filters = filters.concat( self.options.filters );
+ }
+ self.debug( 'filters len:', filters.length );
+ return filters;
+};
+
+JobDAG.prototype.preprocessJobs = function _preprocessJobs( jobs ){
+ this.info( 'processing jobs' );
+
+ var self = this;
+//TODO:? sorting neccessary?
+ self.sort( jobs ).forEach( function( job, i ){
+ var jobData = self.preprocessJob( job, i );
+ if( jobData ){
+ self._idMap[ job.id ] = jobData;
+ }
+ });
+ return self;
+};
+
+JobDAG.prototype.sort = function _sort( jobs ){
+ function cmpCreate( a, b ){
+ if( a.update_time > b.update_time ){ return 1; }
+ if( a.update_time < b.update_time ){ return -1; }
+ return 0;
+ }
+ return jobs.sort( cmpCreate );
+};
+
+JobDAG.prototype.preprocessJob = function _preprocessJob( job, index ){
+ //this.info( 'preprocessJob', job, index );
+ var self = this,
+ jobData = { index: index, job: job };
+
+ jobData.inputs = self.datasetMapToIdArray( job.inputs, function( dataset, nameInJob ){
+ //TODO:? store output name in self._datasets[ output.id ] from creating job?
+ });
+ if( jobData.inputs.length === 0 ){
+ self.noInputJobs.push( job.id );
+ }
+ jobData.outputs = self.datasetMapToIdArray( job.outputs, function( dataset, nameInJob ){
+
+ });
+ if( jobData.outputs.length === 0 ){
+ self.noOutputJobs.push( job.id );
+ }
+
+ //self.debug( JSON.stringify( jobData, null, ' ' ) );
+ // apply filters after processing job allowing access to the additional data above in the filters
+ for( var i=0; i<self.filters.length; i++ ){
+ if( !self.filters[i].call( self, jobData ) ){
+ self.debug( 'job', job.id, ' has been filtered out by function:\n', self.filters[i] );
+ return null;
+ }
+ }
+
+ //self.info( 'preprocessJob returning', jobData );
+ return jobData;
+};
+
+JobDAG.prototype.datasetMapToIdArray = function _datasetMapToIdArray( datasetMap, processFn ){
+ var self = this;
+ return _.map( datasetMap, function( dataset, nameInJob ){
+ if( !dataset.id ){
+ throw new Error( 'No id on datasetMap: ', JSON.stringify( dataset ) );
+ }
+ if( !dataset.src || dataset.src !== 'hda' ){
+ throw new Error( 'Bad src on datasetMap: ', JSON.stringify( dataset ) );
+ }
+ processFn.call( self, dataset, nameInJob );
+ return dataset.id;
+ });
+};
+
+JobDAG.prototype.createGraph = function _createGraph(){
+ var self = this;
+ self.debug( 'connections:' );
+
+ _.each( self._idMap, function( sourceJobData, sourceJobId ){
+ self.debug( '\t', sourceJobId, sourceJobData );
+ self.createVertex( sourceJobId, sourceJobData );
+
+ sourceJobData.usedIn = [];
+ _.each( sourceJobData.outputs, function( outputDatasetId ){
+//TODO: O(n^2)
+ _.each( self._idMap, function( targetJobData, targetJobId ){
+ if( targetJobData === sourceJobData ){ return; }
+
+ if( targetJobData.inputs.indexOf( outputDatasetId ) !== -1 ){
+ self.info( '\t\t\t found connection: ', sourceJobId, targetJobId );
+ sourceJobData.usedIn.push({ job: targetJobId, output: outputDatasetId });
+
+ self.createVertex( targetJobId, targetJobData );
+
+ self.createEdge( sourceJobId, targetJobId, self.directed, {
+ dataset : outputDatasetId
+ });
+ }
+ });
+ });
+ });
+ self.debug( 'job data: ', JSON.stringify( self._idMap, null, ' ' ) );
+ return self;
+};
+
+Graph.prototype.weakComponentGraphArray = function(){
+ return this.weakComponents().map( function( component ){
+//TODO: this seems to belong above (in sort) - why isn't it preserved?
+ component.vertices.sort( function cmpCreate( a, b ){
+ if( a.data.job.update_time > b.data.job.update_time ){ return 1; }
+ if( a.data.job.update_time < b.data.job.update_time ){ return -1; }
+ return 0;
+ });
+ return new Graph( this.directed, component );
+ });
+};
+
+
+// ============================================================================
+ return JobDAG;
+ //return {
+ // jobDAG : jobDAG
+ //};
+});
diff -r 9f5491972e72ee38e011d49e891303e47bca29d9 -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 client/galaxy/scripts/mvc/job/job-li.js
--- /dev/null
+++ b/client/galaxy/scripts/mvc/job/job-li.js
@@ -0,0 +1,129 @@
+define([
+ 'mvc/list/list-item',
+ 'mvc/dataset/dataset-list',
+ "mvc/base-mvc",
+ "utils/localization"
+], function( LIST_ITEM, DATASET_LIST, BASE_MVC, _l ){
+//==============================================================================
+var _super = LIST_ITEM.FoldoutListItemView;
+/** @class
+ */
+var JobListItemView = _super.extend(/** @lends JobListItemView.prototype */{
+
+ /** logger used to record this.log messages, commonly set to console */
+ //logger : console,
+
+
+ className : _super.prototype.className + " job",
+ id : function(){
+ return [ 'job', this.model.get( 'id' ) ].join( '-' );
+ },
+
+ foldoutPanelClass : DATASET_LIST.DatasetList,
+
+ /** Set up: instance vars, options, and event handlers */
+ initialize : function( attributes ){
+ if( attributes.logger ){ this.logger = this.model.logger = attributes.logger; }
+ this.log( this + '.initialize:', attributes );
+ _super.prototype.initialize.call( this, attributes );
+
+ /** where should pages from links be displayed? (default to new tab/window) */
+ this.linkTarget = attributes.linkTarget || '_blank';
+ this._setUpListeners();
+ },
+
+ /** In this override, add the state as a class for use with state-based CSS */
+ _swapNewRender : function( $newRender ){
+ _super.prototype._swapNewRender.call( this, $newRender );
+ if( this.model.has( 'state' ) ){
+ this.$el.addClass( 'state-' + this.model.get( 'state' ) );
+ }
+ return this.$el;
+ },
+
+ /** Stub to return proper foldout panel options */
+ _getFoldoutPanelOptions : function(){
+ var options = _super.prototype._getFoldoutPanelOptions.call( this );
+ return _.extend( options, {
+ collection : this.model.outputCollection,
+ selecting : false
+ });
+ },
+
+ // ........................................................................ misc
+ /** String representation */
+ toString : function(){
+ return 'JobListItemView(' + this.model + ')';
+ }
+});
+
+// ............................................................................ TEMPLATES
+/** underscore templates */
+JobListItemView.prototype.templates = (function(){
+//TODO: move to require text! plugin
+
+ var elTemplate = BASE_MVC.wrapTemplate([
+ '<div class="list-element">',
+ '<div class="id"><%= model.id %></div>',
+ // errors, messages, etc.
+ '<div class="warnings"></div>',
+
+ // multi-select checkbox
+ '<div class="selector">',
+ '<span class="fa fa-2x fa-square-o"></span>',
+ '</div>',
+ // space for title bar buttons - gen. floated to the right
+ '<div class="primary-actions"></div>',
+ '<div class="title-bar"></div>',
+
+ // expandable area for more details
+ '<div class="details"></div>',
+ '</div>'
+ ]);
+
+ var titleBarTemplate = BASE_MVC.wrapTemplate([
+ // adding a tabindex here allows focusing the title bar and the use of keydown to expand the dataset display
+ '<div class="title-bar clear" tabindex="0">',
+ //'<span class="state-icon"></span>',
+ '<div class="title">',
+ '<span class="name"><%- job.tool_id %></span>',
+ '</div>',
+ '<div class="subtitle"></div>',
+ '</div>'
+ ], 'job' );
+
+ //var subtitleTemplate = BASE_MVC.wrapTemplate([
+ // // override this
+ // '<div class="subtitle">',
+ // _l( 'Created' ), ': <%= new Date( job.create_time ).toString() %>, ',
+ // _l( 'Updated' ), ': <%= new Date( job.update_time ).toString() %>',
+ // '</div>'
+ //], 'job' );
+ //
+ //var detailsTemplate = BASE_MVC.wrapTemplate([
+ // '<div class="details">',
+ // '<div class="params">',
+ // '<% _.each( job.params, function( param, paramName ){ %>',
+ // '<div class="param">',
+ // '<label class="prompt"><%= paramName %></label>',
+ // '<span class="value"><%= param %></span>',
+ // '</div>',
+ // '<% }) %>',
+ // '</div>',
+ // '</div>'
+ //], 'job' );
+
+ return _.extend( {}, _super.prototype.templates, {
+ //el : elTemplate,
+ titleBar : titleBarTemplate,
+ //subtitle : subtitleTemplate,
+ //details : detailsTemplate
+ });
+}());
+
+
+//=============================================================================
+ return {
+ JobListItemView : JobListItemView
+ };
+});
diff -r 9f5491972e72ee38e011d49e891303e47bca29d9 -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 client/galaxy/scripts/mvc/job/job-model.js
--- /dev/null
+++ b/client/galaxy/scripts/mvc/job/job-model.js
@@ -0,0 +1,202 @@
+define([
+ "mvc/history/history-contents",
+ "mvc/dataset/states",
+ "utils/ajax-queue",
+ "mvc/base-mvc",
+ "utils/localization"
+], function( HISTORY_CONTENTS, STATES, AJAX_QUEUE, BASE_MVC, _l ){
+//==============================================================================
+var searchableMixin = BASE_MVC.SearchableModelMixin;
+/** @class
+ */
+var Job = Backbone.Model.extend( BASE_MVC.LoggableMixin ).extend(
+ BASE_MVC.mixin( searchableMixin, /** @lends Job.prototype */{
+
+ /** logger used to record this.log messages, commonly set to console */
+ //logger : console,
+
+ /** default attributes for a model */
+ defaults : {
+ model_class : 'Job',
+
+ tool : null,
+ exit_code : null,
+
+ inputs : {},
+ outputs : {},
+ params : {},
+
+ create_time : null,
+ update_time : null,
+ state : STATES.NEW
+ },
+
+ /** instance vars and listeners */
+ initialize : function( attributes, options ){
+ this.debug( this + '(Job).initialize', attributes, options );
+ this.outputCollection = attributes.outputCollection || new HISTORY_CONTENTS.HistoryContents([]);
+ this._setUpListeners();
+ },
+
+ /** set up any event listeners
+ * event: state:ready fired when this DA moves into/is already in a ready state
+ */
+ _setUpListeners : function(){
+ // if the state has changed and the new state is a ready state, fire an event
+ this.on( 'change:state', function( currModel, newState ){
+ this.log( this + ' has changed state:', currModel, newState );
+ if( this.inReadyState() ){
+ this.trigger( 'state:ready', currModel, newState, this.previous( 'state' ) );
+ }
+ });
+ },
+
+ // ........................................................................ common queries
+ /** Is this job in a 'ready' state; where 'Ready' states are states where no
+ * processing is left to do on the server.
+ */
+ inReadyState : function(){
+ return _.contains( STATES.READY_STATES, this.get( 'state' ) );
+ },
+
+ /** Does this model already contain detailed data (as opposed to just summary level data)? */
+ hasDetails : function(){
+ //?? this may not be reliable
+ return _.isEmpty( this.get( 'outputs' ) );
+ },
+
+ // ........................................................................ ajax
+ /** root api url */
+ urlRoot : (( window.galaxy_config && galaxy_config.root )?( galaxy_config.root ):( '/' )) + 'api/jobs',
+ //url : function(){ return this.urlRoot; },
+
+ // ........................................................................ searching
+ // see base-mvc, SearchableModelMixin
+ /** what attributes of an Job will be used in a text search */
+ searchAttributes : [
+ 'tool'
+ ],
+
+ // ........................................................................ misc
+ /** String representation */
+ toString : function(){
+ return [ 'Job(', this.get( 'id' ), ':', this.get( 'tool_id' ), ')' ].join( '' );
+ }
+}));
+
+
+//==============================================================================
+/** @class Backbone collection for Jobs.
+ */
+var JobCollection = Backbone.Collection.extend( BASE_MVC.LoggableMixin ).extend(
+/** @lends JobCollection.prototype */{
+ model : Job,
+
+ /** logger used to record this.log messages, commonly set to console */
+ //logger : console,
+
+ /** root api url */
+ urlRoot : (( window.galaxy_config && galaxy_config.root )?( galaxy_config.root ):( '/' )) + 'api/jobs',
+ url : function(){ return this.urlRoot; },
+
+ intialize : function( models, options ){
+ console.debug( models, options );
+ },
+
+ // ........................................................................ common queries
+ /** Get the ids of every item in this collection
+ * @returns array of encoded ids
+ */
+ ids : function(){
+ return this.map( function( item ){ return item.get( 'id' ); });
+ },
+
+ /** Get jobs that are not ready
+ * @returns array of content models
+ */
+ notReady : function(){
+ return this.filter( function( job ){
+ return !job.inReadyState();
+ });
+ },
+
+ /** return true if any jobs don't have details */
+ haveDetails : function(){
+ return this.all( function( job ){ return job.hasDetails(); });
+ },
+
+ // ........................................................................ ajax
+ queueDetailFetching : function(){
+ var collection = this,
+ queue = new AJAX_QUEUE.AjaxQueue( this.map( function( job ){
+ return function(){
+ return job.fetch({ silent: true });
+ };
+ }));
+ queue.done( function(){
+ collection.trigger( 'details-loaded' );
+ });
+ return queue;
+ },
+
+ //toDAG : function(){
+ // return new JobDAG( this.toJSON() );
+ //},
+
+ // ........................................................................ sorting/filtering
+ /** return a new collection of jobs whose attributes contain the substring matchesWhat */
+ matches : function( matchesWhat ){
+ return this.filter( function( job ){
+ return job.matches( matchesWhat );
+ });
+ },
+
+ // ........................................................................ misc
+ /** override to get a correct/smarter merge when incoming data is partial */
+ set : function( models, options ){
+ // arrrrrrrrrrrrrrrrrg...
+ // (e.g. stupid backbone)
+ // w/o this partial models from the server will fill in missing data with model defaults
+ // and overwrite existing data on the client
+ // see Backbone.Collection.set and _prepareModel
+ var collection = this;
+ models = _.map( models, function( model ){
+ if( !collection.get( model.id ) ){ return model; }
+
+ // merge the models _BEFORE_ calling the superclass version
+ var merged = existing.toJSON();
+ _.extend( merged, model );
+ return merged;
+ });
+ // now call superclass when the data is filled
+ Backbone.Collection.prototype.set.call( this, models, options );
+ },
+
+ /** String representation. */
+ toString : function(){
+ return ([ 'JobCollection(', this.length, ')' ].join( '' ));
+ }
+
+//----------------------------------------------------------------------------- class vars
+}, {
+
+ fromHistory : function( historyId ){
+ console.debug( this );
+ var Collection = this,
+ collection = new Collection([]);
+ collection.fetch({ data: { history_id: historyId }})
+ .done( function(){
+ window.queue = collection.queueDetailFetching();
+
+ });
+ return collection;
+ }
+});
+
+
+//=============================================================================
+ return {
+ Job : Job,
+ JobCollection : JobCollection
+ };
+});
diff -r 9f5491972e72ee38e011d49e891303e47bca29d9 -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 client/galaxy/scripts/mvc/list/list-item.js
--- a/client/galaxy/scripts/mvc/list/list-item.js
+++ b/client/galaxy/scripts/mvc/list/list-item.js
@@ -16,7 +16,7 @@
initialize : function( attributes ){
/** are the details of this view expanded/shown or not? */
this.expanded = attributes.expanded || false;
- //this.log( '\t expanded:', this.expanded );
+ this.log( '\t expanded:', this.expanded );
this.fxSpeed = attributes.fxSpeed || this.fxSpeed;
},
@@ -70,7 +70,9 @@
/** empty out the current el, move the $newRender's children in */
_swapNewRender : function( $newRender ){
- return this.$el.empty().attr( 'class', this.className ).append( $newRender.children() );
+ return this.$el.empty()
+ .attr( 'class', _.isFunction( this.className )? this.className(): this.className )
+ .append( $newRender.children() );
},
/** set up js behaviors, event handlers for elements within the given container
@@ -204,7 +206,7 @@
$newRender.children( '.warnings' ).replaceWith( this._renderWarnings() );
$newRender.children( '.title-bar' ).replaceWith( this._renderTitleBar() );
$newRender.children( '.primary-actions' ).append( this._renderPrimaryActions() );
- $newRender.find( '.title-bar .subtitle' ).replaceWith( this._renderSubtitle() );
+ $newRender.find( '> .title-bar .subtitle' ).replaceWith( this._renderSubtitle() );
return $newRender;
},
diff -r 9f5491972e72ee38e011d49e891303e47bca29d9 -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 client/galaxy/scripts/utils/graph.js
--- /dev/null
+++ b/client/galaxy/scripts/utils/graph.js
@@ -0,0 +1,573 @@
+define([
+],function(){
+/* ============================================================================
+TODO:
+ edges can't retain data
+
+============================================================================ */
+//TODO: go ahead and move to underscore...
+function each( d, fn ){
+ for( var k in d ){
+ if( d.hasOwnProperty( k ) ){
+ fn( d[ k ], k, d );
+ }
+ }
+}
+
+function extend( d, d2 ){
+ for( var k in d2 ){
+ if( d2.hasOwnProperty( k ) ){
+ d[ k ] = d2[ k ];
+ }
+ }
+ return d;
+}
+
+function matches( d, d2 ){
+ for( var k in d2 ){
+ if( d2.hasOwnProperty( k ) ){
+ if( !d.hasOwnProperty( k ) || d[ k ] !== d2[ k ] ){
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+function iterate( obj, propsOrFn ){
+ var fn = typeof propsOrFn === 'function'? propsOrFn : undefined,
+ props = typeof propsOrFn === 'object'? propsOrFn : undefined,
+ returned = [],
+ index = 0;
+ for( var key in obj ){
+ if( obj.hasOwnProperty( key ) ){
+ var value = obj[ key ];
+ if( fn ){
+ returned.push( fn.call( value, value, key, index ) );
+ } else if( props ){
+//TODO: break out to sep?
+ if( typeof value === 'object' && matches( value, props ) ){
+ returned.push( value );
+ }
+ } else {
+ returned.push( value );
+ }
+ index += 1;
+ }
+ }
+ return returned;
+}
+
+
+// ============================================================================
+function Edge( source, target, data ){
+ var self = this;
+ self.source = source !== undefined? source : null;
+ self.target = target !== undefined? target : null;
+ self.data = data || null;
+ //if( typeof data === 'object' ){
+ // extend( self, data );
+ //}
+ return self;
+}
+Edge.prototype.toString = function(){
+ return this.source + '->' + this.target;
+};
+
+Edge.prototype.toJSON = function(){
+ //TODO: this is safe in most browsers (fns will be stripped) - alter tests to incorporate this in order to pass data
+ //return this;
+ var json = {
+ source : this.source,
+ target : this.target
+ };
+ if( this.data ){
+ json.data = this.data;
+ }
+ return json;
+};
+
+// ============================================================================
+function Vertex( name, data ){
+ var self = this;
+ self.name = name !== undefined? name : '(unnamed)';
+ self.data = data || null;
+ self.edges = {};
+ self.degree = 0;
+ return self;
+}
+window.Vertex = Vertex;
+Vertex.prototype.toString = function(){
+ return 'Vertex(' + this.name + ')';
+};
+
+//TODO: better name w no collision for either this.eachEdge or this.edges
+Vertex.prototype.eachEdge = function( propsOrFn ){
+ return iterate( this.edges, propsOrFn );
+};
+
+Vertex.prototype.toJSON = function(){
+ //return this;
+ return {
+ name : this.name,
+ data : this.data
+ };
+};
+
+
+// ============================================================================
+var GraphSearch = function( graph, processFns ){
+ var self = this;
+ self.graph = graph;
+
+ self.processFns = processFns || {
+ vertexEarly : function( vertex, search ){
+ //console.debug( 'processing vertex:', vertex.name, vertex );
+ },
+ edge : function( from, edge, search ){
+ //console.debug( this, 'edge:', from, edge, search );
+ },
+ vertexLate : function( vertex, search ){
+ //console.debug( this, 'vertexLate:', vertex, search );
+ }
+ };
+
+ self._cache = {};
+ return self;
+};
+
+GraphSearch.prototype.search = function _search( start ){
+ var self = this;
+ if( start in self._cache ){ return self._cache[ start ]; }
+ if( !( start instanceof Vertex ) ){ start = self.graph.vertices[ start ]; }
+ return ( self._cache[ start.name ] = self._search( start ) );
+};
+
+GraphSearch.prototype._searchTree = function __searchTree( search ){
+ var self = this;
+ return new Graph( true, {
+ edges: search.edges,
+ vertices: Object.keys( search.discovered ).map( function( key ){
+ return self.graph.vertices[ key ].toJSON();
+ })
+ });
+};
+
+GraphSearch.prototype.searchTree = function _searchTree( start ){
+ return this._searchTree( this.search( start ) );
+};
+
+// ============================================================================
+var BreadthFirstSearch = function( graph, processFns ){
+ var self = this;
+ GraphSearch.call( this, graph, processFns );
+ return self;
+};
+BreadthFirstSearch.prototype = new GraphSearch();
+BreadthFirstSearch.prototype.constructor = BreadthFirstSearch;
+
+BreadthFirstSearch.prototype._search = function __search( start, search ){
+ search = search || {
+ discovered : {},
+ //parents : {},
+ edges : []
+ };
+
+ var self = this,
+ queue = [];
+
+ function discoverAdjacent( adj, edge ){
+ var source = this;
+ if( self.processFns.edge ){ self.processFns.edge.call( self, source, edge, search ); }
+ if( !search.discovered[ adj.name ] ){
+ //console.debug( '\t\t\t', adj.name, 'is undiscovered:', search.discovered[ adj.name ] );
+ search.discovered[ adj.name ] = true;
+ //search.parents[ adj.name ] = source;
+ search.edges.push({ source: source.name, target: adj.name });
+ //console.debug( '\t\t\t queuing undiscovered: ', adj );
+ queue.push( adj );
+ }
+ }
+
+ //console.debug( 'BFS starting. start:', start );
+ search.discovered[ start.name ] = true;
+ queue.push( start );
+ while( queue.length ){
+ var vertex = queue.shift();
+ //console.debug( '\t Queue is shifting. Current:', vertex, 'queue:', queue );
+ if( self.processFns.vertexEarly ){ self.processFns.vertexEarly.call( self, vertex, search ); }
+ self.graph.eachAdjacent( vertex, discoverAdjacent );
+ if( self.processFns.vertexLate ){ self.processFns.vertexLate.call( self, vertex, search ); }
+ }
+ //console.debug( 'search.edges:', JSON.stringify( search.edges ) );
+ return search;
+};
+
+
+// ============================================================================
+var DepthFirstSearch = function( graph, processFns ){
+ var self = this;
+ GraphSearch.call( this, graph, processFns );
+ return self;
+};
+DepthFirstSearch.prototype = new GraphSearch();
+DepthFirstSearch.prototype.constructor = DepthFirstSearch;
+
+DepthFirstSearch.prototype._search = function( start, search ){
+ //console.debug( 'depthFirstSearch:', start );
+ search = search || {
+ discovered : {},
+ //parents : {},
+ edges : [],
+ entryTimes : {},
+ exitTimes : {}
+ };
+ var self = this,
+ time = 0;
+
+ // discover verts adjacent to the source (this):
+ // processing each edge, saving the edge to the tree, and caching the reverse path with parents
+ function discoverAdjacentVertices( adjacent, edge ){
+ //console.debug( '\t\t adjacent:', adjacent, 'edge:', edge );
+ var sourceVertex = this;
+ if( self.processFns.edge ){ self.processFns.edge.call( self, sourceVertex, edge, search ); }
+ if( !search.discovered[ adjacent.name ] ){
+ //search.parents[ adjacent.name ] = sourceVertex;
+ search.edges.push({ source: sourceVertex.name, target: adjacent.name });
+ recurse( adjacent );
+ }
+ }
+
+ // use function stack for DFS stack process verts, times, and discover adjacent verts (recursing into them)
+ function recurse( vertex ){
+ //console.debug( '\t recursing into: ', vertex );
+ search.discovered[ vertex.name ] = true;
+ if( self.processFns.vertexEarly ){ self.processFns.vertexEarly.call( self, vertex, search ); }
+ search.entryTimes[ vertex.name ] = time++;
+
+ self.graph.eachAdjacent( vertex, discoverAdjacentVertices );
+
+ if( self.processFns.vertexLate ){ self.processFns.vertexLate.call( self, vertex, search ); }
+ search.exitTimes[ vertex.name ] = time++;
+ }
+ // begin recursion with the desired start
+ recurse( start );
+
+ return search;
+};
+
+
+// ============================================================================
+function Graph( directed, data, options ){
+//TODO: move directed to options
+ this.directed = directed || false;
+ return this.init( options ).read( data );
+}
+window.Graph = Graph;
+
+
+Graph.prototype.init = function( options ){
+ options = options || {};
+ var self = this;
+
+ self.allowReflexiveEdges = options.allowReflexiveEdges || false;
+
+ self.vertices = {};
+ self.numEdges = 0;
+ return self;
+};
+
+Graph.prototype.read = function( data ){
+ if( !data ){ return this; }
+ var self = this;
+ if( data.hasOwnProperty( 'nodes' ) ){ return self.readNodesAndLinks( data ); }
+ if( data.hasOwnProperty( 'vertices' ) ){ return self.readVerticesAndEdges( data ); }
+ return self;
+};
+
+//TODO: the next two could be combined
+Graph.prototype.readNodesAndLinks = function( data ){
+ if( !( data && data.hasOwnProperty( 'nodes' ) ) ){ return this; }
+ //console.debug( 'readNodesAndLinks:', data );
+ //console.debug( 'data:\n' + JSON.stringify( data, null, ' ' ) );
+ var self = this;
+ data.nodes.forEach( function( node ){
+ self.createVertex( node.name, node.data );
+ });
+ //console.debug( JSON.stringify( self.vertices, null, ' ' ) );
+
+ ( data.links || [] ).forEach( function( edge, i ){
+ var sourceName = data.nodes[ edge.source ].name,
+ targetName = data.nodes[ edge.target ].name;
+ self.createEdge( sourceName, targetName, self.directed );
+ });
+ //self.print();
+ //console.debug( JSON.stringify( self.toNodesAndLinks(), null, ' ' ) );
+ return self;
+};
+
+Graph.prototype.readVerticesAndEdges = function( data ){
+ if( !( data && data.hasOwnProperty( 'vertices' ) ) ){ return this; }
+ //console.debug( 'readVerticesAndEdges:', data );
+ //console.debug( 'data:\n' + JSON.stringify( data, null, ' ' ) );
+ var self = this;
+ data.vertices.forEach( function( node ){
+ self.createVertex( node.name, node.data );
+ });
+ //console.debug( JSON.stringify( self.vertices, null, ' ' ) );
+
+ ( data.edges || [] ).forEach( function( edge, i ){
+ self.createEdge( edge.source, edge.target, self.directed );
+ });
+ //self.print();
+ //console.debug( JSON.stringify( self.toNodesAndLinks(), null, ' ' ) );
+ return self;
+};
+
+Graph.prototype.createVertex = function( name, data ){
+ //console.debug( 'createVertex:', name, data );
+ if( this.vertices[ name ] ){ return this.vertices[ name ]; }
+ return ( this.vertices[ name ] = new Vertex( name, data ) );
+};
+
+Graph.prototype.createEdge = function( sourceName, targetName, directed, data ){
+ //note: allows multiple 'equivalent' edges (to/from same source/target)
+ //console.debug( 'createEdge:', source, target, directed );
+ var isReflexive = sourceName === targetName;
+ if( !this.allowReflexiveEdges && isReflexive ){ return null; }
+
+ sourceVertex = this.vertices[ sourceName ];
+ targetVertex = this.vertices[ targetName ];
+ //note: silently ignores edges from/to unknown vertices
+ if( !( sourceVertex && targetVertex ) ){ return null; }
+
+//TODO: prob. move to vertex
+ var self = this,
+ edge = new Edge( sourceName, targetName, data );
+ sourceVertex.edges[ targetName ] = edge;
+ sourceVertex.degree += 1;
+ self.numEdges += 1;
+
+ //TODO:! don't like having duplicate edges for non-directed graphs
+ // mirror edges (reversing source and target) in non-directed graphs
+ // but only if not reflexive
+ if( !isReflexive && !directed ){
+ // flip directed to prevent recursion loop
+ self.createEdge( targetName, sourceName, true );
+ }
+
+ return edge;
+};
+
+Graph.prototype.edges = function( propsOrFn ){
+ return Array.prototype.concat.apply( [], this.eachVertex( function( vertex ){
+ return vertex.eachEdge( propsOrFn );
+ }));
+};
+
+Graph.prototype.eachVertex = function( propsOrFn ){
+ return iterate( this.vertices, propsOrFn );
+};
+
+Graph.prototype.adjacent = function( vertex ){
+ var self = this;
+ return iterate( vertex.edges, function( edge ){
+ return self.vertices[ edge.target ];
+ });
+};
+
+Graph.prototype.eachAdjacent = function( vertex, fn ){
+ var self = this;
+ return iterate( vertex.edges, function( edge ){
+ var adj = self.vertices[ edge.target ];
+ return fn.call( vertex, adj, edge );
+ });
+};
+
+Graph.prototype.print = function(){
+ var self = this;
+ console.log( 'Graph has ' + Object.keys( self.vertices ).length + ' vertices' );
+ self.eachVertex( function( vertex ){
+ console.log( vertex.toString() );
+ vertex.eachEdge( function( edge ){
+ console.log( '\t ' + edge );
+ });
+ });
+ return self;
+};
+
+Graph.prototype.toDOT = function(){
+ var self = this,
+ strings = [];
+ strings.push( 'graph bler {' );
+ self.edges( function( edge ){
+ strings.push( '\t' + edge.from + ' -- ' + edge.to + ';' );
+ });
+ strings.push( '}' );
+ return strings.join( '\n' );
+};
+
+Graph.prototype.toNodesAndLinks = function(){
+ var self = this,
+ indeces = {};
+ return {
+ nodes : self.eachVertex( function( vertex, key, i ){
+ indeces[ vertex.name ] = i;
+ return vertex.toJSON();
+ }),
+ links : self.edges( function( edge ){
+ var json = edge.toJSON();
+ json.source = indeces[ edge.source ];
+ json.target = indeces[ edge.target ];
+ return json;
+ })
+ };
+};
+
+Graph.prototype.toVerticesAndEdges = function(){
+ var self = this;
+ return {
+ vertices : self.eachVertex( function( vertex, key ){
+ return vertex.toJSON();
+ }),
+ edges : self.edges( function( edge ){
+ return edge.toJSON();
+ })
+ };
+};
+
+Graph.prototype.breadthFirstSearch = function( start, processFns ){
+ return new BreadthFirstSearch( this ).search( start );
+};
+
+Graph.prototype.breadthFirstSearchTree = function( start, processFns ){
+ return new BreadthFirstSearch( this ).searchTree( start );
+};
+
+Graph.prototype.depthFirstSearch = function( start, processFns ){
+ return new DepthFirstSearch( this ).search( start );
+};
+
+Graph.prototype.depthFirstSearchTree = function( start, processFns ){
+ return new DepthFirstSearch( this ).searchTree( start );
+};
+
+
+//Graph.prototype.shortestPath = function( start, end ){
+//};
+//
+//Graph.prototype.articulationVertices = function(){
+//};
+//
+//Graph.prototype.isAcyclic = function(){
+//};
+//
+//Graph.prototype.isBipartite = function(){
+//};
+
+Graph.prototype.weakComponents = function(){
+//TODO: alternately, instead of returning graph-like objects:
+// - could simply decorate the vertices (vertex.component = componentIndex), or clone the graph and do that
+ var self = this,
+ searchGraph = this,
+ undiscovered,
+ components = [];
+
+ function getComponent( undiscoveredVertex ){
+//TODO: better interface on dfs (search v. searchTree)
+ var search = new DepthFirstSearch( searchGraph )._search( undiscoveredVertex );
+
+ // remove curr discovered from undiscovered
+ undiscovered = undiscovered.filter( function( name ){
+ return !( name in search.discovered );
+ });
+
+ return {
+ vertices : Object.keys( search.discovered ).map( function( vertexName ){
+ return self.vertices[ vertexName ].toJSON();
+ }),
+ edges : search.edges.map( function( edge ){
+ // restore any reversed edges
+ var hasBeenReversed = self.vertices[ edge.target ].edges[ edge.source ] !== undefined;
+ if( self.directed && hasBeenReversed ){
+ var swap = edge.source;
+ edge.source = edge.target;
+ edge.target = swap;
+ }
+ return edge;
+ })
+ };
+ }
+
+ if( self.directed ){
+ // if directed - convert to undirected for search
+ searchGraph = new Graph( false, self.toNodesAndLinks() );
+ }
+ undiscovered = Object.keys( searchGraph.vertices );
+ //console.debug( '(initial) undiscovered:', undiscovered );
+ while( undiscovered.length ){
+ var undiscoveredVertex = searchGraph.vertices[ undiscovered.shift() ];
+ components.push( getComponent( undiscoveredVertex ) );
+ //console.debug( 'undiscovered now:', undiscovered );
+ }
+
+ //console.debug( 'components:\n', JSON.stringify( components, null, ' ' ) );
+ return components;
+};
+
+Graph.prototype.weakComponentGraph = function(){
+ //note: although this can often look like the original graph - edges can be lost
+ var components = this.weakComponents();
+ return new Graph( this.directed, {
+ vertices : components.reduce( function( reduction, curr ){
+ return reduction.concat( curr.vertices );
+ }, [] ),
+ edges : components.reduce( function( reduction, curr ){
+ return reduction.concat( curr.edges );
+ }, [] )
+ });
+};
+
+Graph.prototype.weakComponentGraphArray = function(){
+ //note: although this can often look like the original graph - edges can be lost
+ return this.weakComponents().map( function( component ){
+ return new Graph( this.directed, component );
+ });
+};
+
+
+// ============================================================================
+
+
+
+// ============================================================================
+function randGraph( directed, numVerts, numEdges ){
+ //console.debug( 'randGraph', directed, numVerts, numEdges );
+ var data = { nodes : [], links : [] };
+ function randRange( range ){
+ return Math.floor( Math.random() * range );
+ }
+ for( var i=0; i<numVerts; i++ ){
+ data.nodes.push({ name: i });
+ }
+ for( i=0; i<numEdges; i++ ){
+ data.links.push({
+ source : randRange( numVerts ),
+ target : randRange( numVerts )
+ });
+ }
+ //console.debug( JSON.stringify( data, null, ' ' ) );
+ return new Graph( directed, data );
+}
+
+
+// ============================================================================
+ return {
+ Vertex : Vertex,
+ Edge : Edge,
+ BreadthFirstSearch : BreadthFirstSearch,
+ DepthFirstSearch : DepthFirstSearch,
+ Graph : Graph,
+ randGraph : randGraph
+ };
+});
diff -r 9f5491972e72ee38e011d49e891303e47bca29d9 -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -529,6 +529,27 @@
return trans.fill_template( "history/display_structured.mako", items=items, history=history )
@web.expose
+ def structure( self, trans, id=None ):
+ """
+ """
+ if not id:
+ id = trans.security.encode_id( trans.history.id )
+
+ history_to_view = self.get_history( trans, id, check_ownership=False, check_accessible=True )
+ history_data = self.mgrs.histories._get_history_data( trans, history_to_view )
+ history_dictionary = history_data[ 'history' ]
+ hda_dictionaries = history_data[ 'contents' ]
+
+ jobs = ( trans.sa_session.query( trans.app.model.Job )
+ .filter( trans.app.model.Job.user == trans.user )
+ .filter( trans.app.model.Job.history_id == trans.security.decode_id( id ) ) ).all()
+
+ jobs = map( lambda j: self.encode_all_ids( trans, j.to_dict( 'element' ), True ), jobs )
+
+ return trans.fill_template( "history/structure.mako", historyId=history_dictionary[ 'id' ],
+ history=history_dictionary, hdas=hda_dictionaries, jobs=jobs )
+
+ @web.expose
def view( self, trans, id=None, show_deleted=False, show_hidden=False, use_panels=True ):
"""
View a history. If a history is importable, then it is viewable by any user.
diff -r 9f5491972e72ee38e011d49e891303e47bca29d9 -r eacfa5a1a3f2e9fc516bea159c16ddb36067a220 static/scripts/mvc/collection/collection-li.js
--- a/static/scripts/mvc/collection/collection-li.js
+++ b/static/scripts/mvc/collection/collection-li.js
@@ -100,8 +100,6 @@
/** add the DCE class to the list item */
className : ListItemView.prototype.className + " dataset-collection-element",
- /** jq fx speed for this view */
- fxSpeed : 'fast',
/** set up */
initialize : function( attributes ){
This diff is so big that we needed to truncate the remainder.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dannon: Move handler startup to immediately following full creation and association of the JobManager.
by commits-noreply@bitbucket.org 15 Oct '14
by commits-noreply@bitbucket.org 15 Oct '14
15 Oct '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/9f5491972e72/
Changeset: 9f5491972e72
User: dannon
Date: 2014-10-15 14:21:13+00:00
Summary: Move handler startup to immediately following full creation and association of the JobManager.
Affected #: 2 files
diff -r d43121eccec2fa0aa28e4d8ff60204742f6b23af -r 9f5491972e72ee38e011d49e891303e47bca29d9 lib/galaxy/app.py
--- a/lib/galaxy/app.py
+++ b/lib/galaxy/app.py
@@ -134,6 +134,7 @@
# Start the job manager
from galaxy.jobs import manager
self.job_manager = manager.JobManager( self )
+ self.job_manager.start()
# FIXME: These are exposed directly for backward compatibility
self.job_queue = self.job_manager.job_queue
self.job_stop_queue = self.job_manager.job_stop_queue
diff -r d43121eccec2fa0aa28e4d8ff60204742f6b23af -r 9f5491972e72ee38e011d49e891303e47bca29d9 lib/galaxy/jobs/manager.py
--- a/lib/galaxy/jobs/manager.py
+++ b/lib/galaxy/jobs/manager.py
@@ -27,6 +27,8 @@
self.job_handler = NoopHandler()
self.job_queue = self.job_stop_queue = NoopQueue()
self.job_lock = False
+
+ def start( self ):
self.job_handler.start()
def shutdown( self ):
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0