galaxy-commits
Threads by month
- ----- 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
November 2013
- 1 participants
- 208 discussions
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/2be3aed79643/
Changeset: 2be3aed79643
Branch: next-stable
User: greg
Date: 2013-11-02 16:04:47
Summary: Fix for handling a repository dependency that has a tool dependency in error because it has a missing env.sh file.
Affected #: 1 file
diff -r 52df185d7552f2ecc13097bdae86388faf640990 -r 2be3aed79643555879b1b5154449872bcd6447da lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
@@ -272,7 +272,12 @@
env_var_dict[ 'value' ] = new_value
new_env_var_dicts.append( env_var_dict )
else:
- log.debug( 'Invalid file %s specified, ignoring set_environment_for_install action.', env_sh_file_path )
+ tool_dependency.status = app.model.ToolShedRepository.installation_status.ERROR
+ error_message = 'Invalid file %s specified, ignoring set_environment_for_install action.', env_sh_file_path
+ tool_dependency.error_message = error_message
+ sa_session = app.model.context.current
+ sa_session.add( tool_dependency )
+ sa_session.flush()
action_dict[ 'environment_variable' ] = new_env_var_dicts
else:
action_dict[ 'environment_variable' ] = env_var_dicts
https://bitbucket.org/galaxy/galaxy-central/commits/68c80bac0d4d/
Changeset: 68c80bac0d4d
User: greg
Date: 2013-11-02 16:05:11
Summary: Merged from next-stable
Affected #: 1 file
diff -r ea6ff3d7224db5d218eefa91af1b08626229eae1 -r 68c80bac0d4d71854526f6e56156f09b46059205 lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
@@ -272,7 +272,12 @@
env_var_dict[ 'value' ] = new_value
new_env_var_dicts.append( env_var_dict )
else:
- log.debug( 'Invalid file %s specified, ignoring set_environment_for_install action.', env_sh_file_path )
+ tool_dependency.status = app.model.ToolShedRepository.installation_status.ERROR
+ error_message = 'Invalid file %s specified, ignoring set_environment_for_install action.', env_sh_file_path
+ tool_dependency.error_message = error_message
+ sa_session = app.model.context.current
+ sa_session.add( tool_dependency )
+ sa_session.flush()
action_dict[ 'environment_variable' ] = new_env_var_dicts
else:
action_dict[ 'environment_variable' ] = env_var_dicts
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
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/52df185d7552/
Changeset: 52df185d7552
Branch: next-stable
User: Dave Bouvier
Date: 2013-11-01 21:08:49
Summary: Update RSS feed filters to reflect which revisions are regularly tested and relevant to display.
Affected #: 1 file
diff -r 7c60a5b67c3ec7825d3d2511f27867823de19350 -r 52df185d7552f2ecc13097bdae86388faf640990 lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -1552,6 +1552,8 @@
trans.model.Repository.table.c.user_id == user.id ) ):
if not metadata_row.tool_test_results:
continue
+ if metadata_row.changeset_revision != metadata_row.repository.tip( trans.app ):
+ continue
current_repository_errors = []
tool_dependency_errors = []
repository_dependency_errors = []
https://bitbucket.org/galaxy/galaxy-central/commits/ea6ff3d7224d/
Changeset: ea6ff3d7224d
User: Dave Bouvier
Date: 2013-11-01 21:09:23
Summary: Merge with next-stable.
Affected #: 1 file
diff -r 78388e08a9a15fcbe77e78430a7ac05a46e405bd -r ea6ff3d7224db5d218eefa91af1b08626229eae1 lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -1552,6 +1552,8 @@
trans.model.Repository.table.c.user_id == user.id ) ):
if not metadata_row.tool_test_results:
continue
+ if metadata_row.changeset_revision != metadata_row.repository.tip( trans.app ):
+ continue
current_repository_errors = []
tool_dependency_errors = []
repository_dependency_errors = []
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: Users API: add UsesTagsMixin
by commits-noreply@bitbucket.org 01 Nov '13
by commits-noreply@bitbucket.org 01 Nov '13
01 Nov '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/78388e08a9a1/
Changeset: 78388e08a9a1
User: carlfeberhard
Date: 2013-11-01 20:54:56
Summary: Users API: add UsesTagsMixin
Affected #: 1 file
diff -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 -r 78388e08a9a15fcbe77e78430a7ac05a46e405bd lib/galaxy/webapps/galaxy/api/users.py
--- a/lib/galaxy/webapps/galaxy/api/users.py
+++ b/lib/galaxy/webapps/galaxy/api/users.py
@@ -4,12 +4,12 @@
import logging
from paste.httpexceptions import HTTPBadRequest, HTTPNotImplemented
from galaxy import util, web
-from galaxy.web.base.controller import BaseAPIController
+from galaxy.web.base.controller import BaseAPIController, UsesTagsMixin
log = logging.getLogger( __name__ )
-class UserAPIController( BaseAPIController ):
+class UserAPIController( BaseAPIController, UsesTagsMixin ):
@web.expose_api
def index( self, trans, deleted='False', **kwd ):
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 panel, tags: use select2 for tag controls; Fix broken tags set implementation for HDAs; pack scripts
by commits-noreply@bitbucket.org 01 Nov '13
by commits-noreply@bitbucket.org 01 Nov '13
01 Nov '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/d69e53660b28/
Changeset: d69e53660b28
User: carlfeberhard
Date: 2013-11-01 20:25:41
Summary: History panel, tags: use select2 for tag controls; Fix broken tags set implementation for HDAs; pack scripts
Affected #: 23 files
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 lib/galaxy/webapps/galaxy/api/history_contents.py
--- a/lib/galaxy/webapps/galaxy/api/history_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/history_contents.py
@@ -13,7 +13,7 @@
class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin,
- UsesLibraryMixin, UsesLibraryMixinItems ):
+ UsesLibraryMixin, UsesLibraryMixinItems, UsesTagsMixin ):
@web.expose_api_anonymous
def index( self, trans, history_id, ids=None, **kwd ):
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/mvc/dataset/hda-base.js
--- a/static/scripts/mvc/dataset/hda-base.js
+++ b/static/scripts/mvc/dataset/hda-base.js
@@ -259,7 +259,6 @@
* @returns {jQuery} rendered DOM
*/
_render_body : function(){
- console.debug( 'model:', this.model.toJSON() );
var $body = $( '<div>Error: unknown dataset state "' + this.model.get( 'state' ) + '".</div>' ),
// cheesy: get function by assumed matching name
renderFn = this[ '_render_body_' + this.model.get( 'state' ) ];
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/mvc/dataset/hda-edit.js
--- a/static/scripts/mvc/dataset/hda-edit.js
+++ b/static/scripts/mvc/dataset/hda-edit.js
@@ -380,6 +380,10 @@
this._render_tagButton(),
this._render_annotateButton()
]);
+ this.tagsEditor = new TagsEditor({
+ model : this.model,
+ el : $body.find( '.tags-display' )
+ }).render();
return $body;
},
@@ -400,7 +404,7 @@
'click .dataset-unhide' : function( ev ){ this.model.unhide(); return false; },
'click .dataset-purge' : 'confirmPurge',
- 'click .dataset-tag-btn' : 'loadAndDisplayTags',
+ 'click .dataset-tag-btn' : 'displayTags',
'click .dataset-annotate-btn' : 'loadAndDisplayAnnotation'
},
@@ -415,34 +419,8 @@
/** Find the tag area and, if initial: load the html (via ajax) for displaying them; otherwise, unhide/hide
*/
//TODO: into sub-MV
- loadAndDisplayTags : function( event ){
- this.log( this + '.loadAndDisplayTags', event );
- var view = this,
- $tagArea = this.$el.find( '.tags-display' ),
- $tagElt = $tagArea.find( '.tags' );
-
- // Show or hide tag area; if showing tag area and it's empty, fill it.
- if( $tagArea.is( ":hidden" ) ){
- if( !jQuery.trim( $tagElt.html() ) ){
- // Need to fill tag element.
- var xhr = $.ajax( this.urls.tags.get );
- xhr.fail( function( xhr, status, error ){
- view.log( "Tagging failed", xhr, status, error );
- view.trigger( 'error', view, xhr, {}, _l( "Tagging failed" ) );
- });
- xhr.done( function( tagHtml ){
- $tagElt.html( tagHtml );
- $tagElt.find( "[title]" ).tooltip();
- $tagArea.slideDown( view.fxSpeed );
- });
- } else {
- // Tag element is filled; show.
- $tagArea.slideDown( view.fxSpeed );
- }
- } else {
- // Hide.
- $tagArea.slideUp( view.fxSpeed );
- }
+ displayTags : function( event ){
+ this.$el.find( '.tags-display' ).slideToggle( this.fxSpeed );
return false;
},
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/mvc/dataset/hda-model.js
--- a/static/scripts/mvc/dataset/hda-model.js
+++ b/static/scripts/mvc/dataset/hda-model.js
@@ -50,7 +50,9 @@
meta_files : [],
misc_blurb : '',
- misc_info : ''
+ misc_info : '',
+
+ tags : null
},
/** fetch location of this HDA's history in the api */
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/mvc/history/history-panel.js
--- a/static/scripts/mvc/history/history-panel.js
+++ b/static/scripts/mvc/history/history-panel.js
@@ -637,6 +637,10 @@
this._render_tagButton(),
this._render_annotateButton()
]);
+ this.tagsEditor = new TagsEditor({
+ model : this.model,
+ el : $newRender.find( '.history-controls .tags-display' )
+ }).render();
}
this._setUpBehaviours( $newRender );
@@ -755,7 +759,7 @@
// ------------------------------------------------------------------------ panel events
/** event map */
events : {
- 'click .history-tag-button' : 'loadAndDisplayTags',
+ 'click .history-tag-button' : 'displayTags',
//TODO: switch to common close (X) idiom
// allow (error) messages to be clicked away
'click .message-container' : 'clearMessages'
@@ -801,35 +805,8 @@
/** Find the tag area and, if initial: load the html (via ajax) for displaying them; otherwise, unhide/hide
*/
//TODO: into sub-MV
- loadAndDisplayTags : function( event ){
- var panel = this,
- $tagArea = this.$el.find( '.history-controls .tags-display' ),
- $tagElt = $tagArea.find( '.tags' );
-
- // Show or hide tag area; if showing tag area and it's empty, fill it
- if( $tagArea.is( ":hidden" ) ){
- if( !jQuery.trim( $tagElt.html() ) ){
- // Need to fill tag element.
- var xhr = jQuery.ajax( panel.model.tagUrl() );
- xhr.fail( function( xhr, status, error ){
- panel.log( 'Error loading tag area html', xhr, error, status );
- panel.trigger( 'error', panel, xhr, null, _l( "Error loading tags" ) );
- });
- xhr.done( function( html ){
- //panel.log( panel + ' tag elt html (ajax)', tag_elt_html );
- $tagElt.html( html );
- $tagElt.find( "[title]" ).tooltip();
- $tagArea.slideDown( panel.fxSpeed );
- });
- } else {
- // Tag element already filled: show
- $tagArea.slideDown( panel.fxSpeed );
- }
-
- } else {
- // Currently shown: Hide
- $tagArea.slideUp( panel.fxSpeed );
- }
+ displayTags : function( event ){
+ this.$el.find( '.history-controls .tags-display' ).slideToggle( this.fxSpeed );
return false;
},
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/mvc/tags.js
--- a/static/scripts/mvc/tags.js
+++ b/static/scripts/mvc/tags.js
@@ -1,66 +1,88 @@
-/*==============================================================================
-Backbone MV for Tags
+/** */
+var TagsEditor = Backbone.View.extend( LoggableMixin ).extend({
+
+ tagName : 'div',
+ className : 'tags-display',
+ /** */
+ initialize : function( options ){
+ //console.debug( this, options );
+ this.listenTo( this.model, 'change:tags', function(){
+ this.render();
+ });
+ },
-TODO:
-move rendering from tagging_common.py
-move functionality from controllers/tag.py
-?? - polymorph on class or simply class as attr?
+ /** */
+ render : function(){
+ var view = this;
-==============================================================================*/
-/** Single Tag model
- */
-var Tag = BaseModel.extend( LoggableMixin ).extend({
-
- // uncomment this out see log messages
- logger : console,
-
- defaults : {
- id : null,
- itemClass : null
+ this.$el.html( this.template() );
+ this.$el.find( '.tags-input' ).select2({
+ placeholder : 'Add tags',
+ width : '100%',
+ tags : function(){
+ // initialize possible tags in the dropdown based on all the tags the user has used so far
+ return view.getTagsUsed();
+ }
+ });
+ this._behaviors();
+ return this;
},
-
- toString : function(){
- return 'Tag()';
- }
+
+ /** */
+ template : function(){
+ return [
+ //TODO: make prompt optional
+ '<label class="prompt">', _l( 'Tags' ), '</label>',
+ // set up initial tags by adding as CSV to input vals (necc. to init select2)
+ '<input class="tags-input" value="', this.tagsToCSV( this.model.get( 'tags' ) ), '" />'
+ ].join( '' );
+ },
+
+ /** */
+ tagsToCSV : function( tagsArray ){
+ if( !_.isArray( tagsArray ) || _.isEmpty( tagsArray ) ){
+ return '';
+ }
+ return tagsArray.sort().join( ',' );
+ },
+
+ /** */
+ getTagsUsed : function(){
+ return _.map( Galaxy.currUser.get( 'tags_used' ), function( tag ){
+ return { id: tag, text: tag };
+ });
+ },
+
+ /** */
+ _behaviors : function(){
+ var view = this;
+ this.$el.find( '.tags-input' ).on( 'change', function( event ){
+ // save the model's tags in either remove or added event
+ view.model.save({ tags: event.val }, { silent: true });
+ // if it's new, add the tag to the users tags
+ if( event.added ){
+ view.addNewTagToTagsUsed( event.added.text );
+ }
+ });
+ },
+
+ /** */
+ addNewTagToTagsUsed : function( newTag ){
+ var tagsUsed = Galaxy.currUser.get( 'tags_used' );
+ if( !_.contains( tagsUsed, newTag ) ){
+ tagsUsed.push( newTag );
+ tagsUsed.sort();
+ Galaxy.currUser.set( 'tags_used', tagsUsed );
+ }
+ },
+
+ /** */
+ remove : function(){
+ this.$el.off();
+ Backbone.View.prototype.remove.call( this );
+ },
+
+ /** */
+ toString : function(){ return [ 'TagSetView(', this.model + '', ')' ].join(''); }
});
-
-//------------------------------------------------------------------------------
-/** Single Tag view
- */
-var TagView = BaseView.extend( LoggableMixin ).extend({
-
- // uncomment this out see log messages
- logger : console,
-
- toString : function(){
- return 'TagView()';
- }
-});
-
-//==============================================================================
-/** A collection of Tags
- */
-var TagCollection = Backbone.Collection.extend( LoggableMixin ).extend({
- model : Tag,
-
- // uncomment this out see log messages
- logger : console,
-
- toString : function(){
- return 'TagCollection()';
- }
-});
-
-//------------------------------------------------------------------------------
-/** View for a TagCollection (and it's controls) - as per an hda's tag controls on the history panel
- */
-var TagList = BaseView.extend( LoggableMixin ).extend({
-
- // uncomment this out see log messages
- logger : console,
-
- toString : function(){
- return 'TagList()';
- }
-});
\ No newline at end of file
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/mvc/ui.js
--- a/static/scripts/mvc/ui.js
+++ b/static/scripts/mvc/ui.js
@@ -490,3 +490,5 @@
}
return $button;
};
+
+//var hideUntilActivated;
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/packed/mvc/dataset/hda-base.js
--- a/static/scripts/packed/mvc/dataset/hda-base.js
+++ b/static/scripts/packed/mvc/dataset/hda-base.js
@@ -1,1 +1,1 @@
-define(["mvc/dataset/hda-model"],function(b){var a=Backbone.View.extend(LoggableMixin).extend({tagName:"div",className:"dataset hda history-panel-hda",id:function(){return"hda-"+this.model.get("id")},fxSpeed:"fast",initialize:function(c){if(c.logger){this.logger=this.model.logger=c.logger}this.log(this+".initialize:",c);this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton];this.expanded=c.expanded||false;this._setUpListeners()},_setUpListeners:function(){this.model.on("change",function(d,c){if(this.model.changedAttributes().state&&this.model.inReadyState()&&this.expanded&&!this.model.hasDetails()){this.model.fetch()}else{this.render()}},this)},render:function(e){e=(e===undefined)?(true):(e);var c=this;this.$el.find("[title]").tooltip("destroy");this.urls=this.model.urls();var d=$(a.templates.skeleton(this.model.toJSON()));d.find(".dataset-primary-actions").append(this._render_titleButtons());d.children(".dataset-body").replaceWith(this._render_body());this._setUpBehaviors(d);if(e){$(c).queue(function(f){this.$el.fadeOut(c.fxSpeed,f)})}$(c).queue(function(f){this.$el.empty().attr("class",c.className).addClass("state-"+c.model.get("state")).append(d.children());f()});if(e){$(c).queue(function(f){this.$el.fadeIn(c.fxSpeed,f)})}$(c).queue(function(f){this.trigger("rendered",c);if(this.model.inReadyState()){this.trigger("rendered:ready",c)}f()});return this},_setUpBehaviors:function(c){c=c||this.$el;make_popup_menus(c);c.find("[title]").tooltip({placement:"bottom"})},_render_titleButtons:function(){return[this._render_displayButton()]},_render_displayButton:function(){if((this.model.get("state")===b.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.NEW)||(!this.model.get("accessible"))){return null}var d={icon_class:"display",target:"galaxy_main"};if(this.model.get("purged")){d.disabled=true;d.title=_l("Cannot display datasets removed from disk")}else{if(this.model.get("state")===b.HistoryDatasetAssociation.STATES.UPLOAD){d.disabled=true;d.title=_l("This dataset must finish uploading before it can be viewed")}else{d.title=_l("View data");d.href=this.urls.display;var c=this;d.onclick=function(){Galaxy.frame_manager.frame_new({title:"Data Viewer: "+c.model.get("name"),type:"url",location:"center",content:c.urls.display})}}}d.faIcon="fa-eye";return faIconButton(d)},_render_downloadButton:function(){if(this.model.get("purged")||!this.model.hasData()){return null}var d=this.urls,e=this.model.get("meta_files");if(_.isEmpty(e)){return $(['<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>',"</a>"].join(""))}var f="dataset-"+this.model.get("id")+"-popup",c=['<div popupmenu="'+f+'">','<a href="'+d.download+'">',_l("Download Dataset"),"</a>","<a>"+_l("Additional Files")+"</a>",_.map(e,function(g){return['<a class="action-button" href="',d.meta_download+g.file_type,'">',_l("Download")," ",g.file_type,"</a>"].join("")}).join("\n"),"</div>",'<div class="icon-btn-group">','<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>','</a><a class="icon-btn popup" id="'+f+'">','<span class="fa fa-caret-down"></span>',"</a>","</div>"].join("\n");return $(c)},_render_showParamsButton:function(){return faIconButton({title:_l("View details"),href:this.urls.show_params,target:"galaxy_main",faIcon:"fa-info-circle"})},_render_body:function(){console.debug("model:",this.model.toJSON());var d=$('<div>Error: unknown dataset state "'+this.model.get("state")+'".</div>'),c=this["_render_body_"+this.model.get("state")];if(_.isFunction(c)){d=c.call(this)}if(this.expanded){d.show()}return d},_render_stateBodyHelper:function(c,f){f=f||[];var d=this,e=$(a.templates.body(_.extend(this.model.toJSON(),{body:c})));e.find(".dataset-actions .left").append(_.map(f,function(g){return g.call(d)}));return e},_render_body_new:function(){return this._render_stateBodyHelper("<div>"+_l("This is a new dataset and not all of its data are available yet")+"</div>")},_render_body_noPermission:function(){return this._render_stateBodyHelper("<div>"+_l("You do not have permission to view this dataset")+"</div>")},_render_body_discarded:function(){return this._render_stateBodyHelper("<div>"+_l("The job creating this dataset was cancelled before completion")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_queued:function(){return this._render_stateBodyHelper("<div>"+_l("This job is waiting to run")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_upload:function(){return this._render_stateBodyHelper("<div>"+_l("This dataset is currently uploading")+"</div>")},_render_body_setting_metadata:function(){return this._render_stateBodyHelper("<div>"+_l("Metadata is being auto-detected")+"</div>")},_render_body_running:function(){return this._render_stateBodyHelper("<div>"+_l("This job is currently running")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_paused:function(){return this._render_stateBodyHelper("<div>"+_l('This job is paused. Use the "Resume Paused Jobs" in the history menu to resume')+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_error:function(){var c=_l("An error occurred with this dataset")+": <i>"+$.trim(this.model.get("misc_info"))+"</i>";if(!this.model.get("purged")){c="<div>"+this.model.get("misc_blurb")+"</div>"+c}return this._render_stateBodyHelper(c,this.defaultPrimaryActionButtonRenderers.concat([this._render_downloadButton]))},_render_body_empty:function(){return this._render_stateBodyHelper("<div>"+_l("No data")+": <i>"+this.model.get("misc_blurb")+"</i></div>",this.defaultPrimaryActionButtonRenderers)},_render_body_failed_metadata:function(){var c=$('<div class="warningmessagesmall"></div>').append($("<strong/>").text(_l("An error occurred setting the metadata for this dataset"))),d=this._render_body_ok();d.prepend(c);return d},_render_body_ok:function(){var c=this,e=$(a.templates.body(this.model.toJSON())),d=[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers);e.find(".dataset-actions .left").append(_.map(d,function(f){return f.call(c)}));if(this.model.isDeletedOrPurged()){return e}return e},events:{"click .dataset-title-bar":"toggleBodyVisibility"},toggleBodyVisibility:function(d,c){var e=this.$el.find(".dataset-body");c=(c===undefined)?(!e.is(":visible")):(c);if(c){this.expandBody()}else{this.collapseBody()}},expandBody:function(){var c=this;function d(){c.render(false).$el.children(".dataset-body").slideDown(c.fxSpeed,function(){c.expanded=true;c.trigger("body-expanded",c.model.get("id"))})}if(this.model.inReadyState()&&!this.model.hasDetails()){this.model.fetch({silent:true}).always(function(e){d()})}else{d()}},collapseBody:function(){var c=this;this.$el.children(".dataset-body").slideUp(c.fxSpeed,function(){c.expanded=false;c.trigger("body-collapsed",c.model.get("id"))})},remove:function(d){var c=this;this.$el.fadeOut(c.fxSpeed,function(){c.$el.remove();c.off();if(d){d()}})},toString:function(){var c=(this.model)?(this.model+""):("(no model)");return"HDABaseView("+c+")"}});a.templates={skeleton:Handlebars.templates["template-hda-skeleton"],body:Handlebars.templates["template-hda-body"]};return{HDABaseView:a}});
\ No newline at end of file
+define(["mvc/dataset/hda-model"],function(b){var a=Backbone.View.extend(LoggableMixin).extend({tagName:"div",className:"dataset hda history-panel-hda",id:function(){return"hda-"+this.model.get("id")},fxSpeed:"fast",initialize:function(c){if(c.logger){this.logger=this.model.logger=c.logger}this.log(this+".initialize:",c);this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton];this.expanded=c.expanded||false;this._setUpListeners()},_setUpListeners:function(){this.model.on("change",function(d,c){if(this.model.changedAttributes().state&&this.model.inReadyState()&&this.expanded&&!this.model.hasDetails()){this.model.fetch()}else{this.render()}},this)},render:function(e){e=(e===undefined)?(true):(e);var c=this;this.$el.find("[title]").tooltip("destroy");this.urls=this.model.urls();var d=$(a.templates.skeleton(this.model.toJSON()));d.find(".dataset-primary-actions").append(this._render_titleButtons());d.children(".dataset-body").replaceWith(this._render_body());this._setUpBehaviors(d);if(e){$(c).queue(function(f){this.$el.fadeOut(c.fxSpeed,f)})}$(c).queue(function(f){this.$el.empty().attr("class",c.className).addClass("state-"+c.model.get("state")).append(d.children());f()});if(e){$(c).queue(function(f){this.$el.fadeIn(c.fxSpeed,f)})}$(c).queue(function(f){this.trigger("rendered",c);if(this.model.inReadyState()){this.trigger("rendered:ready",c)}f()});return this},_setUpBehaviors:function(c){c=c||this.$el;make_popup_menus(c);c.find("[title]").tooltip({placement:"bottom"})},_render_titleButtons:function(){return[this._render_displayButton()]},_render_displayButton:function(){if((this.model.get("state")===b.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.NEW)||(!this.model.get("accessible"))){return null}var d={icon_class:"display",target:"galaxy_main"};if(this.model.get("purged")){d.disabled=true;d.title=_l("Cannot display datasets removed from disk")}else{if(this.model.get("state")===b.HistoryDatasetAssociation.STATES.UPLOAD){d.disabled=true;d.title=_l("This dataset must finish uploading before it can be viewed")}else{d.title=_l("View data");d.href=this.urls.display;var c=this;d.onclick=function(){Galaxy.frame_manager.frame_new({title:"Data Viewer: "+c.model.get("name"),type:"url",location:"center",content:c.urls.display})}}}d.faIcon="fa-eye";return faIconButton(d)},_render_downloadButton:function(){if(this.model.get("purged")||!this.model.hasData()){return null}var d=this.urls,e=this.model.get("meta_files");if(_.isEmpty(e)){return $(['<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>',"</a>"].join(""))}var f="dataset-"+this.model.get("id")+"-popup",c=['<div popupmenu="'+f+'">','<a href="'+d.download+'">',_l("Download Dataset"),"</a>","<a>"+_l("Additional Files")+"</a>",_.map(e,function(g){return['<a class="action-button" href="',d.meta_download+g.file_type,'">',_l("Download")," ",g.file_type,"</a>"].join("")}).join("\n"),"</div>",'<div class="icon-btn-group">','<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>','</a><a class="icon-btn popup" id="'+f+'">','<span class="fa fa-caret-down"></span>',"</a>","</div>"].join("\n");return $(c)},_render_showParamsButton:function(){return faIconButton({title:_l("View details"),href:this.urls.show_params,target:"galaxy_main",faIcon:"fa-info-circle"})},_render_body:function(){var d=$('<div>Error: unknown dataset state "'+this.model.get("state")+'".</div>'),c=this["_render_body_"+this.model.get("state")];if(_.isFunction(c)){d=c.call(this)}if(this.expanded){d.show()}return d},_render_stateBodyHelper:function(c,f){f=f||[];var d=this,e=$(a.templates.body(_.extend(this.model.toJSON(),{body:c})));e.find(".dataset-actions .left").append(_.map(f,function(g){return g.call(d)}));return e},_render_body_new:function(){return this._render_stateBodyHelper("<div>"+_l("This is a new dataset and not all of its data are available yet")+"</div>")},_render_body_noPermission:function(){return this._render_stateBodyHelper("<div>"+_l("You do not have permission to view this dataset")+"</div>")},_render_body_discarded:function(){return this._render_stateBodyHelper("<div>"+_l("The job creating this dataset was cancelled before completion")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_queued:function(){return this._render_stateBodyHelper("<div>"+_l("This job is waiting to run")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_upload:function(){return this._render_stateBodyHelper("<div>"+_l("This dataset is currently uploading")+"</div>")},_render_body_setting_metadata:function(){return this._render_stateBodyHelper("<div>"+_l("Metadata is being auto-detected")+"</div>")},_render_body_running:function(){return this._render_stateBodyHelper("<div>"+_l("This job is currently running")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_paused:function(){return this._render_stateBodyHelper("<div>"+_l('This job is paused. Use the "Resume Paused Jobs" in the history menu to resume')+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_error:function(){var c=_l("An error occurred with this dataset")+": <i>"+$.trim(this.model.get("misc_info"))+"</i>";if(!this.model.get("purged")){c="<div>"+this.model.get("misc_blurb")+"</div>"+c}return this._render_stateBodyHelper(c,this.defaultPrimaryActionButtonRenderers.concat([this._render_downloadButton]))},_render_body_empty:function(){return this._render_stateBodyHelper("<div>"+_l("No data")+": <i>"+this.model.get("misc_blurb")+"</i></div>",this.defaultPrimaryActionButtonRenderers)},_render_body_failed_metadata:function(){var c=$('<div class="warningmessagesmall"></div>').append($("<strong/>").text(_l("An error occurred setting the metadata for this dataset"))),d=this._render_body_ok();d.prepend(c);return d},_render_body_ok:function(){var c=this,e=$(a.templates.body(this.model.toJSON())),d=[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers);e.find(".dataset-actions .left").append(_.map(d,function(f){return f.call(c)}));if(this.model.isDeletedOrPurged()){return e}return e},events:{"click .dataset-title-bar":"toggleBodyVisibility"},toggleBodyVisibility:function(d,c){var e=this.$el.find(".dataset-body");c=(c===undefined)?(!e.is(":visible")):(c);if(c){this.expandBody()}else{this.collapseBody()}},expandBody:function(){var c=this;function d(){c.render(false).$el.children(".dataset-body").slideDown(c.fxSpeed,function(){c.expanded=true;c.trigger("body-expanded",c.model.get("id"))})}if(this.model.inReadyState()&&!this.model.hasDetails()){this.model.fetch({silent:true}).always(function(e){d()})}else{d()}},collapseBody:function(){var c=this;this.$el.children(".dataset-body").slideUp(c.fxSpeed,function(){c.expanded=false;c.trigger("body-collapsed",c.model.get("id"))})},remove:function(d){var c=this;this.$el.fadeOut(c.fxSpeed,function(){c.$el.remove();c.off();if(d){d()}})},toString:function(){var c=(this.model)?(this.model+""):("(no model)");return"HDABaseView("+c+")"}});a.templates={skeleton:Handlebars.templates["template-hda-skeleton"],body:Handlebars.templates["template-hda-body"]};return{HDABaseView:a}});
\ No newline at end of file
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/packed/mvc/dataset/hda-edit.js
--- a/static/scripts/packed/mvc/dataset/hda-edit.js
+++ b/static/scripts/packed/mvc/dataset/hda-edit.js
@@ -1,1 +1,1 @@
-define(["mvc/dataset/hda-model","mvc/dataset/hda-base"],function(d,a){var f=a.HDABaseView.extend(LoggableMixin).extend({initialize:function(g){a.HDABaseView.prototype.initialize.call(this,g);this.hasUser=g.hasUser;this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton,this._render_rerunButton]},_render_titleButtons:function(){return a.HDABaseView.prototype._render_titleButtons.call(this).concat([this._render_editButton(),this._render_deleteButton()])},_render_editButton:function(){if((this.model.get("state")===d.HistoryDatasetAssociation.STATES.NEW)||(this.model.get("state")===d.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===d.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var i=this.model.get("purged"),g=this.model.get("deleted"),h={title:_l("Edit Attributes"),href:this.urls.edit,target:"galaxy_main",icon_class:"edit"};if(g||i){h.enabled=false;if(i){h.title=_l("Cannot edit attributes of datasets removed from disk")}else{if(g){h.title=_l("Undelete dataset to edit attributes")}}}else{if(this.model.get("state")===d.HistoryDatasetAssociation.STATES.UPLOAD){h.disabled=true;h.title=_l("This dataset must finish uploading before it can be edited")}}h.faIcon="fa-pencil";return faIconButton(h)},_render_deleteButton:function(){if((this.model.get("state")===d.HistoryDatasetAssociation.STATES.NEW)||(this.model.get("state")===d.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var g=this,h=g.urls["delete"],i={title:_l("Delete"),href:h,icon_class:"delete",onclick:function(){g.$el.find(".menu-button.delete").trigger("mouseout");g.model["delete"]()}};if(this.model.get("deleted")||this.model.get("purged")){i={title:_l("Dataset is already deleted"),icon_class:"delete",enabled:false}}i.faIcon="fa-times";return faIconButton(i)},_render_errButton:function(){if(this.model.get("state")!==d.HistoryDatasetAssociation.STATES.ERROR){return null}return faIconButton({title:_l("View or report this error"),href:this.urls.report_error,target:"galaxy_main",faIcon:"fa-bug"})},_render_rerunButton:function(){return faIconButton({title:_l("Run this job again"),href:this.urls.rerun,target:"galaxy_main",faIcon:"fa-refresh"})},_render_visualizationsButton:function(){var g=this.model.get("visualizations");if((!this.model.hasData())||(_.isEmpty(g))){return null}if(_.isObject(g[0])){return this._render_visualizationsFrameworkButton(g)}if(!this.urls.visualization){return null}var i=this.model.get("dbkey"),l=this.urls.visualization,j={},m={dataset_id:this.model.get("id"),hda_ldda:"hda"};if(i){m.dbkey=i}var h=faIconButton({title:_l("Visualize"),href:this.urls.visualization,faIcon:"fa-bar-chart-o"});function k(n){switch(n){case"trackster":return b(l,m,i);case"scatterplot":return e(l,m);default:return function(){Galaxy.frame_manager.frame_new({title:"Visualization",type:"url",content:l+"/"+n+"?"+$.param(m)})}}}if(g.length===1){h.attr("title",g[0]);h.click(k(g[0]))}else{_.each(g,function(o){var n=o.charAt(0).toUpperCase()+o.slice(1);j[_l(n)]=k(o)});make_popupmenu(h,j)}return h},_render_visualizationsFrameworkButton:function(g){if(!(this.model.hasData())||!(g&&!_.isEmpty(g))){return null}var i=faIconButton({title:_l("Visualize"),faIcon:"fa-bar-chart-o"});i.addClass("visualize-icon");if(_.keys(g).length===1){i.attr("title",_.keys(g)[0]);i.attr("href",_.values(g)[0])}else{var j=[];_.each(g,function(k){j.push(k)});var h=new PopupMenu(i,j)}return i},_render_tagButton:function(){if(!this.hasUser||!this.urls.tags.get){return null}return faIconButton({title:_l("Edit dataset tags"),classes:"dataset-tag-btn",faIcon:"fa-tags"})},_render_annotateButton:function(){if(!this.hasUser||!this.urls.annotation.get){return null}return faIconButton({title:_l("Edit dataset annotation"),classes:"dataset-annotate-btn",faIcon:"fa-comment"})},_render_body_failed_metadata:function(){var h=$("<a/>").attr({href:this.urls.edit,target:"galaxy_main"}).text(_l("set it manually or retry auto-detection")),g=$("<span/>").text(". "+_l("You may be able to")+" ").append(h),i=a.HDABaseView.prototype._render_body_failed_metadata.call(this);i.find(".warningmessagesmall strong").append(g);return i},_render_body_error:function(){var g=a.HDABaseView.prototype._render_body_error.call(this);g.find(".dataset-actions .left").prepend(this._render_errButton());return g},_render_body_ok:function(){var g=a.HDABaseView.prototype._render_body_ok.call(this);if(this.model.isDeletedOrPurged()){return g}this.makeDbkeyEditLink(g);g.find(".dataset-actions .left").append(this._render_visualizationsButton());g.find(".dataset-actions .right").append([this._render_tagButton(),this._render_annotateButton()]);return g},makeDbkeyEditLink:function(g){if(this.model.get("metadata_dbkey")==="?"&&!this.model.isDeletedOrPurged()){g.find(".dataset-dbkey .value").replaceWith($('<a target="galaxy_main">?</a>').attr("href",this.urls.edit))}},events:{"click .dataset-title-bar":"toggleBodyVisibility","click .dataset-undelete":function(g){this.model.undelete();return false},"click .dataset-unhide":function(g){this.model.unhide();return false},"click .dataset-purge":"confirmPurge","click .dataset-tag-btn":"loadAndDisplayTags","click .dataset-annotate-btn":"loadAndDisplayAnnotation"},confirmPurge:function c(g){this.model.purge();return false},loadAndDisplayTags:function(i){this.log(this+".loadAndDisplayTags",i);var g=this,h=this.$el.find(".tags-display"),j=h.find(".tags");if(h.is(":hidden")){if(!jQuery.trim(j.html())){var k=$.ajax(this.urls.tags.get);k.fail(function(n,l,m){g.log("Tagging failed",n,l,m);g.trigger("error",g,n,{},_l("Tagging failed"))});k.done(function(l){j.html(l);j.find("[title]").tooltip();h.slideDown(g.fxSpeed)})}else{h.slideDown(g.fxSpeed)}}else{h.slideUp(g.fxSpeed)}return false},loadAndDisplayAnnotation:function(j){this.log(this+".loadAndDisplayAnnotation",j);var i=this,h=this.$el.find(".annotation-display"),g=h.find(".annotation");if(h.is(":hidden")){if(!jQuery.trim(g.html())){var k=$.ajax(this.urls.annotation.get);k.fail(function(n,l,m){i.log("Annotation failed",n,l,m);i.trigger("error",i,n,{},_l("Annotation failed"))});k.done(function(l){l=l||"<em>"+_l("Describe or add notes to dataset")+"</em>";g.html(l);h.find("[title]").tooltip();g.make_text_editable({use_textarea:true,on_finish:function(m){g.text(m);i.model.save({annotation:m},{silent:true}).fail(function(){g.text(i.model.previous("annotation"))})}});h.slideDown(i.fxSpeed)})}else{h.slideDown(i.fxSpeed)}}else{h.slideUp(i.fxSpeed)}return false},toString:function(){var g=(this.model)?(this.model+""):("(no model)");return"HDAView("+g+")"}});function e(g,h){action=function(){Galaxy.frame_manager.frame_new({title:"Scatterplot",type:"url",content:g+"/scatterplot?"+$.param(h),location:"center"});$("div.popmenu-wrapper").remove();return false};return action}function b(g,i,h){return function(){var j={};if(h){j["f-dbkey"]=h}$.ajax({url:g+"/list_tracks?"+$.param(j),dataType:"html",error:function(){alert(("Could not add this dataset to browser")+".")},success:function(k){var l=window.parent;l.Galaxy.modal.show({title:"View Data in a New or Saved Visualization",buttons:{Cancel:function(){l.Galaxy.modal.hide()},"View in saved visualization":function(){l.Galaxy.modal.show({title:"Add Data to Saved Visualization",body:k,buttons:{Cancel:function(){l.Galaxy.modal.hide()},"Add to visualization":function(){$(l.document).find("input[name=id]:checked").each(function(){l.Galaxy.modal.hide();var m=$(this).val();i.id=m;l.Galaxy.frame_manager.frame_new({title:"Trackster",type:"url",content:g+"/trackster?"+$.param(i)})})}}})},"View in new visualization":function(){l.Galaxy.modal.hide();var m=g+"/trackster?"+$.param(i);l.Galaxy.frame_manager.frame_new({title:"Trackster",type:"url",content:m})}}})}});return false}}return{HDAEditView:f}});
\ No newline at end of file
+define(["mvc/dataset/hda-model","mvc/dataset/hda-base"],function(d,a){var f=a.HDABaseView.extend(LoggableMixin).extend({initialize:function(g){a.HDABaseView.prototype.initialize.call(this,g);this.hasUser=g.hasUser;this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton,this._render_rerunButton]},_render_titleButtons:function(){return a.HDABaseView.prototype._render_titleButtons.call(this).concat([this._render_editButton(),this._render_deleteButton()])},_render_editButton:function(){if((this.model.get("state")===d.HistoryDatasetAssociation.STATES.NEW)||(this.model.get("state")===d.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===d.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var i=this.model.get("purged"),g=this.model.get("deleted"),h={title:_l("Edit Attributes"),href:this.urls.edit,target:"galaxy_main",icon_class:"edit"};if(g||i){h.enabled=false;if(i){h.title=_l("Cannot edit attributes of datasets removed from disk")}else{if(g){h.title=_l("Undelete dataset to edit attributes")}}}else{if(this.model.get("state")===d.HistoryDatasetAssociation.STATES.UPLOAD){h.disabled=true;h.title=_l("This dataset must finish uploading before it can be edited")}}h.faIcon="fa-pencil";return faIconButton(h)},_render_deleteButton:function(){if((this.model.get("state")===d.HistoryDatasetAssociation.STATES.NEW)||(this.model.get("state")===d.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var g=this,h=g.urls["delete"],i={title:_l("Delete"),href:h,icon_class:"delete",onclick:function(){g.$el.find(".menu-button.delete").trigger("mouseout");g.model["delete"]()}};if(this.model.get("deleted")||this.model.get("purged")){i={title:_l("Dataset is already deleted"),icon_class:"delete",enabled:false}}i.faIcon="fa-times";return faIconButton(i)},_render_errButton:function(){if(this.model.get("state")!==d.HistoryDatasetAssociation.STATES.ERROR){return null}return faIconButton({title:_l("View or report this error"),href:this.urls.report_error,target:"galaxy_main",faIcon:"fa-bug"})},_render_rerunButton:function(){return faIconButton({title:_l("Run this job again"),href:this.urls.rerun,target:"galaxy_main",faIcon:"fa-refresh"})},_render_visualizationsButton:function(){var g=this.model.get("visualizations");if((!this.model.hasData())||(_.isEmpty(g))){return null}if(_.isObject(g[0])){return this._render_visualizationsFrameworkButton(g)}if(!this.urls.visualization){return null}var i=this.model.get("dbkey"),l=this.urls.visualization,j={},m={dataset_id:this.model.get("id"),hda_ldda:"hda"};if(i){m.dbkey=i}var h=faIconButton({title:_l("Visualize"),href:this.urls.visualization,faIcon:"fa-bar-chart-o"});function k(n){switch(n){case"trackster":return b(l,m,i);case"scatterplot":return e(l,m);default:return function(){Galaxy.frame_manager.frame_new({title:"Visualization",type:"url",content:l+"/"+n+"?"+$.param(m)})}}}if(g.length===1){h.attr("title",g[0]);h.click(k(g[0]))}else{_.each(g,function(o){var n=o.charAt(0).toUpperCase()+o.slice(1);j[_l(n)]=k(o)});make_popupmenu(h,j)}return h},_render_visualizationsFrameworkButton:function(g){if(!(this.model.hasData())||!(g&&!_.isEmpty(g))){return null}var i=faIconButton({title:_l("Visualize"),faIcon:"fa-bar-chart-o"});i.addClass("visualize-icon");if(_.keys(g).length===1){i.attr("title",_.keys(g)[0]);i.attr("href",_.values(g)[0])}else{var j=[];_.each(g,function(k){j.push(k)});var h=new PopupMenu(i,j)}return i},_render_tagButton:function(){if(!this.hasUser||!this.urls.tags.get){return null}return faIconButton({title:_l("Edit dataset tags"),classes:"dataset-tag-btn",faIcon:"fa-tags"})},_render_annotateButton:function(){if(!this.hasUser||!this.urls.annotation.get){return null}return faIconButton({title:_l("Edit dataset annotation"),classes:"dataset-annotate-btn",faIcon:"fa-comment"})},_render_body_failed_metadata:function(){var h=$("<a/>").attr({href:this.urls.edit,target:"galaxy_main"}).text(_l("set it manually or retry auto-detection")),g=$("<span/>").text(". "+_l("You may be able to")+" ").append(h),i=a.HDABaseView.prototype._render_body_failed_metadata.call(this);i.find(".warningmessagesmall strong").append(g);return i},_render_body_error:function(){var g=a.HDABaseView.prototype._render_body_error.call(this);g.find(".dataset-actions .left").prepend(this._render_errButton());return g},_render_body_ok:function(){var g=a.HDABaseView.prototype._render_body_ok.call(this);if(this.model.isDeletedOrPurged()){return g}this.makeDbkeyEditLink(g);g.find(".dataset-actions .left").append(this._render_visualizationsButton());g.find(".dataset-actions .right").append([this._render_tagButton(),this._render_annotateButton()]);this.tagsEditor=new TagsEditor({model:this.model,el:g.find(".tags-display")}).render();return g},makeDbkeyEditLink:function(g){if(this.model.get("metadata_dbkey")==="?"&&!this.model.isDeletedOrPurged()){g.find(".dataset-dbkey .value").replaceWith($('<a target="galaxy_main">?</a>').attr("href",this.urls.edit))}},events:{"click .dataset-title-bar":"toggleBodyVisibility","click .dataset-undelete":function(g){this.model.undelete();return false},"click .dataset-unhide":function(g){this.model.unhide();return false},"click .dataset-purge":"confirmPurge","click .dataset-tag-btn":"displayTags","click .dataset-annotate-btn":"loadAndDisplayAnnotation"},confirmPurge:function c(g){this.model.purge();return false},displayTags:function(g){this.$el.find(".tags-display").slideToggle(this.fxSpeed);return false},loadAndDisplayAnnotation:function(j){this.log(this+".loadAndDisplayAnnotation",j);var i=this,h=this.$el.find(".annotation-display"),g=h.find(".annotation");if(h.is(":hidden")){if(!jQuery.trim(g.html())){var k=$.ajax(this.urls.annotation.get);k.fail(function(n,l,m){i.log("Annotation failed",n,l,m);i.trigger("error",i,n,{},_l("Annotation failed"))});k.done(function(l){l=l||"<em>"+_l("Describe or add notes to dataset")+"</em>";g.html(l);h.find("[title]").tooltip();g.make_text_editable({use_textarea:true,on_finish:function(m){g.text(m);i.model.save({annotation:m},{silent:true}).fail(function(){g.text(i.model.previous("annotation"))})}});h.slideDown(i.fxSpeed)})}else{h.slideDown(i.fxSpeed)}}else{h.slideUp(i.fxSpeed)}return false},toString:function(){var g=(this.model)?(this.model+""):("(no model)");return"HDAView("+g+")"}});function e(g,h){action=function(){Galaxy.frame_manager.frame_new({title:"Scatterplot",type:"url",content:g+"/scatterplot?"+$.param(h),location:"center"});$("div.popmenu-wrapper").remove();return false};return action}function b(g,i,h){return function(){var j={};if(h){j["f-dbkey"]=h}$.ajax({url:g+"/list_tracks?"+$.param(j),dataType:"html",error:function(){alert(("Could not add this dataset to browser")+".")},success:function(k){var l=window.parent;l.Galaxy.modal.show({title:"View Data in a New or Saved Visualization",buttons:{Cancel:function(){l.Galaxy.modal.hide()},"View in saved visualization":function(){l.Galaxy.modal.show({title:"Add Data to Saved Visualization",body:k,buttons:{Cancel:function(){l.Galaxy.modal.hide()},"Add to visualization":function(){$(l.document).find("input[name=id]:checked").each(function(){l.Galaxy.modal.hide();var m=$(this).val();i.id=m;l.Galaxy.frame_manager.frame_new({title:"Trackster",type:"url",content:g+"/trackster?"+$.param(i)})})}}})},"View in new visualization":function(){l.Galaxy.modal.hide();var m=g+"/trackster?"+$.param(i);l.Galaxy.frame_manager.frame_new({title:"Trackster",type:"url",content:m})}}})}});return false}}return{HDAEditView:f}});
\ No newline at end of file
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/packed/mvc/dataset/hda-model.js
--- a/static/scripts/packed/mvc/dataset/hda-model.js
+++ b/static/scripts/packed/mvc/dataset/hda-model.js
@@ -1,1 +1,1 @@
-define([],function(){var d=Backbone.Model.extend(LoggableMixin).extend({defaults:{history_id:null,model_class:"HistoryDatasetAssociation",hid:0,id:null,name:"(unnamed dataset)",state:"new",deleted:false,visible:true,accessible:true,purged:false,data_type:null,file_size:0,file_ext:"",meta_files:[],misc_blurb:"",misc_info:""},urlRoot:"api/histories/",url:function(){return this.urlRoot+this.get("history_id")+"/contents/"+this.get("id")},urls:function(){var i=this.get("id");if(!i){return{}}var h={purge:galaxy_config.root+"datasets/"+i+"/purge_async",display:galaxy_config.root+"datasets/"+i+"/display/?preview=True",edit:galaxy_config.root+"datasets/"+i+"/edit",download:galaxy_config.root+"datasets/"+i+"/display?to_ext="+this.get("file_ext"),report_error:galaxy_config.root+"dataset/errors?id="+i,rerun:galaxy_config.root+"tool_runner/rerun?id="+i,show_params:galaxy_config.root+"datasets/"+i+"/show_params",visualization:galaxy_config.root+"visualization",annotation:{get:galaxy_config.root+"dataset/get_annotation_async?id="+i,set:galaxy_config.root+"dataset/annotate_async?id="+i},tags:{get:galaxy_config.root+"tag/get_tagging_elt_async?item_id="+i+"&item_class=HistoryDatasetAssociation",set:galaxy_config.root+"tag/retag?item_id="+i+"&item_class=HistoryDatasetAssociation"},meta_download:galaxy_config.root+"dataset/get_metadata_file?hda_id="+i+"&metadata_name="};return h},initialize:function(h){this.log(this+".initialize",this.attributes);this.log("\tparent history_id: "+this.get("history_id"));if(!this.get("accessible")){this.set("state",d.STATES.NOT_VIEWABLE)}this._setUpListeners()},_setUpListeners:function(){this.on("change:state",function(i,h){this.log(this+" has changed state:",i,h);if(this.inReadyState()){this.trigger("state:ready",i,h,this.previous("state"))}})},toJSON:function(){var h=Backbone.Model.prototype.toJSON.call(this);h.misc_info=jQuery.trim(h.misc_info);return h},isDeletedOrPurged:function(){return(this.get("deleted")||this.get("purged"))},isVisible:function(i,j){var h=true;if((!i)&&(this.get("deleted")||this.get("purged"))){h=false}if((!j)&&(!this.get("visible"))){h=false}return h},hidden:function(){return !this.get("visible")},inReadyState:function(){var h=_.contains(d.READY_STATES,this.get("state"));return(this.isDeletedOrPurged()||h)},hasDetails:function(){return _.has(this.attributes,"genome_build")},hasData:function(){return(this.get("file_size")>0)},"delete":function c(h){return this.save({deleted:true},h)},undelete:function a(h){return this.save({deleted:false},h)},hide:function b(h){return this.save({visible:false},h)},unhide:function g(h){return this.save({visible:true},h)},purge:function f(h){h=h||{};h.url=galaxy_config.root+"datasets/"+this.get("id")+"/purge_async";var i=this,j=jQuery.ajax(h);j.done(function(m,k,l){i.set("purged",true)});j.fail(function(o,k,n){var l=_l("Unable to purge this dataset");var m=("Removal of datasets by users is not allowed in this Galaxy instance");if(o.responseJSON&&o.responseJSON.error){l=o.responseJSON.error}else{if(o.responseText.indexOf(m)!==-1){l=m}}o.responseText=l;i.trigger("error",i,o,h,_l(l),{error:l})})},searchKeys:["name","file_ext","genome_build","misc_blurb","misc_info","annotation","tags"],search:function(h){var i=this;h=h.toLowerCase();return _.filter(this.searchKeys,function(k){var j=i.get(k);return(_.isString(j)&&j.toLowerCase().indexOf(h)!==-1)})},matches:function(h){return !!this.search(h).length},toString:function(){var h=this.get("id")||"";if(this.get("name")){h=this.get("hid")+' :"'+this.get("name")+'",'+h}return"HDA("+h+")"}});d.STATES={UPLOAD:"upload",QUEUED:"queued",RUNNING:"running",SETTING_METADATA:"setting_metadata",NEW:"new",EMPTY:"empty",OK:"ok",PAUSED:"paused",FAILED_METADATA:"failed_metadata",NOT_VIEWABLE:"noPermission",DISCARDED:"discarded",ERROR:"error"};d.READY_STATES=[d.STATES.NEW,d.STATES.OK,d.STATES.EMPTY,d.STATES.PAUSED,d.STATES.FAILED_METADATA,d.STATES.NOT_VIEWABLE,d.STATES.DISCARDED,d.STATES.ERROR];d.NOT_READY_STATES=[d.STATES.UPLOAD,d.STATES.QUEUED,d.STATES.RUNNING,d.STATES.SETTING_METADATA];var e=Backbone.Collection.extend(LoggableMixin).extend({model:d,urlRoot:galaxy_config.root+"api/histories",url:function(){return this.urlRoot+"/"+this.historyId+"/contents"},initialize:function(i,h){h=h||{};this.historyId=h.historyId},ids:function(){return this.map(function(h){return h.id})},notReady:function(){return this.filter(function(h){return !h.inReadyState()})},running:function(){var h=[];this.each(function(i){if(!i.inReadyState()){h.push(i.get("id"))}});return h},getByHid:function(h){return _.first(this.filter(function(i){return i.get("hid")===h}))},getVisible:function(h,i){return this.filter(function(j){return j.isVisible(h,i)})},fetchAllDetails:function(){return this.fetch({data:{details:"all"}})},matches:function(h){return this.filter(function(i){return i.matches(h)})},set:function(j,h){var i=this;j=_.map(j,function(l){var m=i.get(l.id);if(!m){return l}var k=m.toJSON();_.extend(k,l);return k});Backbone.Collection.prototype.set.call(this,j,h)},toString:function(){return("HDACollection()")}});return{HistoryDatasetAssociation:d,HDACollection:e}});
\ No newline at end of file
+define([],function(){var d=Backbone.Model.extend(LoggableMixin).extend({defaults:{history_id:null,model_class:"HistoryDatasetAssociation",hid:0,id:null,name:"(unnamed dataset)",state:"new",deleted:false,visible:true,accessible:true,purged:false,data_type:null,file_size:0,file_ext:"",meta_files:[],misc_blurb:"",misc_info:"",tags:null},urlRoot:"api/histories/",url:function(){return this.urlRoot+this.get("history_id")+"/contents/"+this.get("id")},urls:function(){var i=this.get("id");if(!i){return{}}var h={purge:galaxy_config.root+"datasets/"+i+"/purge_async",display:galaxy_config.root+"datasets/"+i+"/display/?preview=True",edit:galaxy_config.root+"datasets/"+i+"/edit",download:galaxy_config.root+"datasets/"+i+"/display?to_ext="+this.get("file_ext"),report_error:galaxy_config.root+"dataset/errors?id="+i,rerun:galaxy_config.root+"tool_runner/rerun?id="+i,show_params:galaxy_config.root+"datasets/"+i+"/show_params",visualization:galaxy_config.root+"visualization",annotation:{get:galaxy_config.root+"dataset/get_annotation_async?id="+i,set:galaxy_config.root+"dataset/annotate_async?id="+i},tags:{get:galaxy_config.root+"tag/get_tagging_elt_async?item_id="+i+"&item_class=HistoryDatasetAssociation",set:galaxy_config.root+"tag/retag?item_id="+i+"&item_class=HistoryDatasetAssociation"},meta_download:galaxy_config.root+"dataset/get_metadata_file?hda_id="+i+"&metadata_name="};return h},initialize:function(h){this.log(this+".initialize",this.attributes);this.log("\tparent history_id: "+this.get("history_id"));if(!this.get("accessible")){this.set("state",d.STATES.NOT_VIEWABLE)}this._setUpListeners()},_setUpListeners:function(){this.on("change:state",function(i,h){this.log(this+" has changed state:",i,h);if(this.inReadyState()){this.trigger("state:ready",i,h,this.previous("state"))}})},toJSON:function(){var h=Backbone.Model.prototype.toJSON.call(this);h.misc_info=jQuery.trim(h.misc_info);return h},isDeletedOrPurged:function(){return(this.get("deleted")||this.get("purged"))},isVisible:function(i,j){var h=true;if((!i)&&(this.get("deleted")||this.get("purged"))){h=false}if((!j)&&(!this.get("visible"))){h=false}return h},hidden:function(){return !this.get("visible")},inReadyState:function(){var h=_.contains(d.READY_STATES,this.get("state"));return(this.isDeletedOrPurged()||h)},hasDetails:function(){return _.has(this.attributes,"genome_build")},hasData:function(){return(this.get("file_size")>0)},"delete":function c(h){return this.save({deleted:true},h)},undelete:function a(h){return this.save({deleted:false},h)},hide:function b(h){return this.save({visible:false},h)},unhide:function g(h){return this.save({visible:true},h)},purge:function f(h){h=h||{};h.url=galaxy_config.root+"datasets/"+this.get("id")+"/purge_async";var i=this,j=jQuery.ajax(h);j.done(function(m,k,l){i.set("purged",true)});j.fail(function(o,k,n){var l=_l("Unable to purge this dataset");var m=("Removal of datasets by users is not allowed in this Galaxy instance");if(o.responseJSON&&o.responseJSON.error){l=o.responseJSON.error}else{if(o.responseText.indexOf(m)!==-1){l=m}}o.responseText=l;i.trigger("error",i,o,h,_l(l),{error:l})})},searchKeys:["name","file_ext","genome_build","misc_blurb","misc_info","annotation","tags"],search:function(h){var i=this;h=h.toLowerCase();return _.filter(this.searchKeys,function(k){var j=i.get(k);return(_.isString(j)&&j.toLowerCase().indexOf(h)!==-1)})},matches:function(h){return !!this.search(h).length},toString:function(){var h=this.get("id")||"";if(this.get("name")){h=this.get("hid")+' :"'+this.get("name")+'",'+h}return"HDA("+h+")"}});d.STATES={UPLOAD:"upload",QUEUED:"queued",RUNNING:"running",SETTING_METADATA:"setting_metadata",NEW:"new",EMPTY:"empty",OK:"ok",PAUSED:"paused",FAILED_METADATA:"failed_metadata",NOT_VIEWABLE:"noPermission",DISCARDED:"discarded",ERROR:"error"};d.READY_STATES=[d.STATES.NEW,d.STATES.OK,d.STATES.EMPTY,d.STATES.PAUSED,d.STATES.FAILED_METADATA,d.STATES.NOT_VIEWABLE,d.STATES.DISCARDED,d.STATES.ERROR];d.NOT_READY_STATES=[d.STATES.UPLOAD,d.STATES.QUEUED,d.STATES.RUNNING,d.STATES.SETTING_METADATA];var e=Backbone.Collection.extend(LoggableMixin).extend({model:d,urlRoot:galaxy_config.root+"api/histories",url:function(){return this.urlRoot+"/"+this.historyId+"/contents"},initialize:function(i,h){h=h||{};this.historyId=h.historyId},ids:function(){return this.map(function(h){return h.id})},notReady:function(){return this.filter(function(h){return !h.inReadyState()})},running:function(){var h=[];this.each(function(i){if(!i.inReadyState()){h.push(i.get("id"))}});return h},getByHid:function(h){return _.first(this.filter(function(i){return i.get("hid")===h}))},getVisible:function(h,i){return this.filter(function(j){return j.isVisible(h,i)})},fetchAllDetails:function(){return this.fetch({data:{details:"all"}})},matches:function(h){return this.filter(function(i){return i.matches(h)})},set:function(j,h){var i=this;j=_.map(j,function(l){var m=i.get(l.id);if(!m){return l}var k=m.toJSON();_.extend(k,l);return k});Backbone.Collection.prototype.set.call(this,j,h)},toString:function(){return("HDACollection()")}});return{HistoryDatasetAssociation:d,HDACollection:e}});
\ No newline at end of file
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/packed/mvc/history/history-panel.js
--- a/static/scripts/packed/mvc/history/history-panel.js
+++ b/static/scripts/packed/mvc/history/history-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/history/history-model","mvc/dataset/hda-base","mvc/dataset/hda-edit"],function(d,b,a){var c=Backbone.View.extend(LoggableMixin).extend({HDAView:a.HDAEditView,tagName:"div",className:"history-panel",fxSpeed:"fast",datasetsSelector:".datasets-list",emptyMsgSelector:".empty-history-message",msgsSelector:".message-container",initialize:function(e){e=e||{};if(e.logger){this.logger=e.logger}this.log(this+".initialize:",e);this._setUpListeners();this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);if(this.model){this._setUpWebStorage(e.initiallyExpanded,e.show_deleted,e.show_hidden);this._setUpModelEventHandlers()}if(e.onready){e.onready.call(this)}},_setUpListeners:function(){this.on("error",function(f,i,e,h,g){this.errorHandler(f,i,e,h,g)});this.on("loading-history",function(){this.showLoadingIndicator("loading history...")});this.on("loading-done",function(){this.hideLoadingIndicator()});this.once("rendered",function(){this.trigger("rendered:initial",this);return false});this.on("switched-history current-history new-history",function(){if(_.isEmpty(this.hdaViews)){this.trigger("empty-history",this)}});if(this.logger){this.on("all",function(e){this.log(this+"",arguments)},this)}},errorHandler:function(g,j,f,i,h){var e=this._parseErrorMessage(g,j,f,i,h);if(j&&j.status===0&&j.readyState===0){}else{if(j&&j.status===502){}else{if(!this.$el.find(this.msgsSelector).is(":visible")){this.once("rendered",function(){this.displayMessage("error",e.message,e.details)})}else{this.displayMessage("error",e.message,e.details)}}}},_parseErrorMessage:function(h,l,g,k,j){var f=Galaxy.currUser,e={message:this._bePolite(k),details:{user:(f instanceof User)?(f.toJSON()):(f+""),source:(h instanceof Backbone.Model)?(h.toJSON()):(h+""),xhr:l,options:(l)?(_.omit(g,"xhr")):(g)}};_.extend(e.details,j||{});if(l&&_.isFunction(l.getAllResponseHeaders)){var i=l.getAllResponseHeaders();i=_.compact(i.split("\n"));i=_.map(i,function(m){return m.split(": ")});e.details.xhr.responseHeaders=_.object(i)}return e},_bePolite:function(e){e=e||_l("An error occurred while getting updates from the server");return e+". "+_l("Please contact a Galaxy administrator if the problem persists.")},loadCurrentHistory:function(f){var e=this;return this.loadHistoryWithHDADetails("current",f).then(function(h,g){e.trigger("current-history",e)})},switchToHistory:function(h,g){var e=this,f=function(){return jQuery.post(galaxy_config.root+"api/histories/"+h+"/set_as_current")};return this.loadHistoryWithHDADetails(h,g,f).then(function(j,i){e.trigger("switched-history",e)})},createNewHistory:function(g){var e=this,f=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,g,f).then(function(i,h){e.trigger("new-history",e)})},loadHistoryWithHDADetails:function(h,g,f,j){var e=this,i=function(k){return e.getExpandedHdaIds(k.id)};return this.loadHistory(h,g,f,j,i)},loadHistory:function(h,g,f,k,i){this.trigger("loading-history",this);g=g||{};var e=this;var j=d.History.getHistoryData(h,{historyFn:f,hdaFn:k,hdaDetailIds:g.initiallyExpanded||i});return this._loadHistoryFromXHR(j,g).fail(function(n,l,m){e.trigger("error",e,n,g,_l("An error was encountered while "+l),{historyId:h,history:m||{}})}).always(function(){e.trigger("loading-done",e)})},_loadHistoryFromXHR:function(g,f){var e=this;g.then(function(h,i){e.setModel(h,i,f)});g.fail(function(i,h){e.render()});return g},setModel:function(g,e,f){f=f||{};if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.hdas)}this.hdaViews={};if(Galaxy&&Galaxy.currUser){g.user=Galaxy.currUser.toJSON()}this.model=new d.History(g,e,f);this._setUpWebStorage(f.initiallyExpanded,f.show_deleted,f.show_hidden);this._setUpModelEventHandlers();this.trigger("new-model",this);this.render();return this},refreshHdas:function(f,e){if(this.model){return this.model.refresh(f,e)}return $.when()},_setUpWebStorage:function(f,e,g){this.storage=new PersistentStorage(this._getStorageKey(this.model.get("id")),{expandedHdas:{},show_deleted:false,show_hidden:false});this.log(this+" (prev) storage:",JSON.stringify(this.storage.get(),null,2));if(f){this.storage.set("exandedHdas",f)}if((e===true)||(e===false)){this.storage.set("show_deleted",e)}if((g===true)||(g===false)){this.storage.set("show_hidden",g)}this.show_deleted=this.storage.get("show_deleted");this.show_hidden=this.storage.get("show_hidden");this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get())},_getStorageKey:function(e){if(!e){throw new Error("_getStorageKey needs valid id: "+e)}return("history:"+e)},clearWebStorage:function(){for(var e in sessionStorage){if(e.indexOf("history:")===0){sessionStorage.removeItem(e)}}},getStoredOptions:function(f){if(!f||f==="current"){return(this.storage)?(this.storage.get()):({})}var e=sessionStorage.getItem(this._getStorageKey(f));return(e===null)?({}):(JSON.parse(e))},getExpandedHdaIds:function(e){var f=this.getStoredOptions(e).expandedHdas;return((_.isEmpty(f))?([]):(_.keys(f)))},_setUpModelEventHandlers:function(){this.model.on("error error:hdas",function(f,h,e,g){this.errorHandler(f,h,e,g)},this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("add",this.addHdaView,this);this.model.hdas.on("change:deleted",this.handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this.handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(e){this.model.fetch()},this);this.model.hdas.on("state:ready",function(f,g,e){if((!f.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[f.id])}},this)},addHdaView:function(h){this.log("add."+this,h);var f=this;if(!h.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return}$({}).queue([function g(j){var i=f.$el.find(f.emptyMsgSelector);if(i.is(":visible")){i.fadeOut(f.fxSpeed,j)}else{j()}},function e(j){f.scrollToTop();var i=f.$el.find(f.datasetsSelector);f.createHdaView(h).$el.hide().prependTo(i).slideDown(f.fxSpeed)}])},createHdaView:function(g){var f=g.get("id"),e=this.storage.get("expandedHdas").get(f),h=new this.HDAView({model:g,expanded:e,hasUser:this.model.hasUser(),logger:this.logger});this._setUpHdaListeners(h);this.hdaViews[f]=h;return h.render()},_setUpHdaListeners:function(f){var e=this;f.on("body-expanded",function(g){e.storage.get("expandedHdas").set(g,true)});f.on("body-collapsed",function(g){e.storage.get("expandedHdas").deleteKey(g)});f.on("error",function(h,j,g,i){e.errorHandler(h,j,g,i)})},handleHdaDeletionChange:function(e){if(e.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[e.id])}},handleHdaVisibleChange:function(e){if(e.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[e.id])}},removeHdaView:function(f){if(!f){return}var e=this;f.$el.fadeOut(e.fxSpeed,function(){f.off();f.remove();delete e.hdaViews[f.model.id];if(_.isEmpty(e.hdaViews)){e.$el.find(e.emptyMsgSelector).fadeIn(e.fxSpeed,function(){e.trigger("empty-history",e)})}})},render:function(g){var e=this,f;if(this.model){f=this.renderModel()}else{f=this.renderWithoutModel()}$(e).queue("fx",[function(h){if(e.$el.is(":visible")){e.$el.fadeOut(e.fxSpeed,h)}else{h()}},function(h){e.$el.empty();if(f){e.$el.append(f.children())}e.$el.fadeIn(e.fxSpeed,h)},function(h){if(g){g.call(this)}e.trigger("rendered",this)}]);return this},renderWithoutModel:function(){var e=$("<div/>"),f=$("<div/>").addClass("message-container").css({"margin-left":"4px","margin-right":"4px"});return e.append(f)},renderModel:function(){var e=$("<div/>");if(Galaxy.currUser.isAnonymous()){e.append(c.templates.anonHistoryPanel(this.model.toJSON()))}else{e.append(c.templates.historyPanel(this.model.toJSON()));e.find(".history-secondary-actions").append([this._render_tagButton(),this._render_annotateButton()])}this._setUpBehaviours(e);if(!this.model.hdas.length||!this.renderHdas(e.find(this.datasetsSelector))){e.find(this.emptyMsgSelector).show()}return e},_render_tagButton:function(){return faIconButton({title:_l("Edit history tags"),classes:"history-tag-button",faIcon:"fa-tags"})},_render_annotateButton:function(){return faIconButton({title:_l("Edit history tags"),classes:"history-annotate-button",faIcon:"fa-comment"})},_setUpBehaviours:function(e){e=e||this.$el;e.find("[title]").tooltip({placement:"bottom"});if(!this.model||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){return}var f=this,g=e.find(".history-controls .annotation-display");e.find(".history-controls .history-annotate-button").click(function(){if(g.is(":hidden")){var h=f.$el.find(".history-controls .annotation");h.text(jQuery.trim(h.text()));g.slideDown(f.fxSpeed)}else{g.slideUp(f.fxSpeed)}return false});e.find(".history-name").make_text_editable({on_finish:function(h){e.find(".history-name").text(h);f.model.save({name:h}).fail(function(){e.find(".history-name").text(f.model.previous("name"))})}});e.find(".history-controls .annotation").make_text_editable({use_textarea:true,on_finish:function(h){e.find(".history-controls .annotation").text(h);f.model.save({annotation:h}).fail(function(){e.find(".history-controls .annotation").text(f.model.previous("annotation"))})}})},renderHdas:function(f){this.hdaViews={};var e=this,g=this.model.hdas.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"));_.each(g,function(h){f.prepend(e.createHdaView(h).$el)});return g.length},events:{"click .history-tag-button":"loadAndDisplayTags","click .message-container":"clearMessages"},updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(e){e.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},toggleShowDeleted:function(){this.storage.set("show_deleted",!this.storage.get("show_deleted"));this.render();return this.storage.get("show_deleted")},toggleShowHidden:function(){this.storage.set("show_hidden",!this.storage.get("show_hidden"));this.render();return this.storage.get("show_hidden")},loadAndDisplayTags:function(g){var e=this,f=this.$el.find(".history-controls .tags-display"),h=f.find(".tags");if(f.is(":hidden")){if(!jQuery.trim(h.html())){var i=jQuery.ajax(e.model.tagUrl());i.fail(function(l,j,k){e.log("Error loading tag area html",l,k,j);e.trigger("error",e,l,null,_l("Error loading tags"))});i.done(function(j){h.html(j);h.find("[title]").tooltip();f.slideDown(e.fxSpeed)})}else{f.slideDown(e.fxSpeed)}}else{f.slideUp(e.fxSpeed)}return false},showLoadingIndicator:function(f,e,g){e=(e!==undefined)?(e):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,g)}else{this.$el.fadeOut(e);this.indicator.show(f,e,g)}},hideLoadingIndicator:function(e,f){e=(e!==undefined)?(e):(this.fxSpeed);if(this.indicator){this.indicator.hide(e,f)}},displayMessage:function(j,k,i){var g=this;this.scrollToTop();var h=this.$el.find(this.msgsSelector),e=$("<div/>").addClass(j+"message").html(k);if(!_.isEmpty(i)){var f=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(g.messageToModalOptions(j,k,i));return false});e.append(" ",f)}return h.html(e)},messageToModalOptions:function(i,k,h){var e=this,j=$("<div/>"),g={title:"Details"};function f(l){l=_.omit(l,_.functions(l));return["<table>",_.map(l,function(n,m){n=(_.isObject(n))?(f(n)):(n);return'<tr><td style="vertical-align: top; color: grey">'+m+'</td><td style="padding-left: 8px">'+n+"</td></tr>"}).join(""),"</table>"].join("")}if(_.isObject(h)){g.body=j.append(f(h))}else{g.body=j.html(h)}g.buttons={Ok:function(){Galaxy.modal.hide();e.clearMessages()}};return g},clearMessages:function(){var e=this.$el.find(this.msgsSelector);e.empty()},scrollPosition:function(){return this.$el.parent().scrollTop()},scrollTo:function(e){this.$el.parent().scrollTop(e)},scrollToTop:function(){this.$el.parent().scrollTop(0);return this},scrollIntoView:function(f,g){if(!g){this.$el.parent().parent().scrollTop(f);return this}var e=window,h=this.$el.parent().parent(),j=$(e).innerHeight(),i=(j/2)-(g/2);$(h).scrollTop(f-i);return this},scrollToId:function(f){if((!f)||(!this.hdaViews[f])){return this}var e=this.hdaViews[f].$el;this.scrollIntoView(e.offset().top,e.outerHeight());return this},scrollToHid:function(e){var f=this.model.hdas.getByHid(e);if(!f){return this}return this.scrollToId(f.id)},connectToQuotaMeter:function(e){if(!e){return this}this.listenTo(e,"quota:over",this.showQuotaMessage);this.listenTo(e,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(e&&e.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var e=this.$el.find(".quota-message");if(e.is(":hidden")){e.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var e=this.$el.find(".quota-message");if(!e.is(":hidden")){e.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(e){if(!e){return this}this.on("new-storage",function(g,f){if(e&&g){e.findItemByHtml(_l("Include Deleted Datasets")).checked=g.get("show_deleted");e.findItemByHtml(_l("Include Hidden Datasets")).checked=g.get("show_hidden")}});return this},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});c.templates={historyPanel:Handlebars.templates["template-history-historyPanel"],anonHistoryPanel:Handlebars.templates["template-history-historyPanel-anon"]};return{HistoryPanel:c}});
\ No newline at end of file
+define(["mvc/history/history-model","mvc/dataset/hda-base","mvc/dataset/hda-edit"],function(d,b,a){var c=Backbone.View.extend(LoggableMixin).extend({HDAView:a.HDAEditView,tagName:"div",className:"history-panel",fxSpeed:"fast",datasetsSelector:".datasets-list",emptyMsgSelector:".empty-history-message",msgsSelector:".message-container",initialize:function(e){e=e||{};if(e.logger){this.logger=e.logger}this.log(this+".initialize:",e);this._setUpListeners();this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);if(this.model){this._setUpWebStorage(e.initiallyExpanded,e.show_deleted,e.show_hidden);this._setUpModelEventHandlers()}if(e.onready){e.onready.call(this)}},_setUpListeners:function(){this.on("error",function(f,i,e,h,g){this.errorHandler(f,i,e,h,g)});this.on("loading-history",function(){this.showLoadingIndicator("loading history...")});this.on("loading-done",function(){this.hideLoadingIndicator()});this.once("rendered",function(){this.trigger("rendered:initial",this);return false});this.on("switched-history current-history new-history",function(){if(_.isEmpty(this.hdaViews)){this.trigger("empty-history",this)}});if(this.logger){this.on("all",function(e){this.log(this+"",arguments)},this)}},errorHandler:function(g,j,f,i,h){var e=this._parseErrorMessage(g,j,f,i,h);if(j&&j.status===0&&j.readyState===0){}else{if(j&&j.status===502){}else{if(!this.$el.find(this.msgsSelector).is(":visible")){this.once("rendered",function(){this.displayMessage("error",e.message,e.details)})}else{this.displayMessage("error",e.message,e.details)}}}},_parseErrorMessage:function(h,l,g,k,j){var f=Galaxy.currUser,e={message:this._bePolite(k),details:{user:(f instanceof User)?(f.toJSON()):(f+""),source:(h instanceof Backbone.Model)?(h.toJSON()):(h+""),xhr:l,options:(l)?(_.omit(g,"xhr")):(g)}};_.extend(e.details,j||{});if(l&&_.isFunction(l.getAllResponseHeaders)){var i=l.getAllResponseHeaders();i=_.compact(i.split("\n"));i=_.map(i,function(m){return m.split(": ")});e.details.xhr.responseHeaders=_.object(i)}return e},_bePolite:function(e){e=e||_l("An error occurred while getting updates from the server");return e+". "+_l("Please contact a Galaxy administrator if the problem persists.")},loadCurrentHistory:function(f){var e=this;return this.loadHistoryWithHDADetails("current",f).then(function(h,g){e.trigger("current-history",e)})},switchToHistory:function(h,g){var e=this,f=function(){return jQuery.post(galaxy_config.root+"api/histories/"+h+"/set_as_current")};return this.loadHistoryWithHDADetails(h,g,f).then(function(j,i){e.trigger("switched-history",e)})},createNewHistory:function(g){var e=this,f=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,g,f).then(function(i,h){e.trigger("new-history",e)})},loadHistoryWithHDADetails:function(h,g,f,j){var e=this,i=function(k){return e.getExpandedHdaIds(k.id)};return this.loadHistory(h,g,f,j,i)},loadHistory:function(h,g,f,k,i){this.trigger("loading-history",this);g=g||{};var e=this;var j=d.History.getHistoryData(h,{historyFn:f,hdaFn:k,hdaDetailIds:g.initiallyExpanded||i});return this._loadHistoryFromXHR(j,g).fail(function(n,l,m){e.trigger("error",e,n,g,_l("An error was encountered while "+l),{historyId:h,history:m||{}})}).always(function(){e.trigger("loading-done",e)})},_loadHistoryFromXHR:function(g,f){var e=this;g.then(function(h,i){e.setModel(h,i,f)});g.fail(function(i,h){e.render()});return g},setModel:function(g,e,f){f=f||{};if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.hdas)}this.hdaViews={};if(Galaxy&&Galaxy.currUser){g.user=Galaxy.currUser.toJSON()}this.model=new d.History(g,e,f);this._setUpWebStorage(f.initiallyExpanded,f.show_deleted,f.show_hidden);this._setUpModelEventHandlers();this.trigger("new-model",this);this.render();return this},refreshHdas:function(f,e){if(this.model){return this.model.refresh(f,e)}return $.when()},_setUpWebStorage:function(f,e,g){this.storage=new PersistentStorage(this._getStorageKey(this.model.get("id")),{expandedHdas:{},show_deleted:false,show_hidden:false});this.log(this+" (prev) storage:",JSON.stringify(this.storage.get(),null,2));if(f){this.storage.set("exandedHdas",f)}if((e===true)||(e===false)){this.storage.set("show_deleted",e)}if((g===true)||(g===false)){this.storage.set("show_hidden",g)}this.show_deleted=this.storage.get("show_deleted");this.show_hidden=this.storage.get("show_hidden");this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get())},_getStorageKey:function(e){if(!e){throw new Error("_getStorageKey needs valid id: "+e)}return("history:"+e)},clearWebStorage:function(){for(var e in sessionStorage){if(e.indexOf("history:")===0){sessionStorage.removeItem(e)}}},getStoredOptions:function(f){if(!f||f==="current"){return(this.storage)?(this.storage.get()):({})}var e=sessionStorage.getItem(this._getStorageKey(f));return(e===null)?({}):(JSON.parse(e))},getExpandedHdaIds:function(e){var f=this.getStoredOptions(e).expandedHdas;return((_.isEmpty(f))?([]):(_.keys(f)))},_setUpModelEventHandlers:function(){this.model.on("error error:hdas",function(f,h,e,g){this.errorHandler(f,h,e,g)},this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("add",this.addHdaView,this);this.model.hdas.on("change:deleted",this.handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this.handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(e){this.model.fetch()},this);this.model.hdas.on("state:ready",function(f,g,e){if((!f.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[f.id])}},this)},addHdaView:function(h){this.log("add."+this,h);var f=this;if(!h.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return}$({}).queue([function g(j){var i=f.$el.find(f.emptyMsgSelector);if(i.is(":visible")){i.fadeOut(f.fxSpeed,j)}else{j()}},function e(j){f.scrollToTop();var i=f.$el.find(f.datasetsSelector);f.createHdaView(h).$el.hide().prependTo(i).slideDown(f.fxSpeed)}])},createHdaView:function(g){var f=g.get("id"),e=this.storage.get("expandedHdas").get(f),h=new this.HDAView({model:g,expanded:e,hasUser:this.model.hasUser(),logger:this.logger});this._setUpHdaListeners(h);this.hdaViews[f]=h;return h.render()},_setUpHdaListeners:function(f){var e=this;f.on("body-expanded",function(g){e.storage.get("expandedHdas").set(g,true)});f.on("body-collapsed",function(g){e.storage.get("expandedHdas").deleteKey(g)});f.on("error",function(h,j,g,i){e.errorHandler(h,j,g,i)})},handleHdaDeletionChange:function(e){if(e.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[e.id])}},handleHdaVisibleChange:function(e){if(e.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[e.id])}},removeHdaView:function(f){if(!f){return}var e=this;f.$el.fadeOut(e.fxSpeed,function(){f.off();f.remove();delete e.hdaViews[f.model.id];if(_.isEmpty(e.hdaViews)){e.$el.find(e.emptyMsgSelector).fadeIn(e.fxSpeed,function(){e.trigger("empty-history",e)})}})},render:function(g){var e=this,f;if(this.model){f=this.renderModel()}else{f=this.renderWithoutModel()}$(e).queue("fx",[function(h){if(e.$el.is(":visible")){e.$el.fadeOut(e.fxSpeed,h)}else{h()}},function(h){e.$el.empty();if(f){e.$el.append(f.children())}e.$el.fadeIn(e.fxSpeed,h)},function(h){if(g){g.call(this)}e.trigger("rendered",this)}]);return this},renderWithoutModel:function(){var e=$("<div/>"),f=$("<div/>").addClass("message-container").css({"margin-left":"4px","margin-right":"4px"});return e.append(f)},renderModel:function(){var e=$("<div/>");if(Galaxy.currUser.isAnonymous()){e.append(c.templates.anonHistoryPanel(this.model.toJSON()))}else{e.append(c.templates.historyPanel(this.model.toJSON()));e.find(".history-secondary-actions").append([this._render_tagButton(),this._render_annotateButton()]);this.tagsEditor=new TagsEditor({model:this.model,el:e.find(".history-controls .tags-display")}).render()}this._setUpBehaviours(e);if(!this.model.hdas.length||!this.renderHdas(e.find(this.datasetsSelector))){e.find(this.emptyMsgSelector).show()}return e},_render_tagButton:function(){return faIconButton({title:_l("Edit history tags"),classes:"history-tag-button",faIcon:"fa-tags"})},_render_annotateButton:function(){return faIconButton({title:_l("Edit history tags"),classes:"history-annotate-button",faIcon:"fa-comment"})},_setUpBehaviours:function(e){e=e||this.$el;e.find("[title]").tooltip({placement:"bottom"});if(!this.model||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){return}var f=this,g=e.find(".history-controls .annotation-display");e.find(".history-controls .history-annotate-button").click(function(){if(g.is(":hidden")){var h=f.$el.find(".history-controls .annotation");h.text(jQuery.trim(h.text()));g.slideDown(f.fxSpeed)}else{g.slideUp(f.fxSpeed)}return false});e.find(".history-name").make_text_editable({on_finish:function(h){e.find(".history-name").text(h);f.model.save({name:h}).fail(function(){e.find(".history-name").text(f.model.previous("name"))})}});e.find(".history-controls .annotation").make_text_editable({use_textarea:true,on_finish:function(h){e.find(".history-controls .annotation").text(h);f.model.save({annotation:h}).fail(function(){e.find(".history-controls .annotation").text(f.model.previous("annotation"))})}})},renderHdas:function(f){this.hdaViews={};var e=this,g=this.model.hdas.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"));_.each(g,function(h){f.prepend(e.createHdaView(h).$el)});return g.length},events:{"click .history-tag-button":"displayTags","click .message-container":"clearMessages"},updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(e){e.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},toggleShowDeleted:function(){this.storage.set("show_deleted",!this.storage.get("show_deleted"));this.render();return this.storage.get("show_deleted")},toggleShowHidden:function(){this.storage.set("show_hidden",!this.storage.get("show_hidden"));this.render();return this.storage.get("show_hidden")},displayTags:function(e){this.$el.find(".history-controls .tags-display").slideToggle(this.fxSpeed);return false},showLoadingIndicator:function(f,e,g){e=(e!==undefined)?(e):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,g)}else{this.$el.fadeOut(e);this.indicator.show(f,e,g)}},hideLoadingIndicator:function(e,f){e=(e!==undefined)?(e):(this.fxSpeed);if(this.indicator){this.indicator.hide(e,f)}},displayMessage:function(j,k,i){var g=this;this.scrollToTop();var h=this.$el.find(this.msgsSelector),e=$("<div/>").addClass(j+"message").html(k);if(!_.isEmpty(i)){var f=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(g.messageToModalOptions(j,k,i));return false});e.append(" ",f)}return h.html(e)},messageToModalOptions:function(i,k,h){var e=this,j=$("<div/>"),g={title:"Details"};function f(l){l=_.omit(l,_.functions(l));return["<table>",_.map(l,function(n,m){n=(_.isObject(n))?(f(n)):(n);return'<tr><td style="vertical-align: top; color: grey">'+m+'</td><td style="padding-left: 8px">'+n+"</td></tr>"}).join(""),"</table>"].join("")}if(_.isObject(h)){g.body=j.append(f(h))}else{g.body=j.html(h)}g.buttons={Ok:function(){Galaxy.modal.hide();e.clearMessages()}};return g},clearMessages:function(){var e=this.$el.find(this.msgsSelector);e.empty()},scrollPosition:function(){return this.$el.parent().scrollTop()},scrollTo:function(e){this.$el.parent().scrollTop(e)},scrollToTop:function(){this.$el.parent().scrollTop(0);return this},scrollIntoView:function(f,g){if(!g){this.$el.parent().parent().scrollTop(f);return this}var e=window,h=this.$el.parent().parent(),j=$(e).innerHeight(),i=(j/2)-(g/2);$(h).scrollTop(f-i);return this},scrollToId:function(f){if((!f)||(!this.hdaViews[f])){return this}var e=this.hdaViews[f].$el;this.scrollIntoView(e.offset().top,e.outerHeight());return this},scrollToHid:function(e){var f=this.model.hdas.getByHid(e);if(!f){return this}return this.scrollToId(f.id)},connectToQuotaMeter:function(e){if(!e){return this}this.listenTo(e,"quota:over",this.showQuotaMessage);this.listenTo(e,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(e&&e.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var e=this.$el.find(".quota-message");if(e.is(":hidden")){e.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var e=this.$el.find(".quota-message");if(!e.is(":hidden")){e.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(e){if(!e){return this}this.on("new-storage",function(g,f){if(e&&g){e.findItemByHtml(_l("Include Deleted Datasets")).checked=g.get("show_deleted");e.findItemByHtml(_l("Include Hidden Datasets")).checked=g.get("show_hidden")}});return this},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});c.templates={historyPanel:Handlebars.templates["template-history-historyPanel"],anonHistoryPanel:Handlebars.templates["template-history-historyPanel-anon"]};return{HistoryPanel:c}});
\ No newline at end of file
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/packed/mvc/tags.js
--- a/static/scripts/packed/mvc/tags.js
+++ b/static/scripts/packed/mvc/tags.js
@@ -1,1 +1,1 @@
-var Tag=BaseModel.extend(LoggableMixin).extend({logger:console,defaults:{id:null,itemClass:null},toString:function(){return"Tag()"}});var TagView=BaseView.extend(LoggableMixin).extend({logger:console,toString:function(){return"TagView()"}});var TagCollection=Backbone.Collection.extend(LoggableMixin).extend({model:Tag,logger:console,toString:function(){return"TagCollection()"}});var TagList=BaseView.extend(LoggableMixin).extend({logger:console,toString:function(){return"TagList()"}});
\ No newline at end of file
+var TagsEditor=Backbone.View.extend(LoggableMixin).extend({tagName:"div",className:"tags-display",initialize:function(a){this.listenTo(this.model,"change:tags",function(){this.render()})},render:function(){var a=this;this.$el.html(this.template());this.$el.find(".tags-input").select2({placeholder:"Add tags",width:"100%",tags:function(){return a.getTagsUsed()}});this._behaviors();return this},template:function(){return['<label class="prompt">',_l("Tags"),"</label>",'<input class="tags-input" value="',this.tagsToCSV(this.model.get("tags")),'" />'].join("")},tagsToCSV:function(a){if(!_.isArray(a)||_.isEmpty(a)){return""}return a.sort().join(",")},getTagsUsed:function(){return _.map(Galaxy.currUser.get("tags_used"),function(a){return{id:a,text:a}})},_behaviors:function(){var a=this;this.$el.find(".tags-input").on("change",function(b){a.model.save({tags:b.val},{silent:true});if(b.added){a.addNewTagToTagsUsed(b.added.text)}})},addNewTagToTagsUsed:function(a){var b=Galaxy.currUser.get("tags_used");if(!_.contains(b,a)){b.push(a);b.sort();Galaxy.currUser.set("tags_used",b)}},remove:function(){this.$el.off();Backbone.View.prototype.remove.call(this)},toString:function(){return["TagSetView(",this.model+"",")"].join("")}});
\ No newline at end of file
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/packed/templates/compiled/history-templates.js
--- a/static/scripts/packed/templates/compiled/history-templates.js
+++ b/static/scripts/packed/templates/compiled/history-templates.js
@@ -1,1 +1,1 @@
-(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-body"]=b(function(j,v,t,o,C){this.compilerInfo=[4,">= 1.0.0"];t=this.merge(t,j.helpers);C=C||{};var u="",l,e="function",d=this.escapeExpression,s=this,c=t.blockHelperMissing;function r(G,F){var D="",E;D+='\n <div class="dataset-summary">\n ';if(E=t.body){E=E.call(G,{hash:{},data:F})}else{E=G.body;E=typeof E===e?E.apply(G):E}if(E||E===0){D+=E}D+='\n </div>\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';return D}function q(G,F){var D="",E;D+='\n <div class="dataset-summary">\n ';E=t["if"].call(G,G.misc_blurb,{hash:{},inverse:s.noop,fn:s.program(4,p,F),data:F});if(E||E===0){D+=E}D+="\n\n ";E=t["if"].call(G,G.data_type,{hash:{},inverse:s.noop,fn:s.program(6,n,F),data:F});if(E||E===0){D+=E}D+="\n\n ";E=t["if"].call(G,G.metadata_dbkey,{hash:{},inverse:s.noop,fn:s.program(9,i,F),data:F});if(E||E===0){D+=E}D+="\n\n ";E=t["if"].call(G,G.misc_info,{hash:{},inverse:s.noop,fn:s.program(12,A,F),data:F});if(E||E===0){D+=E}D+='\n </div>\n\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';E=t.unless.call(G,G.deleted,{hash:{},inverse:s.noop,fn:s.program(14,z,F),data:F});if(E||E===0){D+=E}D+="\n\n ";return D}function p(G,F){var D="",E;D+='\n <div class="dataset-blurb">\n <span class="value">';if(E=t.misc_blurb){E=E.call(G,{hash:{},data:F})}else{E=G.misc_blurb;E=typeof E===e?E.apply(G):E}D+=d(E)+"</span>\n </div>\n ";return D}function n(H,G){var D="",F,E;D+='\n <div class="dataset-datatype">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(7,m,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <span class="value">';if(F=t.data_type){F=F.call(H,{hash:{},data:G})}else{F=H.data_type;F=typeof F===e?F.apply(H):F}D+=d(F)+"</span>\n </div>\n ";return D}function m(E,D){return"format"}function i(H,G){var D="",F,E;D+='\n <div class="dataset-dbkey">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(10,B,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <span class="value">\n ';if(F=t.metadata_dbkey){F=F.call(H,{hash:{},data:G})}else{F=H.metadata_dbkey;F=typeof F===e?F.apply(H):F}D+=d(F)+"\n </span>\n </div>\n ";return D}function B(E,D){return"database"}function A(G,F){var D="",E;D+='\n <div class="dataset-info">\n <span class="value">';if(E=t.misc_info){E=E.call(G,{hash:{},data:F})}else{E=G.misc_info;E=typeof E===e?E.apply(G):E}D+=d(E)+"</span>\n </div>\n ";return D}function z(H,G){var D="",F,E;D+='\n \n <div class="tags-display">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(15,y,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <div class="tags"></div>\n </div>\n\n \n <div class="annotation-display">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(17,x,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <div id="dataset-';if(F=t.id){F=F.call(H,{hash:{},data:G})}else{F=H.id;F=typeof F===e?F.apply(H):F}D+=d(F)+'-annotation" class="annotation editable-text"\n title="';E={hash:{},inverse:s.noop,fn:s.program(19,w,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='"></div>\n </div>\n\n <div class="dataset-display-applications">\n ';F=t.each.call(H,H.display_apps,{hash:{},inverse:s.noop,fn:s.program(21,k,G),data:G});if(F||F===0){D+=F}D+="\n\n ";F=t.each.call(H,H.display_types,{hash:{},inverse:s.noop,fn:s.program(21,k,G),data:G});if(F||F===0){D+=F}D+='\n </div>\n\n <div class="dataset-peek">\n ';F=t["if"].call(H,H.peek,{hash:{},inverse:s.noop,fn:s.program(25,f,G),data:G});if(F||F===0){D+=F}D+="\n </div>\n\n ";return D}function y(E,D){return"Tags"}function x(E,D){return"Annotation"}function w(E,D){return"Edit dataset annotation"}function k(G,F){var D="",E;D+='\n <div class="display-application">\n <span class="display-application-location">';if(E=t.label){E=E.call(G,{hash:{},data:F})}else{E=G.label;E=typeof E===e?E.apply(G):E}D+=d(E)+'</span>\n <span class="display-application-links">\n ';E=t.each.call(G,G.links,{hash:{},inverse:s.noop,fn:s.program(22,h,F),data:F});if(E||E===0){D+=E}D+="\n </span>\n </div>\n ";return D}function h(H,G){var D="",F,E;D+='\n <a target="';if(F=t.target){F=F.call(H,{hash:{},data:G})}else{F=H.target;F=typeof F===e?F.apply(H):F}D+=d(F)+'" href="';if(F=t.href){F=F.call(H,{hash:{},data:G})}else{F=H.href;F=typeof F===e?F.apply(H):F}D+=d(F)+'">';E={hash:{},inverse:s.noop,fn:s.program(23,g,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+="</a>\n ";return D}function g(F,E){var D;if(D=t.text){D=D.call(F,{hash:{},data:E})}else{D=F.text;D=typeof D===e?D.apply(F):D}return d(D)}function f(G,F){var D="",E;D+='\n <pre class="peek">';if(E=t.peek){E=E.call(G,{hash:{},data:F})}else{E=G.peek;E=typeof E===e?E.apply(G):E}if(E||E===0){D+=E}D+="</pre>\n ";return D}u+='<div class="dataset-body">\n ';l=t["if"].call(v,v.body,{hash:{},inverse:s.program(3,q,C),fn:s.program(1,r,C),data:C});if(l||l===0){u+=l}u+="\n</div>";return u})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-body"]=b(function(j,v,t,o,C){this.compilerInfo=[4,">= 1.0.0"];t=this.merge(t,j.helpers);C=C||{};var u="",l,e="function",d=this.escapeExpression,s=this,c=t.blockHelperMissing;function r(G,F){var D="",E;D+='\n <div class="dataset-summary">\n ';if(E=t.body){E=E.call(G,{hash:{},data:F})}else{E=G.body;E=typeof E===e?E.apply(G):E}if(E||E===0){D+=E}D+='\n </div>\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';return D}function q(G,F){var D="",E;D+='\n <div class="dataset-summary">\n ';E=t["if"].call(G,G.misc_blurb,{hash:{},inverse:s.noop,fn:s.program(4,p,F),data:F});if(E||E===0){D+=E}D+="\n\n ";E=t["if"].call(G,G.data_type,{hash:{},inverse:s.noop,fn:s.program(6,n,F),data:F});if(E||E===0){D+=E}D+="\n\n ";E=t["if"].call(G,G.metadata_dbkey,{hash:{},inverse:s.noop,fn:s.program(9,i,F),data:F});if(E||E===0){D+=E}D+="\n\n ";E=t["if"].call(G,G.misc_info,{hash:{},inverse:s.noop,fn:s.program(12,A,F),data:F});if(E||E===0){D+=E}D+='\n </div>\n\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';E=t.unless.call(G,G.deleted,{hash:{},inverse:s.noop,fn:s.program(14,z,F),data:F});if(E||E===0){D+=E}D+="\n\n ";return D}function p(G,F){var D="",E;D+='\n <div class="dataset-blurb">\n <span class="value">';if(E=t.misc_blurb){E=E.call(G,{hash:{},data:F})}else{E=G.misc_blurb;E=typeof E===e?E.apply(G):E}D+=d(E)+"</span>\n </div>\n ";return D}function n(H,G){var D="",F,E;D+='\n <div class="dataset-datatype">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(7,m,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <span class="value">';if(F=t.data_type){F=F.call(H,{hash:{},data:G})}else{F=H.data_type;F=typeof F===e?F.apply(H):F}D+=d(F)+"</span>\n </div>\n ";return D}function m(E,D){return"format"}function i(H,G){var D="",F,E;D+='\n <div class="dataset-dbkey">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(10,B,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <span class="value">\n ';if(F=t.metadata_dbkey){F=F.call(H,{hash:{},data:G})}else{F=H.metadata_dbkey;F=typeof F===e?F.apply(H):F}D+=d(F)+"\n </span>\n </div>\n ";return D}function B(E,D){return"database"}function A(G,F){var D="",E;D+='\n <div class="dataset-info">\n <span class="value">';if(E=t.misc_info){E=E.call(G,{hash:{},data:F})}else{E=G.misc_info;E=typeof E===e?E.apply(G):E}D+=d(E)+"</span>\n </div>\n ";return D}function z(H,G){var D="",F,E;D+='\n \n <div class="tags-display">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(15,y,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <div class="tags"></div>\n </div>\n\n \n <div class="annotation-display">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(17,x,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <div id="dataset-';if(F=t.id){F=F.call(H,{hash:{},data:G})}else{F=H.id;F=typeof F===e?F.apply(H):F}D+=d(F)+'-annotation" class="annotation editable-text"\n title="';E={hash:{},inverse:s.noop,fn:s.program(19,w,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='"></div>\n </div>\n\n <div class="dataset-display-applications">\n ';F=t.each.call(H,H.display_apps,{hash:{},inverse:s.noop,fn:s.program(21,k,G),data:G});if(F||F===0){D+=F}D+="\n\n ";F=t.each.call(H,H.display_types,{hash:{},inverse:s.noop,fn:s.program(21,k,G),data:G});if(F||F===0){D+=F}D+='\n </div>\n\n <div class="dataset-peek">\n ';F=t["if"].call(H,H.peek,{hash:{},inverse:s.noop,fn:s.program(25,f,G),data:G});if(F||F===0){D+=F}D+="\n </div>\n\n ";return D}function y(E,D){return"Tags"}function x(E,D){return"Annotation"}function w(E,D){return"Edit dataset annotation"}function k(G,F){var D="",E;D+='\n <div class="display-application">\n <span class="display-application-location">';if(E=t.label){E=E.call(G,{hash:{},data:F})}else{E=G.label;E=typeof E===e?E.apply(G):E}D+=d(E)+'</span>\n <span class="display-application-links">\n ';E=t.each.call(G,G.links,{hash:{},inverse:s.noop,fn:s.program(22,h,F),data:F});if(E||E===0){D+=E}D+="\n </span>\n </div>\n ";return D}function h(H,G){var D="",F,E;D+='\n <a target="';if(F=t.target){F=F.call(H,{hash:{},data:G})}else{F=H.target;F=typeof F===e?F.apply(H):F}D+=d(F)+'" href="';if(F=t.href){F=F.call(H,{hash:{},data:G})}else{F=H.href;F=typeof F===e?F.apply(H):F}D+=d(F)+'">';E={hash:{},inverse:s.noop,fn:s.program(23,g,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+="</a>\n ";return D}function g(F,E){var D;if(D=t.text){D=D.call(F,{hash:{},data:E})}else{D=F.text;D=typeof D===e?D.apply(F):D}return d(D)}function f(G,F){var D="",E;D+='\n <pre class="peek">';if(E=t.peek){E=E.call(G,{hash:{},data:F})}else{E=G.peek;E=typeof E===e?E.apply(G):E}if(E||E===0){D+=E}D+="</pre>\n ";return D}u+='<div class="dataset-body">\n ';l=t["if"].call(v,v.body,{hash:{},inverse:s.program(3,q,C),fn:s.program(1,r,C),data:C});if(l||l===0){u+=l}u+="\n</div>";return u})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-skeleton"]=b(function(f,r,p,k,w){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,f.helpers);w=w||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var x="",z,y;x+='\n <div class="errormessagesmall">\n ';y={hash:{},inverse:o.noop,fn:o.program(2,m,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+=":\n ";y={hash:{},inverse:o.noop,fn:o.program(4,l,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </div>\n ";return x}function m(y,x){return"There was an error getting the data for this dataset"}function l(z,y){var x;if(x=p.error){x=x.call(z,{hash:{},data:y})}else{x=z.error;x=typeof x===e?x.apply(z):x}return d(x)}function j(A,z){var x="",y;x+="\n ";y=p["if"].call(A,A.purged,{hash:{},inverse:o.program(10,v,z),fn:o.program(7,i,z),data:z});if(y||y===0){x+=y}x+="\n ";return x}function i(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(8,g,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n\n ";return x}function g(y,x){return"This dataset has been deleted and removed from disk."}function v(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(11,u,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n \n Click <a href="javascript:void(0);" class="dataset-undelete">here</a> to undelete it\n or <a href="javascript:void(0);" class="dataset-purge">here</a> to immediately remove it from disk\n </strong></div>\n ';return x}function u(y,x){return"This dataset has been deleted."}function t(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(14,s,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n Click <a href="javascript:void(0);" class="dataset-unhide">here</a> to unhide it\n </strong></div>\n ';return x}function s(y,x){return"This dataset has been hidden."}q+='<div class="dataset hda">\n <div class="dataset-warnings">\n ';h=p["if"].call(r,r.error,{hash:{},inverse:o.noop,fn:o.program(1,n,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(6,j,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p.unless.call(r,r.visible,{hash:{},inverse:o.noop,fn:o.program(13,t,w),data:w});if(h||h===0){q+=h}q+='\n </div>\n\n <div class="dataset-primary-actions"></div>\n <div class="dataset-title-bar clear">\n <span class="dataset-state-icon state-icon"></span>\n <div class="dataset-title">\n <span class="hda-hid">';if(h=p.hid){h=h.call(r,{hash:{},data:w})}else{h=r.hid;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n <span class="dataset-name">';if(h=p.name){h=h.call(r,{hash:{},data:w})}else{h=r.name;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n </div>\n </div>\n\n <div class="dataset-body"></div>\n</div>';return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-skeleton"]=b(function(f,r,p,k,w){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,f.helpers);w=w||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var x="",z,y;x+='\n <div class="errormessagesmall">\n ';y={hash:{},inverse:o.noop,fn:o.program(2,m,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+=":\n ";y={hash:{},inverse:o.noop,fn:o.program(4,l,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </div>\n ";return x}function m(y,x){return"There was an error getting the data for this dataset"}function l(z,y){var x;if(x=p.error){x=x.call(z,{hash:{},data:y})}else{x=z.error;x=typeof x===e?x.apply(z):x}return d(x)}function j(A,z){var x="",y;x+="\n ";y=p["if"].call(A,A.purged,{hash:{},inverse:o.program(10,v,z),fn:o.program(7,i,z),data:z});if(y||y===0){x+=y}x+="\n ";return x}function i(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(8,g,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n\n ";return x}function g(y,x){return"This dataset has been deleted and removed from disk."}function v(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(11,u,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n \n Click <a href="javascript:void(0);" class="dataset-undelete">here</a> to undelete it\n or <a href="javascript:void(0);" class="dataset-purge">here</a> to immediately remove it from disk\n </strong></div>\n ';return x}function u(y,x){return"This dataset has been deleted."}function t(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(14,s,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n Click <a href="javascript:void(0);" class="dataset-unhide">here</a> to unhide it\n </strong></div>\n ';return x}function s(y,x){return"This dataset has been hidden."}q+='<div class="dataset hda">\n <div class="dataset-warnings">\n ';h=p["if"].call(r,r.error,{hash:{},inverse:o.noop,fn:o.program(1,n,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(6,j,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p.unless.call(r,r.visible,{hash:{},inverse:o.noop,fn:o.program(13,t,w),data:w});if(h||h===0){q+=h}q+='\n </div>\n\n <div class="dataset-primary-actions"></div>\n <div class="dataset-title-bar clear">\n <span class="dataset-state-icon state-icon"></span>\n <div class="dataset-title">\n <span class="hda-hid">';if(h=p.hid){h=h.call(r,{hash:{},data:w})}else{h=r.hid;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n <span class="dataset-name">';if(h=p.name){h=h.call(r,{hash:{},data:w})}else{h=r.name;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n </div>\n </div>\n\n <div class="dataset-body"></div>\n</div>';return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel-anon"]=b(function(g,r,p,k,u){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);u=u||{};var q="",i,f,o=this,e="function",c=p.blockHelperMissing,d=this.escapeExpression;function n(z,y){var v="",x,w;v+='\n <div class="history-name" title="';w={hash:{},inverse:o.noop,fn:o.program(2,m,y),data:y};if(x=p.local){x=x.call(z,w)}else{x=z.local;x=typeof x===e?x.apply(z):x}if(!p.local){x=c.call(z,x,w)}if(x||x===0){v+=x}v+='">\n ';if(x=p.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===e?x.apply(z):x}v+=d(x)+"\n </div>\n ";return v}function m(w,v){return"You must be logged in to edit your history name"}function l(y,x){var v="",w;v+='\n <div class="history-size">';if(w=p.nice_size){w=w.call(y,{hash:{},data:x})}else{w=y.nice_size;w=typeof w===e?w.apply(y):w}v+=d(w)+"</div>\n ";return v}function j(y,x){var v="",w;v+='\n \n <div class="';if(w=p.status){w=w.call(y,{hash:{},data:x})}else{w=y.status;w=typeof w===e?w.apply(y):w}v+=d(w)+'message">';if(w=p.message){w=w.call(y,{hash:{},data:x})}else{w=y.message;w=typeof w===e?w.apply(y):w}v+=d(w)+"</div>\n ";return v}function h(w,v){return"You are over your disk quota"}function t(w,v){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function s(w,v){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n\n <div class="history-title">\n \n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,u),data:u});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(4,l,u),data:u});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(6,j,u),data:u});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(8,h,u),data:u};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(10,t,u),data:u};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(12,s,u),data:u};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(i,u,s,n,A){this.compilerInfo=[4,">= 1.0.0"];s=this.merge(s,i.helpers);A=A||{};var t="",k,f,r=this,e="function",c=s.blockHelperMissing,d=this.escapeExpression;function q(F,E){var B="",D,C;B+='\n <div class="history-name" title="';C={hash:{},inverse:r.noop,fn:r.program(2,p,E),data:E};if(D=s.local){D=D.call(F,C)}else{D=F.local;D=typeof D===e?D.apply(F):D}if(!s.local){D=c.call(F,D,C)}if(D||D===0){B+=D}B+='">\n ';if(D=s.name){D=D.call(F,{hash:{},data:E})}else{D=F.name;D=typeof D===e?D.apply(F):D}B+=d(D)+"\n </div>\n ";return B}function p(C,B){return"Click to rename history"}function o(E,D){var B="",C;B+='\n <div class="history-size">';if(C=s.nice_size){C=C.call(E,{hash:{},data:D})}else{C=E.nice_size;C=typeof C===e?C.apply(E):C}B+=d(C)+"</div>\n ";return B}function m(F,E){var B="",D,C;B+='\n <div class="warningmessagesmall"><strong>\n ';C={hash:{},inverse:r.noop,fn:r.program(7,l,E),data:E};if(D=s.local){D=D.call(F,C)}else{D=F.local;D=typeof D===e?D.apply(F):D}if(!s.local){D=c.call(F,D,C)}if(D||D===0){B+=D}B+="\n </strong></div>\n ";return B}function l(C,B){return"You are currently viewing a deleted history!"}function h(E,D){var B="",C;B+='\n \n <div class="';if(C=s.status){C=C.call(E,{hash:{},data:D})}else{C=E.status;C=typeof C===e?C.apply(E):C}B+=d(C)+'message">';if(C=s.message){C=C.call(E,{hash:{},data:D})}else{C=E.message;C=typeof C===e?C.apply(E):C}B+=d(C)+"</div>\n ";return B}function z(C,B){return"You are over your disk quota"}function y(C,B){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function x(C,B){return"Tags"}function w(C,B){return"Annotation"}function v(C,B){return"Click to edit annotation"}function j(D,C){var B;if(B=s.annotation){B=B.call(D,{hash:{},data:C})}else{B=D.annotation;B=typeof B===e?B.apply(D):B}return d(B)}function g(C,B){return"Your history is empty. Click 'Get Data' on the left pane to start"}t+='<div class="history-controls">\n\n <div class="history-title">\n ';k=s["if"].call(u,u.name,{hash:{},inverse:r.noop,fn:r.program(1,q,A),data:A});if(k||k===0){t+=k}t+='\n </div>\n\n <div class="history-subtitle clear">\n ';k=s["if"].call(u,u.nice_size,{hash:{},inverse:r.noop,fn:r.program(4,o,A),data:A});if(k||k===0){t+=k}t+='\n\n <div class="history-secondary-actions">\n </div>\n </div>\n\n ';k=s["if"].call(u,u.deleted,{hash:{},inverse:r.noop,fn:r.program(6,m,A),data:A});if(k||k===0){t+=k}t+='\n\n <div class="message-container">\n ';k=s["if"].call(u,u.message,{hash:{},inverse:r.noop,fn:r.program(9,h,A),data:A});if(k||k===0){t+=k}t+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:r.noop,fn:r.program(11,z,A),data:A};if(k=s.local){k=k.call(u,f)}else{k=u.local;k=typeof k===e?k.apply(u):k}if(!s.local){k=c.call(u,k,f)}if(k||k===0){t+=k}t+=".\n ";f={hash:{},inverse:r.noop,fn:r.program(13,y,A),data:A};if(k=s.local){k=k.call(u,f)}else{k=u.local;k=typeof k===e?k.apply(u):k}if(!s.local){k=c.call(u,k,f)}if(k||k===0){t+=k}t+='.\n </div>\n \n \n \n <div class="tags-display">\n <label class="prompt">';f={hash:{},inverse:r.noop,fn:r.program(15,x,A),data:A};if(k=s.local){k=k.call(u,f)}else{k=u.local;k=typeof k===e?k.apply(u):k}if(!s.local){k=c.call(u,k,f)}if(k||k===0){t+=k}t+='</label>\n <div class="tags"></div>\n </div>\n\n \n <div class="annotation-display">\n <label class="prompt">';f={hash:{},inverse:r.noop,fn:r.program(17,w,A),data:A};if(k=s.local){k=k.call(u,f)}else{k=u.local;k=typeof k===e?k.apply(u):k}if(!s.local){k=c.call(u,k,f)}if(k||k===0){t+=k}t+='</label>\n <div class="annotation" title="';f={hash:{},inverse:r.noop,fn:r.program(19,v,A),data:A};if(k=s.local){k=k.call(u,f)}else{k=u.local;k=typeof k===e?k.apply(u):k}if(!s.local){k=c.call(u,k,f)}if(k||k===0){t+=k}t+='"\n \n >';k=s["if"].call(u,u.annotation,{hash:{},inverse:r.noop,fn:r.program(21,j,A),data:A});if(k||k===0){t+=k}t+='</div>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:r.noop,fn:r.program(23,g,A),data:A};if(k=s.local){k=k.call(u,f)}else{k=u.local;k=typeof k===e?k.apply(u):k}if(!s.local){k=c.call(u,k,f)}if(k||k===0){t+=k}t+="\n </div>";return t})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(k,w,u,p,C){this.compilerInfo=[4,">= 1.0.0"];u=this.merge(u,k.helpers);C=C||{};var v="",m,g,t=this,e="function",c=u.blockHelperMissing,d=this.escapeExpression;function s(H,G){var D="",F,E;D+='\n <div class="history-name" title="';E={hash:{},inverse:t.noop,fn:t.program(2,r,G),data:G};if(F=u.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!u.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='">\n ';if(F=u.name){F=F.call(H,{hash:{},data:G})}else{F=H.name;F=typeof F===e?F.apply(H):F}D+=d(F)+"\n </div>\n ";return D}function r(E,D){return"Click to rename history"}function q(G,F){var D="",E;D+='\n <div class="history-size">';if(E=u.nice_size){E=E.call(G,{hash:{},data:F})}else{E=G.nice_size;E=typeof E===e?E.apply(G):E}D+=d(E)+"</div>\n ";return D}function o(H,G){var D="",F,E;D+='\n <div class="warningmessagesmall"><strong>\n ';E={hash:{},inverse:t.noop,fn:t.program(7,n,G),data:G};if(F=u.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!u.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+="\n </strong></div>\n ";return D}function n(E,D){return"You are currently viewing a deleted history!"}function j(G,F){var D="",E;D+='\n \n <div class="';if(E=u.status){E=E.call(G,{hash:{},data:F})}else{E=G.status;E=typeof E===e?E.apply(G):E}D+=d(E)+'message">';if(E=u.message){E=E.call(G,{hash:{},data:F})}else{E=G.message;E=typeof E===e?E.apply(G):E}D+=d(E)+"</div>\n ";return D}function B(E,D){return"You are over your disk quota"}function A(E,D){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function z(E,D){return"Tags"}function y(E,D){return"Annotation"}function x(E,D){return"Click to edit annotation"}function l(G,F){var D="",E;D+="\n ";if(E=u.annotation){E=E.call(G,{hash:{},data:F})}else{E=G.annotation;E=typeof E===e?E.apply(G):E}D+=d(E)+"\n ";return D}function i(H,G){var D="",F,E;D+="\n <em>";E={hash:{},inverse:t.noop,fn:t.program(24,h,G),data:G};if(F=u.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!u.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+="</em>\n ";return D}function h(E,D){return"Describe or add notes to history"}function f(E,D){return"Your history is empty. Click 'Get Data' on the left pane to start"}v+='<div class="history-controls">\n\n <div class="history-title">\n ';m=u["if"].call(w,w.name,{hash:{},inverse:t.noop,fn:t.program(1,s,C),data:C});if(m||m===0){v+=m}v+='\n </div>\n\n <div class="history-subtitle clear">\n ';m=u["if"].call(w,w.nice_size,{hash:{},inverse:t.noop,fn:t.program(4,q,C),data:C});if(m||m===0){v+=m}v+='\n\n <div class="history-secondary-actions">\n </div>\n </div>\n\n ';m=u["if"].call(w,w.deleted,{hash:{},inverse:t.noop,fn:t.program(6,o,C),data:C});if(m||m===0){v+=m}v+='\n\n <div class="message-container">\n ';m=u["if"].call(w,w.message,{hash:{},inverse:t.noop,fn:t.program(9,j,C),data:C});if(m||m===0){v+=m}v+='\n </div>\n\n <div class="quota-message errormessage">\n ';g={hash:{},inverse:t.noop,fn:t.program(11,B,C),data:C};if(m=u.local){m=m.call(w,g)}else{m=w.local;m=typeof m===e?m.apply(w):m}if(!u.local){m=c.call(w,m,g)}if(m||m===0){v+=m}v+=".\n ";g={hash:{},inverse:t.noop,fn:t.program(13,A,C),data:C};if(m=u.local){m=m.call(w,g)}else{m=w.local;m=typeof m===e?m.apply(w):m}if(!u.local){m=c.call(w,m,g)}if(m||m===0){v+=m}v+='.\n </div>\n \n \n \n <div class="tags-display">\n <label class="prompt">';g={hash:{},inverse:t.noop,fn:t.program(15,z,C),data:C};if(m=u.local){m=m.call(w,g)}else{m=w.local;m=typeof m===e?m.apply(w):m}if(!u.local){m=c.call(w,m,g)}if(m||m===0){v+=m}v+='</label>\n <div class="tags"></div>\n </div>\n\n \n <div class="annotation-display">\n <label class="prompt">';g={hash:{},inverse:t.noop,fn:t.program(17,y,C),data:C};if(m=u.local){m=m.call(w,g)}else{m=w.local;m=typeof m===e?m.apply(w):m}if(!u.local){m=c.call(w,m,g)}if(m||m===0){v+=m}v+='</label>\n <div class="annotation" title="';g={hash:{},inverse:t.noop,fn:t.program(19,x,C),data:C};if(m=u.local){m=m.call(w,g)}else{m=w.local;m=typeof m===e?m.apply(w):m}if(!u.local){m=c.call(w,m,g)}if(m||m===0){v+=m}v+='">\n ';m=u["if"].call(w,w.annotation,{hash:{},inverse:t.program(23,i,C),fn:t.program(21,l,C),data:C});if(m||m===0){v+=m}v+='\n </div>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';g={hash:{},inverse:t.noop,fn:t.program(26,f,C),data:C};if(m=u.local){m=m.call(w,g)}else{m=w.local;m=typeof m===e?m.apply(w):m}if(!u.local){m=c.call(w,m,g)}if(m||m===0){v+=m}v+="\n </div>";return v})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-body"]=b(function(h,u,s,n,B){this.compilerInfo=[4,">= 1.0.0"];s=this.merge(s,h.helpers);B=B||{};var t="",j,e="function",d=this.escapeExpression,r=this,c=s.blockHelperMissing;function q(F,E){var C="",D;C+='\n <div class="dataset-summary">\n ';if(D=s.body){D=D.call(F,{hash:{},data:E})}else{D=F.body;D=typeof D===e?D.apply(F):D}if(D||D===0){C+=D}C+='\n </div>\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';return C}function p(F,E){var C="",D;C+='\n <div class="dataset-summary">\n ';D=s["if"].call(F,F.misc_blurb,{hash:{},inverse:r.noop,fn:r.program(4,o,E),data:E});if(D||D===0){C+=D}C+="\n\n ";D=s["if"].call(F,F.data_type,{hash:{},inverse:r.noop,fn:r.program(6,m,E),data:E});if(D||D===0){C+=D}C+="\n\n ";D=s["if"].call(F,F.metadata_dbkey,{hash:{},inverse:r.noop,fn:r.program(9,g,E),data:E});if(D||D===0){C+=D}C+="\n\n ";D=s["if"].call(F,F.misc_info,{hash:{},inverse:r.noop,fn:r.program(12,z,E),data:E});if(D||D===0){C+=D}C+='\n </div>\n\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';D=s.unless.call(F,F.deleted,{hash:{},inverse:r.noop,fn:r.program(14,y,E),data:E});if(D||D===0){C+=D}C+="\n\n ";return C}function o(F,E){var C="",D;C+='\n <div class="dataset-blurb">\n <span class="value">';if(D=s.misc_blurb){D=D.call(F,{hash:{},data:E})}else{D=F.misc_blurb;D=typeof D===e?D.apply(F):D}C+=d(D)+"</span>\n </div>\n ";return C}function m(G,F){var C="",E,D;C+='\n <div class="dataset-datatype">\n <label class="prompt">';D={hash:{},inverse:r.noop,fn:r.program(7,l,F),data:F};if(E=s.local){E=E.call(G,D)}else{E=G.local;E=typeof E===e?E.apply(G):E}if(!s.local){E=c.call(G,E,D)}if(E||E===0){C+=E}C+='</label>\n <span class="value">';if(E=s.data_type){E=E.call(G,{hash:{},data:F})}else{E=G.data_type;E=typeof E===e?E.apply(G):E}C+=d(E)+"</span>\n </div>\n ";return C}function l(D,C){return"format"}function g(G,F){var C="",E,D;C+='\n <div class="dataset-dbkey">\n <label class="prompt">';D={hash:{},inverse:r.noop,fn:r.program(10,A,F),data:F};if(E=s.local){E=E.call(G,D)}else{E=G.local;E=typeof E===e?E.apply(G):E}if(!s.local){E=c.call(G,E,D)}if(E||E===0){C+=E}C+='</label>\n <span class="value">\n ';if(E=s.metadata_dbkey){E=E.call(G,{hash:{},data:F})}else{E=G.metadata_dbkey;E=typeof E===e?E.apply(G):E}C+=d(E)+"\n </span>\n </div>\n ";return C}function A(D,C){return"database"}function z(F,E){var C="",D;C+='\n <div class="dataset-info">\n <span class="value">';if(D=s.misc_info){D=D.call(F,{hash:{},data:E})}else{D=F.misc_info;D=typeof D===e?D.apply(F):D}C+=d(D)+"</span>\n </div>\n ";return C}function y(G,F){var C="",E,D;C+='\n \n <div class="tags-display"></div>\n\n \n <div class="annotation-display">\n <label class="prompt">';D={hash:{},inverse:r.noop,fn:r.program(15,x,F),data:F};if(E=s.local){E=E.call(G,D)}else{E=G.local;E=typeof E===e?E.apply(G):E}if(!s.local){E=c.call(G,E,D)}if(E||E===0){C+=E}C+='</label>\n <div id="dataset-';if(E=s.id){E=E.call(G,{hash:{},data:F})}else{E=G.id;E=typeof E===e?E.apply(G):E}C+=d(E)+'-annotation" class="annotation editable-text"\n title="';D={hash:{},inverse:r.noop,fn:r.program(17,w,F),data:F};if(E=s.local){E=E.call(G,D)}else{E=G.local;E=typeof E===e?E.apply(G):E}if(!s.local){E=c.call(G,E,D)}if(E||E===0){C+=E}C+='"></div>\n </div>\n\n <div class="dataset-display-applications">\n ';E=s.each.call(G,G.display_apps,{hash:{},inverse:r.noop,fn:r.program(19,v,F),data:F});if(E||E===0){C+=E}C+="\n\n ";E=s.each.call(G,G.display_types,{hash:{},inverse:r.noop,fn:r.program(19,v,F),data:F});if(E||E===0){C+=E}C+='\n </div>\n\n <div class="dataset-peek">\n ';E=s["if"].call(G,G.peek,{hash:{},inverse:r.noop,fn:r.program(23,f,F),data:F});if(E||E===0){C+=E}C+="\n </div>\n\n ";return C}function x(D,C){return"Annotation"}function w(D,C){return"Edit dataset annotation"}function v(F,E){var C="",D;C+='\n <div class="display-application">\n <span class="display-application-location">';if(D=s.label){D=D.call(F,{hash:{},data:E})}else{D=F.label;D=typeof D===e?D.apply(F):D}C+=d(D)+'</span>\n <span class="display-application-links">\n ';D=s.each.call(F,F.links,{hash:{},inverse:r.noop,fn:r.program(20,k,E),data:E});if(D||D===0){C+=D}C+="\n </span>\n </div>\n ";return C}function k(G,F){var C="",E,D;C+='\n <a target="';if(E=s.target){E=E.call(G,{hash:{},data:F})}else{E=G.target;E=typeof E===e?E.apply(G):E}C+=d(E)+'" href="';if(E=s.href){E=E.call(G,{hash:{},data:F})}else{E=G.href;E=typeof E===e?E.apply(G):E}C+=d(E)+'">';D={hash:{},inverse:r.noop,fn:r.program(21,i,F),data:F};if(E=s.local){E=E.call(G,D)}else{E=G.local;E=typeof E===e?E.apply(G):E}if(!s.local){E=c.call(G,E,D)}if(E||E===0){C+=E}C+="</a>\n ";return C}function i(E,D){var C;if(C=s.text){C=C.call(E,{hash:{},data:D})}else{C=E.text;C=typeof C===e?C.apply(E):C}return d(C)}function f(F,E){var C="",D;C+='\n <pre class="peek">';if(D=s.peek){D=D.call(F,{hash:{},data:E})}else{D=F.peek;D=typeof D===e?D.apply(F):D}if(D||D===0){C+=D}C+="</pre>\n ";return C}t+='<div class="dataset-body">\n ';j=s["if"].call(u,u.body,{hash:{},inverse:r.program(3,p,B),fn:r.program(1,q,B),data:B});if(j||j===0){t+=j}t+="\n</div>";return t})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-body"]=b(function(j,v,t,o,C){this.compilerInfo=[4,">= 1.0.0"];t=this.merge(t,j.helpers);C=C||{};var u="",l,e="function",d=this.escapeExpression,s=this,c=t.blockHelperMissing;function r(G,F){var D="",E;D+='\n <div class="dataset-summary">\n ';if(E=t.body){E=E.call(G,{hash:{},data:F})}else{E=G.body;E=typeof E===e?E.apply(G):E}if(E||E===0){D+=E}D+='\n </div>\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';return D}function q(G,F){var D="",E;D+='\n <div class="dataset-summary">\n ';E=t["if"].call(G,G.misc_blurb,{hash:{},inverse:s.noop,fn:s.program(4,p,F),data:F});if(E||E===0){D+=E}D+="\n\n ";E=t["if"].call(G,G.data_type,{hash:{},inverse:s.noop,fn:s.program(6,n,F),data:F});if(E||E===0){D+=E}D+="\n\n ";E=t["if"].call(G,G.metadata_dbkey,{hash:{},inverse:s.noop,fn:s.program(9,i,F),data:F});if(E||E===0){D+=E}D+="\n\n ";E=t["if"].call(G,G.misc_info,{hash:{},inverse:s.noop,fn:s.program(12,A,F),data:F});if(E||E===0){D+=E}D+='\n </div>\n\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';E=t.unless.call(G,G.deleted,{hash:{},inverse:s.noop,fn:s.program(14,z,F),data:F});if(E||E===0){D+=E}D+="\n\n ";return D}function p(G,F){var D="",E;D+='\n <div class="dataset-blurb">\n <span class="value">';if(E=t.misc_blurb){E=E.call(G,{hash:{},data:F})}else{E=G.misc_blurb;E=typeof E===e?E.apply(G):E}D+=d(E)+"</span>\n </div>\n ";return D}function n(H,G){var D="",F,E;D+='\n <div class="dataset-datatype">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(7,m,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <span class="value">';if(F=t.data_type){F=F.call(H,{hash:{},data:G})}else{F=H.data_type;F=typeof F===e?F.apply(H):F}D+=d(F)+"</span>\n </div>\n ";return D}function m(E,D){return"format"}function i(H,G){var D="",F,E;D+='\n <div class="dataset-dbkey">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(10,B,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <span class="value">\n ';if(F=t.metadata_dbkey){F=F.call(H,{hash:{},data:G})}else{F=H.metadata_dbkey;F=typeof F===e?F.apply(H):F}D+=d(F)+"\n </span>\n </div>\n ";return D}function B(E,D){return"database"}function A(G,F){var D="",E;D+='\n <div class="dataset-info">\n <span class="value">';if(E=t.misc_info){E=E.call(G,{hash:{},data:F})}else{E=G.misc_info;E=typeof E===e?E.apply(G):E}D+=d(E)+"</span>\n </div>\n ";return D}function z(H,G){var D="",F,E;D+='\n \n <div class="tags-display">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(15,y,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <div class="tags"></div>\n </div>\n\n \n <div class="annotation-display">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(17,x,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <div id="dataset-';if(F=t.id){F=F.call(H,{hash:{},data:G})}else{F=H.id;F=typeof F===e?F.apply(H):F}D+=d(F)+'-annotation" class="annotation editable-text"\n title="';E={hash:{},inverse:s.noop,fn:s.program(19,w,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='"></div>\n </div>\n\n <div class="dataset-display-applications">\n ';F=t.each.call(H,H.display_apps,{hash:{},inverse:s.noop,fn:s.program(21,k,G),data:G});if(F||F===0){D+=F}D+="\n\n ";F=t.each.call(H,H.display_types,{hash:{},inverse:s.noop,fn:s.program(21,k,G),data:G});if(F||F===0){D+=F}D+='\n </div>\n\n <div class="dataset-peek">\n ';F=t["if"].call(H,H.peek,{hash:{},inverse:s.noop,fn:s.program(25,f,G),data:G});if(F||F===0){D+=F}D+="\n </div>\n\n ";return D}function y(E,D){return"Tags"}function x(E,D){return"Annotation"}function w(E,D){return"Edit dataset annotation"}function k(G,F){var D="",E;D+='\n <div class="display-application">\n <span class="display-application-location">';if(E=t.label){E=E.call(G,{hash:{},data:F})}else{E=G.label;E=typeof E===e?E.apply(G):E}D+=d(E)+'</span>\n <span class="display-application-links">\n ';E=t.each.call(G,G.links,{hash:{},inverse:s.noop,fn:s.program(22,h,F),data:F});if(E||E===0){D+=E}D+="\n </span>\n </div>\n ";return D}function h(H,G){var D="",F,E;D+='\n <a target="';if(F=t.target){F=F.call(H,{hash:{},data:G})}else{F=H.target;F=typeof F===e?F.apply(H):F}D+=d(F)+'" href="';if(F=t.href){F=F.call(H,{hash:{},data:G})}else{F=H.href;F=typeof F===e?F.apply(H):F}D+=d(F)+'">';E={hash:{},inverse:s.noop,fn:s.program(23,g,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+="</a>\n ";return D}function g(F,E){var D;if(D=t.text){D=D.call(F,{hash:{},data:E})}else{D=F.text;D=typeof D===e?D.apply(F):D}return d(D)}function f(G,F){var D="",E;D+='\n <pre class="peek">';if(E=t.peek){E=E.call(G,{hash:{},data:F})}else{E=G.peek;E=typeof E===e?E.apply(G):E}if(E||E===0){D+=E}D+="</pre>\n ";return D}u+='<div class="dataset-body">\n ';l=t["if"].call(v,v.body,{hash:{},inverse:s.program(3,q,C),fn:s.program(1,r,C),data:C});if(l||l===0){u+=l}u+="\n</div>";return u})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-skeleton"]=b(function(f,r,p,k,w){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,f.helpers);w=w||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var x="",z,y;x+='\n <div class="errormessagesmall">\n ';y={hash:{},inverse:o.noop,fn:o.program(2,m,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+=":\n ";y={hash:{},inverse:o.noop,fn:o.program(4,l,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </div>\n ";return x}function m(y,x){return"There was an error getting the data for this dataset"}function l(z,y){var x;if(x=p.error){x=x.call(z,{hash:{},data:y})}else{x=z.error;x=typeof x===e?x.apply(z):x}return d(x)}function j(A,z){var x="",y;x+="\n ";y=p["if"].call(A,A.purged,{hash:{},inverse:o.program(10,v,z),fn:o.program(7,i,z),data:z});if(y||y===0){x+=y}x+="\n ";return x}function i(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(8,g,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n\n ";return x}function g(y,x){return"This dataset has been deleted and removed from disk."}function v(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(11,u,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n \n Click <a href="javascript:void(0);" class="dataset-undelete">here</a> to undelete it\n or <a href="javascript:void(0);" class="dataset-purge">here</a> to immediately remove it from disk\n </strong></div>\n ';return x}function u(y,x){return"This dataset has been deleted."}function t(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(14,s,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n Click <a href="javascript:void(0);" class="dataset-unhide">here</a> to unhide it\n </strong></div>\n ';return x}function s(y,x){return"This dataset has been hidden."}q+='<div class="dataset hda">\n <div class="dataset-warnings">\n ';h=p["if"].call(r,r.error,{hash:{},inverse:o.noop,fn:o.program(1,n,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(6,j,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p.unless.call(r,r.visible,{hash:{},inverse:o.noop,fn:o.program(13,t,w),data:w});if(h||h===0){q+=h}q+='\n </div>\n\n <div class="dataset-primary-actions"></div>\n <div class="dataset-title-bar clear">\n <span class="dataset-state-icon state-icon"></span>\n <div class="dataset-title">\n <span class="hda-hid">';if(h=p.hid){h=h.call(r,{hash:{},data:w})}else{h=r.hid;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n <span class="dataset-name">';if(h=p.name){h=h.call(r,{hash:{},data:w})}else{h=r.name;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n </div>\n </div>\n\n <div class="dataset-body"></div>\n</div>';return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-skeleton"]=b(function(f,r,p,k,w){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,f.helpers);w=w||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var x="",z,y;x+='\n <div class="errormessagesmall">\n ';y={hash:{},inverse:o.noop,fn:o.program(2,m,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+=":\n ";y={hash:{},inverse:o.noop,fn:o.program(4,l,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </div>\n ";return x}function m(y,x){return"There was an error getting the data for this dataset"}function l(z,y){var x;if(x=p.error){x=x.call(z,{hash:{},data:y})}else{x=z.error;x=typeof x===e?x.apply(z):x}return d(x)}function j(A,z){var x="",y;x+="\n ";y=p["if"].call(A,A.purged,{hash:{},inverse:o.program(10,v,z),fn:o.program(7,i,z),data:z});if(y||y===0){x+=y}x+="\n ";return x}function i(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(8,g,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n\n ";return x}function g(y,x){return"This dataset has been deleted and removed from disk."}function v(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(11,u,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n \n Click <a href="javascript:void(0);" class="dataset-undelete">here</a> to undelete it\n or <a href="javascript:void(0);" class="dataset-purge">here</a> to immediately remove it from disk\n </strong></div>\n ';return x}function u(y,x){return"This dataset has been deleted."}function t(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(14,s,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n Click <a href="javascript:void(0);" class="dataset-unhide">here</a> to unhide it\n </strong></div>\n ';return x}function s(y,x){return"This dataset has been hidden."}q+='<div class="dataset hda">\n <div class="dataset-warnings">\n ';h=p["if"].call(r,r.error,{hash:{},inverse:o.noop,fn:o.program(1,n,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(6,j,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p.unless.call(r,r.visible,{hash:{},inverse:o.noop,fn:o.program(13,t,w),data:w});if(h||h===0){q+=h}q+='\n </div>\n\n <div class="dataset-primary-actions"></div>\n <div class="dataset-title-bar clear">\n <span class="dataset-state-icon state-icon"></span>\n <div class="dataset-title">\n <span class="hda-hid">';if(h=p.hid){h=h.call(r,{hash:{},data:w})}else{h=r.hid;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n <span class="dataset-name">';if(h=p.name){h=h.call(r,{hash:{},data:w})}else{h=r.name;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n </div>\n </div>\n\n <div class="dataset-body"></div>\n</div>';return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel-anon"]=b(function(g,r,p,k,u){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);u=u||{};var q="",i,f,o=this,e="function",c=p.blockHelperMissing,d=this.escapeExpression;function n(z,y){var v="",x,w;v+='\n <div class="history-name" title="';w={hash:{},inverse:o.noop,fn:o.program(2,m,y),data:y};if(x=p.local){x=x.call(z,w)}else{x=z.local;x=typeof x===e?x.apply(z):x}if(!p.local){x=c.call(z,x,w)}if(x||x===0){v+=x}v+='">\n ';if(x=p.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===e?x.apply(z):x}v+=d(x)+"\n </div>\n ";return v}function m(w,v){return"You must be logged in to edit your history name"}function l(y,x){var v="",w;v+='\n <div class="history-size">';if(w=p.nice_size){w=w.call(y,{hash:{},data:x})}else{w=y.nice_size;w=typeof w===e?w.apply(y):w}v+=d(w)+"</div>\n ";return v}function j(y,x){var v="",w;v+='\n \n <div class="';if(w=p.status){w=w.call(y,{hash:{},data:x})}else{w=y.status;w=typeof w===e?w.apply(y):w}v+=d(w)+'message">';if(w=p.message){w=w.call(y,{hash:{},data:x})}else{w=y.message;w=typeof w===e?w.apply(y):w}v+=d(w)+"</div>\n ";return v}function h(w,v){return"You are over your disk quota"}function t(w,v){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function s(w,v){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n\n <div class="history-title">\n \n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,u),data:u});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(4,l,u),data:u});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(6,j,u),data:u});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(8,h,u),data:u};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(10,t,u),data:u};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(12,s,u),data:u};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(h,t,r,m,z){this.compilerInfo=[4,">= 1.0.0"];r=this.merge(r,h.helpers);z=z||{};var s="",j,f,q=this,e="function",c=r.blockHelperMissing,d=this.escapeExpression;function p(E,D){var A="",C,B;A+='\n <div class="history-name" title="';B={hash:{},inverse:q.noop,fn:q.program(2,o,D),data:D};if(C=r.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!r.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+='">\n ';if(C=r.name){C=C.call(E,{hash:{},data:D})}else{C=E.name;C=typeof C===e?C.apply(E):C}A+=d(C)+"\n </div>\n ";return A}function o(B,A){return"Click to rename history"}function n(D,C){var A="",B;A+='\n <div class="history-size">';if(B=r.nice_size){B=B.call(D,{hash:{},data:C})}else{B=D.nice_size;B=typeof B===e?B.apply(D):B}A+=d(B)+"</div>\n ";return A}function l(E,D){var A="",C,B;A+='\n <div class="warningmessagesmall"><strong>\n ';B={hash:{},inverse:q.noop,fn:q.program(7,k,D),data:D};if(C=r.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!r.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+="\n </strong></div>\n ";return A}function k(B,A){return"You are currently viewing a deleted history!"}function g(D,C){var A="",B;A+='\n \n <div class="';if(B=r.status){B=B.call(D,{hash:{},data:C})}else{B=D.status;B=typeof B===e?B.apply(D):B}A+=d(B)+'message">';if(B=r.message){B=B.call(D,{hash:{},data:C})}else{B=D.message;B=typeof B===e?B.apply(D):B}A+=d(B)+"</div>\n ";return A}function y(B,A){return"You are over your disk quota"}function x(B,A){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function w(B,A){return"Annotation"}function v(B,A){return"Click to edit annotation"}function u(C,B){var A;if(A=r.annotation){A=A.call(C,{hash:{},data:B})}else{A=C.annotation;A=typeof A===e?A.apply(C):A}return d(A)}function i(B,A){return"Your history is empty. Click 'Get Data' on the left pane to start"}s+='<div class="history-controls">\n\n <div class="history-title">\n ';j=r["if"].call(t,t.name,{hash:{},inverse:q.noop,fn:q.program(1,p,z),data:z});if(j||j===0){s+=j}s+='\n </div>\n\n <div class="history-subtitle clear">\n ';j=r["if"].call(t,t.nice_size,{hash:{},inverse:q.noop,fn:q.program(4,n,z),data:z});if(j||j===0){s+=j}s+='\n\n <div class="history-secondary-actions">\n </div>\n </div>\n\n ';j=r["if"].call(t,t.deleted,{hash:{},inverse:q.noop,fn:q.program(6,l,z),data:z});if(j||j===0){s+=j}s+='\n\n <div class="message-container">\n ';j=r["if"].call(t,t.message,{hash:{},inverse:q.noop,fn:q.program(9,g,z),data:z});if(j||j===0){s+=j}s+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:q.noop,fn:q.program(11,y,z),data:z};if(j=r.local){j=j.call(t,f)}else{j=t.local;j=typeof j===e?j.apply(t):j}if(!r.local){j=c.call(t,j,f)}if(j||j===0){s+=j}s+=".\n ";f={hash:{},inverse:q.noop,fn:q.program(13,x,z),data:z};if(j=r.local){j=j.call(t,f)}else{j=t.local;j=typeof j===e?j.apply(t):j}if(!r.local){j=c.call(t,j,f)}if(j||j===0){s+=j}s+='.\n </div>\n \n \n \n <div class="tags-display"></div>\n\n \n <div class="annotation-display">\n <label class="prompt">';f={hash:{},inverse:q.noop,fn:q.program(15,w,z),data:z};if(j=r.local){j=j.call(t,f)}else{j=t.local;j=typeof j===e?j.apply(t):j}if(!r.local){j=c.call(t,j,f)}if(j||j===0){s+=j}s+='</label>\n <div class="annotation" title="';f={hash:{},inverse:q.noop,fn:q.program(17,v,z),data:z};if(j=r.local){j=j.call(t,f)}else{j=t.local;j=typeof j===e?j.apply(t):j}if(!r.local){j=c.call(t,j,f)}if(j||j===0){s+=j}s+='"\n \n >';j=r["if"].call(t,t.annotation,{hash:{},inverse:q.noop,fn:q.program(19,u,z),data:z});if(j||j===0){s+=j}s+='</div>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:q.noop,fn:q.program(21,i,z),data:z};if(j=r.local){j=j.call(t,f)}else{j=t.local;j=typeof j===e?j.apply(t):j}if(!r.local){j=c.call(t,j,f)}if(j||j===0){s+=j}s+="\n </div>";return s})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(k,w,u,p,C){this.compilerInfo=[4,">= 1.0.0"];u=this.merge(u,k.helpers);C=C||{};var v="",m,g,t=this,e="function",c=u.blockHelperMissing,d=this.escapeExpression;function s(H,G){var D="",F,E;D+='\n <div class="history-name" title="';E={hash:{},inverse:t.noop,fn:t.program(2,r,G),data:G};if(F=u.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!u.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='">\n ';if(F=u.name){F=F.call(H,{hash:{},data:G})}else{F=H.name;F=typeof F===e?F.apply(H):F}D+=d(F)+"\n </div>\n ";return D}function r(E,D){return"Click to rename history"}function q(G,F){var D="",E;D+='\n <div class="history-size">';if(E=u.nice_size){E=E.call(G,{hash:{},data:F})}else{E=G.nice_size;E=typeof E===e?E.apply(G):E}D+=d(E)+"</div>\n ";return D}function o(H,G){var D="",F,E;D+='\n <div class="warningmessagesmall"><strong>\n ';E={hash:{},inverse:t.noop,fn:t.program(7,n,G),data:G};if(F=u.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!u.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+="\n </strong></div>\n ";return D}function n(E,D){return"You are currently viewing a deleted history!"}function j(G,F){var D="",E;D+='\n \n <div class="';if(E=u.status){E=E.call(G,{hash:{},data:F})}else{E=G.status;E=typeof E===e?E.apply(G):E}D+=d(E)+'message">';if(E=u.message){E=E.call(G,{hash:{},data:F})}else{E=G.message;E=typeof E===e?E.apply(G):E}D+=d(E)+"</div>\n ";return D}function B(E,D){return"You are over your disk quota"}function A(E,D){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function z(E,D){return"Tags"}function y(E,D){return"Annotation"}function x(E,D){return"Click to edit annotation"}function l(G,F){var D="",E;D+="\n ";if(E=u.annotation){E=E.call(G,{hash:{},data:F})}else{E=G.annotation;E=typeof E===e?E.apply(G):E}D+=d(E)+"\n ";return D}function i(H,G){var D="",F,E;D+="\n <em>";E={hash:{},inverse:t.noop,fn:t.program(24,h,G),data:G};if(F=u.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!u.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+="</em>\n ";return D}function h(E,D){return"Describe or add notes to history"}function f(E,D){return"Your history is empty. Click 'Get Data' on the left pane to start"}v+='<div class="history-controls">\n\n <div class="history-title">\n ';m=u["if"].call(w,w.name,{hash:{},inverse:t.noop,fn:t.program(1,s,C),data:C});if(m||m===0){v+=m}v+='\n </div>\n\n <div class="history-subtitle clear">\n ';m=u["if"].call(w,w.nice_size,{hash:{},inverse:t.noop,fn:t.program(4,q,C),data:C});if(m||m===0){v+=m}v+='\n\n <div class="history-secondary-actions">\n </div>\n </div>\n\n ';m=u["if"].call(w,w.deleted,{hash:{},inverse:t.noop,fn:t.program(6,o,C),data:C});if(m||m===0){v+=m}v+='\n\n <div class="message-container">\n ';m=u["if"].call(w,w.message,{hash:{},inverse:t.noop,fn:t.program(9,j,C),data:C});if(m||m===0){v+=m}v+='\n </div>\n\n <div class="quota-message errormessage">\n ';g={hash:{},inverse:t.noop,fn:t.program(11,B,C),data:C};if(m=u.local){m=m.call(w,g)}else{m=w.local;m=typeof m===e?m.apply(w):m}if(!u.local){m=c.call(w,m,g)}if(m||m===0){v+=m}v+=".\n ";g={hash:{},inverse:t.noop,fn:t.program(13,A,C),data:C};if(m=u.local){m=m.call(w,g)}else{m=w.local;m=typeof m===e?m.apply(w):m}if(!u.local){m=c.call(w,m,g)}if(m||m===0){v+=m}v+='.\n </div>\n \n \n \n <div class="tags-display">\n <label class="prompt">';g={hash:{},inverse:t.noop,fn:t.program(15,z,C),data:C};if(m=u.local){m=m.call(w,g)}else{m=w.local;m=typeof m===e?m.apply(w):m}if(!u.local){m=c.call(w,m,g)}if(m||m===0){v+=m}v+='</label>\n <div class="tags"></div>\n </div>\n\n \n <div class="annotation-display">\n <label class="prompt">';g={hash:{},inverse:t.noop,fn:t.program(17,y,C),data:C};if(m=u.local){m=m.call(w,g)}else{m=w.local;m=typeof m===e?m.apply(w):m}if(!u.local){m=c.call(w,m,g)}if(m||m===0){v+=m}v+='</label>\n <div class="annotation" title="';g={hash:{},inverse:t.noop,fn:t.program(19,x,C),data:C};if(m=u.local){m=m.call(w,g)}else{m=w.local;m=typeof m===e?m.apply(w):m}if(!u.local){m=c.call(w,m,g)}if(m||m===0){v+=m}v+='">\n ';m=u["if"].call(w,w.annotation,{hash:{},inverse:t.program(23,i,C),fn:t.program(21,l,C),data:C});if(m||m===0){v+=m}v+='\n </div>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';g={hash:{},inverse:t.noop,fn:t.program(26,f,C),data:C};if(m=u.local){m=m.call(w,g)}else{m=w.local;m=typeof m===e?m.apply(w):m}if(!u.local){m=c.call(w,m,g)}if(m||m===0){v+=m}v+="\n </div>";return v})})();
\ No newline at end of file
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/packed/templates/compiled/template-hda-body.js
--- a/static/scripts/packed/templates/compiled/template-hda-body.js
+++ b/static/scripts/packed/templates/compiled/template-hda-body.js
@@ -1,1 +1,1 @@
-(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-body"]=b(function(j,v,t,o,C){this.compilerInfo=[4,">= 1.0.0"];t=this.merge(t,j.helpers);C=C||{};var u="",l,e="function",d=this.escapeExpression,s=this,c=t.blockHelperMissing;function r(G,F){var D="",E;D+='\n <div class="dataset-summary">\n ';if(E=t.body){E=E.call(G,{hash:{},data:F})}else{E=G.body;E=typeof E===e?E.apply(G):E}if(E||E===0){D+=E}D+='\n </div>\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';return D}function q(G,F){var D="",E;D+='\n <div class="dataset-summary">\n ';E=t["if"].call(G,G.misc_blurb,{hash:{},inverse:s.noop,fn:s.program(4,p,F),data:F});if(E||E===0){D+=E}D+="\n\n ";E=t["if"].call(G,G.data_type,{hash:{},inverse:s.noop,fn:s.program(6,n,F),data:F});if(E||E===0){D+=E}D+="\n\n ";E=t["if"].call(G,G.metadata_dbkey,{hash:{},inverse:s.noop,fn:s.program(9,i,F),data:F});if(E||E===0){D+=E}D+="\n\n ";E=t["if"].call(G,G.misc_info,{hash:{},inverse:s.noop,fn:s.program(12,A,F),data:F});if(E||E===0){D+=E}D+='\n </div>\n\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';E=t.unless.call(G,G.deleted,{hash:{},inverse:s.noop,fn:s.program(14,z,F),data:F});if(E||E===0){D+=E}D+="\n\n ";return D}function p(G,F){var D="",E;D+='\n <div class="dataset-blurb">\n <span class="value">';if(E=t.misc_blurb){E=E.call(G,{hash:{},data:F})}else{E=G.misc_blurb;E=typeof E===e?E.apply(G):E}D+=d(E)+"</span>\n </div>\n ";return D}function n(H,G){var D="",F,E;D+='\n <div class="dataset-datatype">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(7,m,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <span class="value">';if(F=t.data_type){F=F.call(H,{hash:{},data:G})}else{F=H.data_type;F=typeof F===e?F.apply(H):F}D+=d(F)+"</span>\n </div>\n ";return D}function m(E,D){return"format"}function i(H,G){var D="",F,E;D+='\n <div class="dataset-dbkey">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(10,B,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <span class="value">\n ';if(F=t.metadata_dbkey){F=F.call(H,{hash:{},data:G})}else{F=H.metadata_dbkey;F=typeof F===e?F.apply(H):F}D+=d(F)+"\n </span>\n </div>\n ";return D}function B(E,D){return"database"}function A(G,F){var D="",E;D+='\n <div class="dataset-info">\n <span class="value">';if(E=t.misc_info){E=E.call(G,{hash:{},data:F})}else{E=G.misc_info;E=typeof E===e?E.apply(G):E}D+=d(E)+"</span>\n </div>\n ";return D}function z(H,G){var D="",F,E;D+='\n \n <div class="tags-display">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(15,y,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <div class="tags"></div>\n </div>\n\n \n <div class="annotation-display">\n <label class="prompt">';E={hash:{},inverse:s.noop,fn:s.program(17,x,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='</label>\n <div id="dataset-';if(F=t.id){F=F.call(H,{hash:{},data:G})}else{F=H.id;F=typeof F===e?F.apply(H):F}D+=d(F)+'-annotation" class="annotation editable-text"\n title="';E={hash:{},inverse:s.noop,fn:s.program(19,w,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+='"></div>\n </div>\n\n <div class="dataset-display-applications">\n ';F=t.each.call(H,H.display_apps,{hash:{},inverse:s.noop,fn:s.program(21,k,G),data:G});if(F||F===0){D+=F}D+="\n\n ";F=t.each.call(H,H.display_types,{hash:{},inverse:s.noop,fn:s.program(21,k,G),data:G});if(F||F===0){D+=F}D+='\n </div>\n\n <div class="dataset-peek">\n ';F=t["if"].call(H,H.peek,{hash:{},inverse:s.noop,fn:s.program(25,f,G),data:G});if(F||F===0){D+=F}D+="\n </div>\n\n ";return D}function y(E,D){return"Tags"}function x(E,D){return"Annotation"}function w(E,D){return"Edit dataset annotation"}function k(G,F){var D="",E;D+='\n <div class="display-application">\n <span class="display-application-location">';if(E=t.label){E=E.call(G,{hash:{},data:F})}else{E=G.label;E=typeof E===e?E.apply(G):E}D+=d(E)+'</span>\n <span class="display-application-links">\n ';E=t.each.call(G,G.links,{hash:{},inverse:s.noop,fn:s.program(22,h,F),data:F});if(E||E===0){D+=E}D+="\n </span>\n </div>\n ";return D}function h(H,G){var D="",F,E;D+='\n <a target="';if(F=t.target){F=F.call(H,{hash:{},data:G})}else{F=H.target;F=typeof F===e?F.apply(H):F}D+=d(F)+'" href="';if(F=t.href){F=F.call(H,{hash:{},data:G})}else{F=H.href;F=typeof F===e?F.apply(H):F}D+=d(F)+'">';E={hash:{},inverse:s.noop,fn:s.program(23,g,G),data:G};if(F=t.local){F=F.call(H,E)}else{F=H.local;F=typeof F===e?F.apply(H):F}if(!t.local){F=c.call(H,F,E)}if(F||F===0){D+=F}D+="</a>\n ";return D}function g(F,E){var D;if(D=t.text){D=D.call(F,{hash:{},data:E})}else{D=F.text;D=typeof D===e?D.apply(F):D}return d(D)}function f(G,F){var D="",E;D+='\n <pre class="peek">';if(E=t.peek){E=E.call(G,{hash:{},data:F})}else{E=G.peek;E=typeof E===e?E.apply(G):E}if(E||E===0){D+=E}D+="</pre>\n ";return D}u+='<div class="dataset-body">\n ';l=t["if"].call(v,v.body,{hash:{},inverse:s.program(3,q,C),fn:s.program(1,r,C),data:C});if(l||l===0){u+=l}u+="\n</div>";return u})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-body"]=b(function(h,u,s,n,B){this.compilerInfo=[4,">= 1.0.0"];s=this.merge(s,h.helpers);B=B||{};var t="",j,e="function",d=this.escapeExpression,r=this,c=s.blockHelperMissing;function q(F,E){var C="",D;C+='\n <div class="dataset-summary">\n ';if(D=s.body){D=D.call(F,{hash:{},data:E})}else{D=F.body;D=typeof D===e?D.apply(F):D}if(D||D===0){C+=D}C+='\n </div>\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';return C}function p(F,E){var C="",D;C+='\n <div class="dataset-summary">\n ';D=s["if"].call(F,F.misc_blurb,{hash:{},inverse:r.noop,fn:r.program(4,o,E),data:E});if(D||D===0){C+=D}C+="\n\n ";D=s["if"].call(F,F.data_type,{hash:{},inverse:r.noop,fn:r.program(6,m,E),data:E});if(D||D===0){C+=D}C+="\n\n ";D=s["if"].call(F,F.metadata_dbkey,{hash:{},inverse:r.noop,fn:r.program(9,g,E),data:E});if(D||D===0){C+=D}C+="\n\n ";D=s["if"].call(F,F.misc_info,{hash:{},inverse:r.noop,fn:r.program(12,z,E),data:E});if(D||D===0){C+=D}C+='\n </div>\n\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';D=s.unless.call(F,F.deleted,{hash:{},inverse:r.noop,fn:r.program(14,y,E),data:E});if(D||D===0){C+=D}C+="\n\n ";return C}function o(F,E){var C="",D;C+='\n <div class="dataset-blurb">\n <span class="value">';if(D=s.misc_blurb){D=D.call(F,{hash:{},data:E})}else{D=F.misc_blurb;D=typeof D===e?D.apply(F):D}C+=d(D)+"</span>\n </div>\n ";return C}function m(G,F){var C="",E,D;C+='\n <div class="dataset-datatype">\n <label class="prompt">';D={hash:{},inverse:r.noop,fn:r.program(7,l,F),data:F};if(E=s.local){E=E.call(G,D)}else{E=G.local;E=typeof E===e?E.apply(G):E}if(!s.local){E=c.call(G,E,D)}if(E||E===0){C+=E}C+='</label>\n <span class="value">';if(E=s.data_type){E=E.call(G,{hash:{},data:F})}else{E=G.data_type;E=typeof E===e?E.apply(G):E}C+=d(E)+"</span>\n </div>\n ";return C}function l(D,C){return"format"}function g(G,F){var C="",E,D;C+='\n <div class="dataset-dbkey">\n <label class="prompt">';D={hash:{},inverse:r.noop,fn:r.program(10,A,F),data:F};if(E=s.local){E=E.call(G,D)}else{E=G.local;E=typeof E===e?E.apply(G):E}if(!s.local){E=c.call(G,E,D)}if(E||E===0){C+=E}C+='</label>\n <span class="value">\n ';if(E=s.metadata_dbkey){E=E.call(G,{hash:{},data:F})}else{E=G.metadata_dbkey;E=typeof E===e?E.apply(G):E}C+=d(E)+"\n </span>\n </div>\n ";return C}function A(D,C){return"database"}function z(F,E){var C="",D;C+='\n <div class="dataset-info">\n <span class="value">';if(D=s.misc_info){D=D.call(F,{hash:{},data:E})}else{D=F.misc_info;D=typeof D===e?D.apply(F):D}C+=d(D)+"</span>\n </div>\n ";return C}function y(G,F){var C="",E,D;C+='\n \n <div class="tags-display"></div>\n\n \n <div class="annotation-display">\n <label class="prompt">';D={hash:{},inverse:r.noop,fn:r.program(15,x,F),data:F};if(E=s.local){E=E.call(G,D)}else{E=G.local;E=typeof E===e?E.apply(G):E}if(!s.local){E=c.call(G,E,D)}if(E||E===0){C+=E}C+='</label>\n <div id="dataset-';if(E=s.id){E=E.call(G,{hash:{},data:F})}else{E=G.id;E=typeof E===e?E.apply(G):E}C+=d(E)+'-annotation" class="annotation editable-text"\n title="';D={hash:{},inverse:r.noop,fn:r.program(17,w,F),data:F};if(E=s.local){E=E.call(G,D)}else{E=G.local;E=typeof E===e?E.apply(G):E}if(!s.local){E=c.call(G,E,D)}if(E||E===0){C+=E}C+='"></div>\n </div>\n\n <div class="dataset-display-applications">\n ';E=s.each.call(G,G.display_apps,{hash:{},inverse:r.noop,fn:r.program(19,v,F),data:F});if(E||E===0){C+=E}C+="\n\n ";E=s.each.call(G,G.display_types,{hash:{},inverse:r.noop,fn:r.program(19,v,F),data:F});if(E||E===0){C+=E}C+='\n </div>\n\n <div class="dataset-peek">\n ';E=s["if"].call(G,G.peek,{hash:{},inverse:r.noop,fn:r.program(23,f,F),data:F});if(E||E===0){C+=E}C+="\n </div>\n\n ";return C}function x(D,C){return"Annotation"}function w(D,C){return"Edit dataset annotation"}function v(F,E){var C="",D;C+='\n <div class="display-application">\n <span class="display-application-location">';if(D=s.label){D=D.call(F,{hash:{},data:E})}else{D=F.label;D=typeof D===e?D.apply(F):D}C+=d(D)+'</span>\n <span class="display-application-links">\n ';D=s.each.call(F,F.links,{hash:{},inverse:r.noop,fn:r.program(20,k,E),data:E});if(D||D===0){C+=D}C+="\n </span>\n </div>\n ";return C}function k(G,F){var C="",E,D;C+='\n <a target="';if(E=s.target){E=E.call(G,{hash:{},data:F})}else{E=G.target;E=typeof E===e?E.apply(G):E}C+=d(E)+'" href="';if(E=s.href){E=E.call(G,{hash:{},data:F})}else{E=G.href;E=typeof E===e?E.apply(G):E}C+=d(E)+'">';D={hash:{},inverse:r.noop,fn:r.program(21,i,F),data:F};if(E=s.local){E=E.call(G,D)}else{E=G.local;E=typeof E===e?E.apply(G):E}if(!s.local){E=c.call(G,E,D)}if(E||E===0){C+=E}C+="</a>\n ";return C}function i(E,D){var C;if(C=s.text){C=C.call(E,{hash:{},data:D})}else{C=E.text;C=typeof C===e?C.apply(E):C}return d(C)}function f(F,E){var C="",D;C+='\n <pre class="peek">';if(D=s.peek){D=D.call(F,{hash:{},data:E})}else{D=F.peek;D=typeof D===e?D.apply(F):D}if(D||D===0){C+=D}C+="</pre>\n ";return C}t+='<div class="dataset-body">\n ';j=s["if"].call(u,u.body,{hash:{},inverse:r.program(3,p,B),fn:r.program(1,q,B),data:B});if(j||j===0){t+=j}t+="\n</div>";return t})})();
\ No newline at end of file
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/packed/templates/compiled/template-history-historyPanel.js
--- a/static/scripts/packed/templates/compiled/template-history-historyPanel.js
+++ b/static/scripts/packed/templates/compiled/template-history-historyPanel.js
@@ -1,1 +1,1 @@
-(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(i,u,s,n,A){this.compilerInfo=[4,">= 1.0.0"];s=this.merge(s,i.helpers);A=A||{};var t="",k,f,r=this,e="function",c=s.blockHelperMissing,d=this.escapeExpression;function q(F,E){var B="",D,C;B+='\n <div class="history-name" title="';C={hash:{},inverse:r.noop,fn:r.program(2,p,E),data:E};if(D=s.local){D=D.call(F,C)}else{D=F.local;D=typeof D===e?D.apply(F):D}if(!s.local){D=c.call(F,D,C)}if(D||D===0){B+=D}B+='">\n ';if(D=s.name){D=D.call(F,{hash:{},data:E})}else{D=F.name;D=typeof D===e?D.apply(F):D}B+=d(D)+"\n </div>\n ";return B}function p(C,B){return"Click to rename history"}function o(E,D){var B="",C;B+='\n <div class="history-size">';if(C=s.nice_size){C=C.call(E,{hash:{},data:D})}else{C=E.nice_size;C=typeof C===e?C.apply(E):C}B+=d(C)+"</div>\n ";return B}function m(F,E){var B="",D,C;B+='\n <div class="warningmessagesmall"><strong>\n ';C={hash:{},inverse:r.noop,fn:r.program(7,l,E),data:E};if(D=s.local){D=D.call(F,C)}else{D=F.local;D=typeof D===e?D.apply(F):D}if(!s.local){D=c.call(F,D,C)}if(D||D===0){B+=D}B+="\n </strong></div>\n ";return B}function l(C,B){return"You are currently viewing a deleted history!"}function h(E,D){var B="",C;B+='\n \n <div class="';if(C=s.status){C=C.call(E,{hash:{},data:D})}else{C=E.status;C=typeof C===e?C.apply(E):C}B+=d(C)+'message">';if(C=s.message){C=C.call(E,{hash:{},data:D})}else{C=E.message;C=typeof C===e?C.apply(E):C}B+=d(C)+"</div>\n ";return B}function z(C,B){return"You are over your disk quota"}function y(C,B){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function x(C,B){return"Tags"}function w(C,B){return"Annotation"}function v(C,B){return"Click to edit annotation"}function j(D,C){var B;if(B=s.annotation){B=B.call(D,{hash:{},data:C})}else{B=D.annotation;B=typeof B===e?B.apply(D):B}return d(B)}function g(C,B){return"Your history is empty. Click 'Get Data' on the left pane to start"}t+='<div class="history-controls">\n\n <div class="history-title">\n ';k=s["if"].call(u,u.name,{hash:{},inverse:r.noop,fn:r.program(1,q,A),data:A});if(k||k===0){t+=k}t+='\n </div>\n\n <div class="history-subtitle clear">\n ';k=s["if"].call(u,u.nice_size,{hash:{},inverse:r.noop,fn:r.program(4,o,A),data:A});if(k||k===0){t+=k}t+='\n\n <div class="history-secondary-actions">\n </div>\n </div>\n\n ';k=s["if"].call(u,u.deleted,{hash:{},inverse:r.noop,fn:r.program(6,m,A),data:A});if(k||k===0){t+=k}t+='\n\n <div class="message-container">\n ';k=s["if"].call(u,u.message,{hash:{},inverse:r.noop,fn:r.program(9,h,A),data:A});if(k||k===0){t+=k}t+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:r.noop,fn:r.program(11,z,A),data:A};if(k=s.local){k=k.call(u,f)}else{k=u.local;k=typeof k===e?k.apply(u):k}if(!s.local){k=c.call(u,k,f)}if(k||k===0){t+=k}t+=".\n ";f={hash:{},inverse:r.noop,fn:r.program(13,y,A),data:A};if(k=s.local){k=k.call(u,f)}else{k=u.local;k=typeof k===e?k.apply(u):k}if(!s.local){k=c.call(u,k,f)}if(k||k===0){t+=k}t+='.\n </div>\n \n \n \n <div class="tags-display">\n <label class="prompt">';f={hash:{},inverse:r.noop,fn:r.program(15,x,A),data:A};if(k=s.local){k=k.call(u,f)}else{k=u.local;k=typeof k===e?k.apply(u):k}if(!s.local){k=c.call(u,k,f)}if(k||k===0){t+=k}t+='</label>\n <div class="tags"></div>\n </div>\n\n \n <div class="annotation-display">\n <label class="prompt">';f={hash:{},inverse:r.noop,fn:r.program(17,w,A),data:A};if(k=s.local){k=k.call(u,f)}else{k=u.local;k=typeof k===e?k.apply(u):k}if(!s.local){k=c.call(u,k,f)}if(k||k===0){t+=k}t+='</label>\n <div class="annotation" title="';f={hash:{},inverse:r.noop,fn:r.program(19,v,A),data:A};if(k=s.local){k=k.call(u,f)}else{k=u.local;k=typeof k===e?k.apply(u):k}if(!s.local){k=c.call(u,k,f)}if(k||k===0){t+=k}t+='"\n \n >';k=s["if"].call(u,u.annotation,{hash:{},inverse:r.noop,fn:r.program(21,j,A),data:A});if(k||k===0){t+=k}t+='</div>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:r.noop,fn:r.program(23,g,A),data:A};if(k=s.local){k=k.call(u,f)}else{k=u.local;k=typeof k===e?k.apply(u):k}if(!s.local){k=c.call(u,k,f)}if(k||k===0){t+=k}t+="\n </div>";return t})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(h,t,r,m,z){this.compilerInfo=[4,">= 1.0.0"];r=this.merge(r,h.helpers);z=z||{};var s="",j,f,q=this,e="function",c=r.blockHelperMissing,d=this.escapeExpression;function p(E,D){var A="",C,B;A+='\n <div class="history-name" title="';B={hash:{},inverse:q.noop,fn:q.program(2,o,D),data:D};if(C=r.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!r.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+='">\n ';if(C=r.name){C=C.call(E,{hash:{},data:D})}else{C=E.name;C=typeof C===e?C.apply(E):C}A+=d(C)+"\n </div>\n ";return A}function o(B,A){return"Click to rename history"}function n(D,C){var A="",B;A+='\n <div class="history-size">';if(B=r.nice_size){B=B.call(D,{hash:{},data:C})}else{B=D.nice_size;B=typeof B===e?B.apply(D):B}A+=d(B)+"</div>\n ";return A}function l(E,D){var A="",C,B;A+='\n <div class="warningmessagesmall"><strong>\n ';B={hash:{},inverse:q.noop,fn:q.program(7,k,D),data:D};if(C=r.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!r.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+="\n </strong></div>\n ";return A}function k(B,A){return"You are currently viewing a deleted history!"}function g(D,C){var A="",B;A+='\n \n <div class="';if(B=r.status){B=B.call(D,{hash:{},data:C})}else{B=D.status;B=typeof B===e?B.apply(D):B}A+=d(B)+'message">';if(B=r.message){B=B.call(D,{hash:{},data:C})}else{B=D.message;B=typeof B===e?B.apply(D):B}A+=d(B)+"</div>\n ";return A}function y(B,A){return"You are over your disk quota"}function x(B,A){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function w(B,A){return"Annotation"}function v(B,A){return"Click to edit annotation"}function u(C,B){var A;if(A=r.annotation){A=A.call(C,{hash:{},data:B})}else{A=C.annotation;A=typeof A===e?A.apply(C):A}return d(A)}function i(B,A){return"Your history is empty. Click 'Get Data' on the left pane to start"}s+='<div class="history-controls">\n\n <div class="history-title">\n ';j=r["if"].call(t,t.name,{hash:{},inverse:q.noop,fn:q.program(1,p,z),data:z});if(j||j===0){s+=j}s+='\n </div>\n\n <div class="history-subtitle clear">\n ';j=r["if"].call(t,t.nice_size,{hash:{},inverse:q.noop,fn:q.program(4,n,z),data:z});if(j||j===0){s+=j}s+='\n\n <div class="history-secondary-actions">\n </div>\n </div>\n\n ';j=r["if"].call(t,t.deleted,{hash:{},inverse:q.noop,fn:q.program(6,l,z),data:z});if(j||j===0){s+=j}s+='\n\n <div class="message-container">\n ';j=r["if"].call(t,t.message,{hash:{},inverse:q.noop,fn:q.program(9,g,z),data:z});if(j||j===0){s+=j}s+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:q.noop,fn:q.program(11,y,z),data:z};if(j=r.local){j=j.call(t,f)}else{j=t.local;j=typeof j===e?j.apply(t):j}if(!r.local){j=c.call(t,j,f)}if(j||j===0){s+=j}s+=".\n ";f={hash:{},inverse:q.noop,fn:q.program(13,x,z),data:z};if(j=r.local){j=j.call(t,f)}else{j=t.local;j=typeof j===e?j.apply(t):j}if(!r.local){j=c.call(t,j,f)}if(j||j===0){s+=j}s+='.\n </div>\n \n \n \n <div class="tags-display"></div>\n\n \n <div class="annotation-display">\n <label class="prompt">';f={hash:{},inverse:q.noop,fn:q.program(15,w,z),data:z};if(j=r.local){j=j.call(t,f)}else{j=t.local;j=typeof j===e?j.apply(t):j}if(!r.local){j=c.call(t,j,f)}if(j||j===0){s+=j}s+='</label>\n <div class="annotation" title="';f={hash:{},inverse:q.noop,fn:q.program(17,v,z),data:z};if(j=r.local){j=j.call(t,f)}else{j=t.local;j=typeof j===e?j.apply(t):j}if(!r.local){j=c.call(t,j,f)}if(j||j===0){s+=j}s+='"\n \n >';j=r["if"].call(t,t.annotation,{hash:{},inverse:q.noop,fn:q.program(19,u,z),data:z});if(j||j===0){s+=j}s+='</div>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:q.noop,fn:q.program(21,i,z),data:z};if(j=r.local){j=j.call(t,f)}else{j=t.local;j=typeof j===e?j.apply(t):j}if(!r.local){j=c.call(t,j,f)}if(j||j===0){s+=j}s+="\n </div>";return s})})();
\ No newline at end of file
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/templates/compiled/history-templates.js
--- a/static/scripts/templates/compiled/history-templates.js
+++ b/static/scripts/templates/compiled/history-templates.js
@@ -108,37 +108,31 @@
var buffer = "", stack1, options;
buffer += "\n "
- + "\n <div class=\"tags-display\">\n <label class=\"prompt\">";
+ + "\n <div class=\"tags-display\"></div>\n\n "
+ + "\n <div class=\"annotation-display\">\n <label class=\"prompt\">";
options = {hash:{},inverse:self.noop,fn:self.program(15, program15, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "</label>\n <div class=\"tags\"></div>\n </div>\n\n "
- + "\n <div class=\"annotation-display\">\n <label class=\"prompt\">";
- options = {hash:{},inverse:self.noop,fn:self.program(17, program17, data),data:data};
- if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
- else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
- if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "</label>\n <div id=\"dataset-";
if (stack1 = helpers.id) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
buffer += escapeExpression(stack1)
+ "-annotation\" class=\"annotation editable-text\"\n title=\"";
- options = {hash:{},inverse:self.noop,fn:self.program(19, program19, data),data:data};
+ options = {hash:{},inverse:self.noop,fn:self.program(17, program17, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\"></div>\n </div>\n\n <div class=\"dataset-display-applications\">\n ";
- stack1 = helpers.each.call(depth0, depth0.display_apps, {hash:{},inverse:self.noop,fn:self.program(21, program21, data),data:data});
+ stack1 = helpers.each.call(depth0, depth0.display_apps, {hash:{},inverse:self.noop,fn:self.program(19, program19, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n\n ";
- stack1 = helpers.each.call(depth0, depth0.display_types, {hash:{},inverse:self.noop,fn:self.program(21, program21, data),data:data});
+ stack1 = helpers.each.call(depth0, depth0.display_types, {hash:{},inverse:self.noop,fn:self.program(19, program19, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n </div>\n\n <div class=\"dataset-peek\">\n ";
- stack1 = helpers['if'].call(depth0, depth0.peek, {hash:{},inverse:self.noop,fn:self.program(25, program25, data),data:data});
+ stack1 = helpers['if'].call(depth0, depth0.peek, {hash:{},inverse:self.noop,fn:self.program(23, program23, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n </div>\n\n ";
return buffer;
@@ -146,35 +140,29 @@
function program15(depth0,data) {
- return "Tags";
+ return "Annotation";
}
function program17(depth0,data) {
- return "Annotation";
+ return "Edit dataset annotation";
}
function program19(depth0,data) {
-
- return "Edit dataset annotation";
- }
-
-function program21(depth0,data) {
-
var buffer = "", stack1;
buffer += "\n <div class=\"display-application\">\n <span class=\"display-application-location\">";
if (stack1 = helpers.label) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
else { stack1 = depth0.label; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
buffer += escapeExpression(stack1)
+ "</span>\n <span class=\"display-application-links\">\n ";
- stack1 = helpers.each.call(depth0, depth0.links, {hash:{},inverse:self.noop,fn:self.program(22, program22, data),data:data});
+ stack1 = helpers.each.call(depth0, depth0.links, {hash:{},inverse:self.noop,fn:self.program(20, program20, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n </span>\n </div>\n ";
return buffer;
}
-function program22(depth0,data) {
+function program20(depth0,data) {
var buffer = "", stack1, options;
buffer += "\n <a target=\"";
@@ -186,7 +174,7 @@
else { stack1 = depth0.href; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
buffer += escapeExpression(stack1)
+ "\">";
- options = {hash:{},inverse:self.noop,fn:self.program(23, program23, data),data:data};
+ options = {hash:{},inverse:self.noop,fn:self.program(21, program21, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
@@ -194,7 +182,7 @@
buffer += "</a>\n ";
return buffer;
}
-function program23(depth0,data) {
+function program21(depth0,data) {
var stack1;
if (stack1 = helpers.text) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
@@ -202,7 +190,7 @@
return escapeExpression(stack1);
}
-function program25(depth0,data) {
+function program23(depth0,data) {
var buffer = "", stack1;
buffer += "\n <pre class=\"peek\">";
@@ -886,30 +874,24 @@
function program15(depth0,data) {
- return "Tags";
+ return "Annotation";
}
function program17(depth0,data) {
- return "Annotation";
+ return "Click to edit annotation";
}
function program19(depth0,data) {
-
- return "Click to edit annotation";
- }
-
-function program21(depth0,data) {
-
var stack1;
if (stack1 = helpers.annotation) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
else { stack1 = depth0.annotation; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
return escapeExpression(stack1);
}
-function program23(depth0,data) {
+function program21(depth0,data) {
return "Your history is empty. Click 'Get Data' on the left pane to start";
@@ -941,33 +923,27 @@
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += ".\n </div>\n \n "
+ "\n "
- + "\n <div class=\"tags-display\">\n <label class=\"prompt\">";
+ + "\n <div class=\"tags-display\"></div>\n\n "
+ + "\n <div class=\"annotation-display\">\n <label class=\"prompt\">";
options = {hash:{},inverse:self.noop,fn:self.program(15, program15, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "</label>\n <div class=\"tags\"></div>\n </div>\n\n "
- + "\n <div class=\"annotation-display\">\n <label class=\"prompt\">";
+ buffer += "</label>\n <div class=\"annotation\" title=\"";
options = {hash:{},inverse:self.noop,fn:self.program(17, program17, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "</label>\n <div class=\"annotation\" title=\"";
- options = {hash:{},inverse:self.noop,fn:self.program(19, program19, data),data:data};
- if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
- else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
- if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\"\n "
+ "\n >";
- stack1 = helpers['if'].call(depth0, depth0.annotation, {hash:{},inverse:self.noop,fn:self.program(21, program21, data),data:data});
+ stack1 = helpers['if'].call(depth0, depth0.annotation, {hash:{},inverse:self.noop,fn:self.program(19, program19, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "</div>\n </div>\n\n </div>"
+ "\n\n "
+ "\n <div class=\"datasets-list\"></div>\n\n <div class=\"empty-history-message infomessagesmall\">\n ";
- options = {hash:{},inverse:self.noop,fn:self.program(23, program23, data),data:data};
+ options = {hash:{},inverse:self.noop,fn:self.program(21, program21, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/templates/compiled/template-hda-body.js
--- a/static/scripts/templates/compiled/template-hda-body.js
+++ b/static/scripts/templates/compiled/template-hda-body.js
@@ -108,37 +108,31 @@
var buffer = "", stack1, options;
buffer += "\n "
- + "\n <div class=\"tags-display\">\n <label class=\"prompt\">";
+ + "\n <div class=\"tags-display\"></div>\n\n "
+ + "\n <div class=\"annotation-display\">\n <label class=\"prompt\">";
options = {hash:{},inverse:self.noop,fn:self.program(15, program15, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "</label>\n <div class=\"tags\"></div>\n </div>\n\n "
- + "\n <div class=\"annotation-display\">\n <label class=\"prompt\">";
- options = {hash:{},inverse:self.noop,fn:self.program(17, program17, data),data:data};
- if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
- else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
- if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "</label>\n <div id=\"dataset-";
if (stack1 = helpers.id) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
buffer += escapeExpression(stack1)
+ "-annotation\" class=\"annotation editable-text\"\n title=\"";
- options = {hash:{},inverse:self.noop,fn:self.program(19, program19, data),data:data};
+ options = {hash:{},inverse:self.noop,fn:self.program(17, program17, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\"></div>\n </div>\n\n <div class=\"dataset-display-applications\">\n ";
- stack1 = helpers.each.call(depth0, depth0.display_apps, {hash:{},inverse:self.noop,fn:self.program(21, program21, data),data:data});
+ stack1 = helpers.each.call(depth0, depth0.display_apps, {hash:{},inverse:self.noop,fn:self.program(19, program19, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n\n ";
- stack1 = helpers.each.call(depth0, depth0.display_types, {hash:{},inverse:self.noop,fn:self.program(21, program21, data),data:data});
+ stack1 = helpers.each.call(depth0, depth0.display_types, {hash:{},inverse:self.noop,fn:self.program(19, program19, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n </div>\n\n <div class=\"dataset-peek\">\n ";
- stack1 = helpers['if'].call(depth0, depth0.peek, {hash:{},inverse:self.noop,fn:self.program(25, program25, data),data:data});
+ stack1 = helpers['if'].call(depth0, depth0.peek, {hash:{},inverse:self.noop,fn:self.program(23, program23, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n </div>\n\n ";
return buffer;
@@ -146,35 +140,29 @@
function program15(depth0,data) {
- return "Tags";
+ return "Annotation";
}
function program17(depth0,data) {
- return "Annotation";
+ return "Edit dataset annotation";
}
function program19(depth0,data) {
-
- return "Edit dataset annotation";
- }
-
-function program21(depth0,data) {
-
var buffer = "", stack1;
buffer += "\n <div class=\"display-application\">\n <span class=\"display-application-location\">";
if (stack1 = helpers.label) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
else { stack1 = depth0.label; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
buffer += escapeExpression(stack1)
+ "</span>\n <span class=\"display-application-links\">\n ";
- stack1 = helpers.each.call(depth0, depth0.links, {hash:{},inverse:self.noop,fn:self.program(22, program22, data),data:data});
+ stack1 = helpers.each.call(depth0, depth0.links, {hash:{},inverse:self.noop,fn:self.program(20, program20, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n </span>\n </div>\n ";
return buffer;
}
-function program22(depth0,data) {
+function program20(depth0,data) {
var buffer = "", stack1, options;
buffer += "\n <a target=\"";
@@ -186,7 +174,7 @@
else { stack1 = depth0.href; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
buffer += escapeExpression(stack1)
+ "\">";
- options = {hash:{},inverse:self.noop,fn:self.program(23, program23, data),data:data};
+ options = {hash:{},inverse:self.noop,fn:self.program(21, program21, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
@@ -194,7 +182,7 @@
buffer += "</a>\n ";
return buffer;
}
-function program23(depth0,data) {
+function program21(depth0,data) {
var stack1;
if (stack1 = helpers.text) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
@@ -202,7 +190,7 @@
return escapeExpression(stack1);
}
-function program25(depth0,data) {
+function program23(depth0,data) {
var buffer = "", stack1;
buffer += "\n <pre class=\"peek\">";
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/templates/compiled/template-history-historyPanel.js
--- a/static/scripts/templates/compiled/template-history-historyPanel.js
+++ b/static/scripts/templates/compiled/template-history-historyPanel.js
@@ -87,30 +87,24 @@
function program15(depth0,data) {
- return "Tags";
+ return "Annotation";
}
function program17(depth0,data) {
- return "Annotation";
+ return "Click to edit annotation";
}
function program19(depth0,data) {
-
- return "Click to edit annotation";
- }
-
-function program21(depth0,data) {
-
var stack1;
if (stack1 = helpers.annotation) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
else { stack1 = depth0.annotation; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
return escapeExpression(stack1);
}
-function program23(depth0,data) {
+function program21(depth0,data) {
return "Your history is empty. Click 'Get Data' on the left pane to start";
@@ -142,33 +136,27 @@
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += ".\n </div>\n \n "
+ "\n "
- + "\n <div class=\"tags-display\">\n <label class=\"prompt\">";
+ + "\n <div class=\"tags-display\"></div>\n\n "
+ + "\n <div class=\"annotation-display\">\n <label class=\"prompt\">";
options = {hash:{},inverse:self.noop,fn:self.program(15, program15, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "</label>\n <div class=\"tags\"></div>\n </div>\n\n "
- + "\n <div class=\"annotation-display\">\n <label class=\"prompt\">";
+ buffer += "</label>\n <div class=\"annotation\" title=\"";
options = {hash:{},inverse:self.noop,fn:self.program(17, program17, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "</label>\n <div class=\"annotation\" title=\"";
- options = {hash:{},inverse:self.noop,fn:self.program(19, program19, data),data:data};
- if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
- else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
- if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\"\n "
+ "\n >";
- stack1 = helpers['if'].call(depth0, depth0.annotation, {hash:{},inverse:self.noop,fn:self.program(21, program21, data),data:data});
+ stack1 = helpers['if'].call(depth0, depth0.annotation, {hash:{},inverse:self.noop,fn:self.program(19, program19, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "</div>\n </div>\n\n </div>"
+ "\n\n "
+ "\n <div class=\"datasets-list\"></div>\n\n <div class=\"empty-history-message infomessagesmall\">\n ";
- options = {hash:{},inverse:self.noop,fn:self.program(23, program23, data),data:data};
+ options = {hash:{},inverse:self.noop,fn:self.program(21, program21, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/templates/hda-templates.html
--- a/static/scripts/templates/hda-templates.html
+++ b/static/scripts/templates/hda-templates.html
@@ -98,10 +98,7 @@
{{#unless deleted }}
{{! TODO: move to mvc/tag.js }}
- <div class="tags-display">
- <label class="prompt">{{#local}}Tags{{/local}}</label>
- <div class="tags"></div>
- </div>
+ <div class="tags-display"></div>
{{! TODO: move to mvc/annotations.js }}
<div class="annotation-display">
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/scripts/templates/history-templates.html
--- a/static/scripts/templates/history-templates.html
+++ b/static/scripts/templates/history-templates.html
@@ -40,10 +40,7 @@
{{! tags and annotations }}
{{! TODO: move to mvc/tag.js }}
- <div class="tags-display">
- <label class="prompt">{{#local}}Tags{{/local}}</label>
- <div class="tags"></div>
- </div>
+ <div class="tags-display"></div>
{{! TODO: move to mvc/annotations.js }}
<div class="annotation-display">
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/style/blue/base.css
--- a/static/style/blue/base.css
+++ b/static/style/blue/base.css
@@ -1524,6 +1524,8 @@
.history-panel .history-controls .quota-message{display:none;margin:8px 0px 5px 0px}
.history-panel .history-controls .tags-display,.history-panel .history-controls .annotation-display{display:none;margin-bottom:8px}.history-panel .history-controls .tags-display label,.history-panel .history-controls .annotation-display label{display:block;margin:0px;padding:0px;font-weight:normal;font-style:italic;font-size:90%;color:#555}
.history-panel .history-controls .tags-display label:after,.history-panel .history-controls .annotation-display label:after{content:':'}
+.history-panel .history-controls .tags-display .select2-container{min-width:0px}.history-panel .history-controls .tags-display .select2-container .select2-choices{border-radius:3px}
+.history-panel .history-controls .tags-display input{border-radius:3px}
.history-panel .history-controls .annotation-display .annotation{border-radius:3px;border:1px solid fadout(#999, 50%);padding:4px;white-space:pre-line}
.history-panel .history-controls .annotation-display textarea{margin:0px 0px 2px 0px;display:block;border-radius:3px;width:100%}
.history-panel .dataset{border-width:1px 0px 0px 0px}
@@ -1550,7 +1552,7 @@
.dataset .dataset-body .dataset-actions .left .icon-btn-group{margin-right:2px}.dataset .dataset-body .dataset-actions .left .icon-btn-group .icon-btn{margin-right:0}
.dataset .dataset-body .dataset-actions .left:not(:empty){margin-bottom:8px}
.dataset .dataset-body .dataset-actions .right{float:right}.dataset .dataset-body .dataset-actions .right .icon-btn{margin-left:2px}
-.dataset .dataset-body .tags-display{display:none;margin-bottom:8px}
+.dataset .dataset-body .tags-display{display:none;margin-bottom:8px}.dataset .dataset-body .tags-display .select2-container{min-width:0px}.dataset .dataset-body .tags-display .select2-container .select2-choices{border-radius:3px}
.dataset .dataset-body .annotation-display{display:none;margin-bottom:8px}.dataset .dataset-body .annotation-display .annotation{border-radius:3px;border:1px solid rgba(153,153,153,0.30000000000000004);padding:4px;white-space:pre-line}
.dataset .dataset-body .annotation-display textarea{margin:0px 0px 2px 0px;display:block;border-radius:3px;width:100%}
.dataset .dataset-body .dataset-display-applications .display-application:last-child{margin-bottom:8px}
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 static/style/src/less/history.less
--- a/static/style/src/less/history.less
+++ b/static/style/src/less/history.less
@@ -134,6 +134,17 @@
content: ':';
}
}
+ .tags-display {
+ .select2-container {
+ min-width: 0px;
+ .select2-choices {
+ border-radius: 3px;
+ }
+ }
+ input {
+ border-radius: 3px;
+ }
+ }
.annotation-display {
.annotation {
border-radius: 3px;
@@ -321,6 +332,12 @@
.tags-display {
display: none;
.vertical-spacing;
+ .select2-container {
+ min-width: 0px;
+ .select2-choices {
+ border-radius: 3px;
+ }
+ }
}
.annotation-display {
display: none;
diff -r 26ada521210ef52684dda3e394521bcfba23b5da -r d69e53660b28c5b84a9b6606b7ab989303cc74c6 templates/webapps/galaxy/history/history_panel.mako
--- a/templates/webapps/galaxy/history/history_panel.mako
+++ b/templates/webapps/galaxy/history/history_panel.mako
@@ -90,7 +90,7 @@
## ----------------------------------------------------------------------------- generic 'base' function
<%def name="history_panel_javascripts()">
${h.js(
- "libs/jquery/jquery.autocomplete", "galaxy.autocom_tagging"
+ "mvc/tags"
)}
##TODO: concat these
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: Tags: user: cache a list of tags used in Galaxy.currUser and add to users.show; Histories, HDAs: allow setting and getting of tags (in bulk) via update
by commits-noreply@bitbucket.org 01 Nov '13
by commits-noreply@bitbucket.org 01 Nov '13
01 Nov '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/26ada521210e/
Changeset: 26ada521210e
User: carlfeberhard
Date: 2013-11-01 20:16:50
Summary: Tags: user: cache a list of tags used in Galaxy.currUser and add to users.show; Histories, HDAs: allow setting and getting of tags (in bulk) via update
Affected #: 6 files
diff -r ef83037950ea0e867271999e2cc1902bd9f65302 -r 26ada521210ef52684dda3e394521bcfba23b5da lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1751,6 +1751,14 @@
misc_info = hda.info,
misc_blurb = hda.blurb )
+ tags_str_list = []
+ for tag in self.tags:
+ tag_str = tag.user_tname
+ if tag.value is not None:
+ tag_str += ":" + tag.user_value
+ tags_str_list.append( tag_str )
+ rval[ 'tags' ] = tags_str_list
+
if hda.copied_from_library_dataset_dataset_association is not None:
rval['copied_from_ldda_id'] = hda.copied_from_library_dataset_dataset_association.id
diff -r ef83037950ea0e867271999e2cc1902bd9f65302 -r 26ada521210ef52684dda3e394521bcfba23b5da lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -452,7 +452,8 @@
if 'annotation' in new_data.keys() and trans.get_user():
history.add_item_annotation( trans.sa_session, trans.get_user(), history, new_data[ 'annotation' ] )
changed[ 'annotation' ] = new_data[ 'annotation' ]
- # tags
+ if 'tags' in new_data.keys() and trans.get_user():
+ self.set_tags_from_list( trans, history, new_data[ 'tags' ], user=trans.user )
# importable (ctrl.history.set_accessible_async)
# sharing/permissions?
# slugs?
@@ -715,7 +716,8 @@
if 'annotation' in new_data.keys() and trans.get_user():
hda.add_item_annotation( trans.sa_session, trans.get_user(), hda, new_data[ 'annotation' ] )
changed[ 'annotation' ] = new_data[ 'annotation' ]
- # tags
+ if 'tags' in new_data.keys() and trans.get_user():
+ self.set_tags_from_list( trans, hda, new_data[ 'tags' ], user=trans.user )
# sharing/permissions?
# purged
@@ -2340,6 +2342,49 @@
log.debug( "In get_item_tag_assoc with tagged_item %s" % tagged_item )
return self.get_tag_handler( trans )._get_item_tag_assoc( user, tagged_item, tag_name )
+ def set_tags_from_list( self, trans, item, new_tags_list, user=None ):
+ #precondition: item is already security checked against user
+ #precondition: incoming tags is a list of sanitized/formatted strings
+ user = user or trans.user
+
+ # based on controllers/tag retag_async: delete all old, reset to entire new
+ trans.app.tag_handler.delete_item_tags( trans, user, item )
+ new_tags_str = ','.join( new_tags_list )
+ trans.app.tag_handler.apply_item_tags( trans, user, item, new_tags_str.encode( 'utf-8' ) )
+ trans.sa_session.flush()
+ return item.tags
+
+ def get_user_tags_used( self, trans, user=None ):
+ """
+ Return a list of distinct 'user_tname:user_value' strings that the
+ given user has used.
+
+ user defaults to trans.user.
+ Returns an empty list if no user is given and trans.user is anonymous.
+ """
+ #TODO: for lack of a UsesUserMixin - placing this here - maybe into UsesTags, tho
+ user = user or trans.user
+ if not user:
+ return []
+
+ # get all the taggable model TagAssociations
+ tag_models = [ v.tag_assoc_class for v in trans.app.tag_handler.item_tag_assoc_info.values() ]
+ # create a union of subqueries for each for this user - getting only the tname and user_value
+ all_tags_query = None
+ for tag_model in tag_models:
+ subq = ( trans.sa_session.query( tag_model.user_tname, tag_model.user_value )
+ .filter( tag_model.user == trans.user ) )
+ all_tags_query = subq if all_tags_query is None else all_tags_query.union( subq )
+
+ # if nothing init'd the query, bail
+ if all_tags_query is None:
+ return []
+
+ # boil the tag tuples down into a sorted list of DISTINCT name:val strings
+ tags = all_tags_query.distinct().all()
+ tags = [( ( name + ':' + val ) if val else name ) for name, val in tags ]
+ return sorted( tags )
+
class UsesExtendedMetadataMixin( SharableItemSecurityMixin ):
diff -r ef83037950ea0e867271999e2cc1902bd9f65302 -r 26ada521210ef52684dda3e394521bcfba23b5da lib/galaxy/webapps/galaxy/api/histories.py
--- a/lib/galaxy/webapps/galaxy/api/histories.py
+++ b/lib/galaxy/webapps/galaxy/api/histories.py
@@ -11,14 +11,14 @@
from galaxy import web
from galaxy.util import string_as_bool, restore_text
from galaxy.util.sanitize_html import sanitize_html
-from galaxy.web.base.controller import BaseAPIController, UsesHistoryMixin
+from galaxy.web.base.controller import BaseAPIController, UsesHistoryMixin, UsesTagsMixin
from galaxy.web import url_for
from galaxy.model.orm import desc
import logging
log = logging.getLogger( __name__ )
-class HistoriesController( BaseAPIController, UsesHistoryMixin ):
+class HistoriesController( BaseAPIController, UsesHistoryMixin, UsesTagsMixin ):
@web.expose_api_anonymous
def index( self, trans, deleted='False', **kwd ):
@@ -369,6 +369,9 @@
if not ( isinstance( val, str ) or isinstance( val, unicode ) ):
raise ValueError( 'annotation must be a string or unicode: %s' %( str( type( val ) ) ) )
validated_payload[ 'annotation' ] = sanitize_html( val, 'utf-8' )
+ elif key == 'tags':
+ if isinstance( val, list ):
+ validated_payload[ 'tags' ] = [ sanitize_html( t, 'utf-8' ) for t in val ]
elif key not in valid_but_uneditable_keys:
pass
#log.warn( 'unknown key: %s', str( key ) )
diff -r ef83037950ea0e867271999e2cc1902bd9f65302 -r 26ada521210ef52684dda3e394521bcfba23b5da lib/galaxy/webapps/galaxy/api/history_contents.py
--- a/lib/galaxy/webapps/galaxy/api/history_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/history_contents.py
@@ -4,9 +4,10 @@
import logging
from galaxy import exceptions, util, web
-from galaxy.web.base.controller import (BaseAPIController, url_for,
+from galaxy.web.base.controller import ( BaseAPIController, url_for,
UsesHistoryDatasetAssociationMixin, UsesHistoryMixin, UsesLibraryMixin,
- UsesLibraryMixinItems)
+ UsesLibraryMixinItems, UsesTagsMixin )
+from galaxy.util.sanitize_html import sanitize_html
log = logging.getLogger( __name__ )
@@ -413,6 +414,9 @@
if not ( isinstance( val, str ) or isinstance( val, unicode ) ):
raise ValueError( 'misc_info must be a string or unicode: %s' %( str( type( val ) ) ) )
validated_payload[ 'info' ] = util.sanitize_html.sanitize_html( val, 'utf-8' )
+ elif key == 'tags':
+ if isinstance( val, list ):
+ validated_payload[ 'tags' ] = [ sanitize_html( t, 'utf-8' ) for t in val ]
elif key not in valid_but_uneditable_keys:
pass
#log.warn( 'unknown key: %s', str( key ) )
diff -r ef83037950ea0e867271999e2cc1902bd9f65302 -r 26ada521210ef52684dda3e394521bcfba23b5da lib/galaxy/webapps/galaxy/api/users.py
--- a/lib/galaxy/webapps/galaxy/api/users.py
+++ b/lib/galaxy/webapps/galaxy/api/users.py
@@ -71,6 +71,7 @@
raise HTTPBadRequest( detail='Invalid user id ( %s ) specified' % id )
item = user.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id,
'total_disk_usage': float } )
+ item[ 'tags_used' ] = self.get_user_tags_used( trans, user=user )
#TODO: move into api_values (needs trans, tho - can we do that with api_keys/@property??)
#TODO: works with other users (from admin)??
item['quota_percent'] = trans.app.quota_agent.get_percent( trans=trans )
diff -r ef83037950ea0e867271999e2cc1902bd9f65302 -r 26ada521210ef52684dda3e394521bcfba23b5da templates/webapps/galaxy/base_panels.mako
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -18,7 +18,9 @@
if trans.user:
user_dict = trans.user.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id,
'total_disk_usage': float } )
+ ##TODO: move these into to_dict
user_dict['quota_percent'] = trans.app.quota_agent.get_percent( trans=trans )
+ user_dict['tags_used'] = trans.webapp.controllers[ 'tag' ].get_user_tags_used( trans )
else:
usage = 0
percent = None
@@ -33,6 +35,7 @@
'nice_total_disk_usage' : util.nice_size( usage ),
'quota_percent' : percent
}
+
%>
${h.to_json_string( user_dict )}
</%def>
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
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/7c60a5b67c3e/
Changeset: 7c60a5b67c3e
Branch: next-stable
User: greg
Date: 2013-11-01 16:53:44
Summary: Return an error response rather than just logging a message when uploading a directroy or a tarball that contains a tool dependency definition that is missing wither an <actions> tag set or an <actions_group> tag set.
Affected #: 1 file
diff -r a1b7ad9035bf945f78edfb84204d746fd74a8929 -r 7c60a5b67c3ec7825d3d2511f27867823de19350 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
@@ -295,9 +295,8 @@
elif os.path.split( uploaded_file_name )[ -1 ] == suc.TOOL_DEPENDENCY_DEFINITION_FILENAME:
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
- # Can this be displayed?
if error_message:
- log.debug( str( 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 )
@@ -360,8 +359,7 @@
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
if error_message:
- # Can this be displayed?
- log.debug( 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 )
https://bitbucket.org/galaxy/galaxy-central/commits/ef83037950ea/
Changeset: ef83037950ea
User: greg
Date: 2013-11-01 16:54:18
Summary: Merged from next-stable
Affected #: 1 file
diff -r c4a7154555192be09051a3fbb1ad655a3926be3b -r ef83037950ea0e867271999e2cc1902bd9f65302 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
@@ -309,9 +309,8 @@
elif os.path.split( uploaded_file_name )[ -1 ] == suc.TOOL_DEPENDENCY_DEFINITION_FILENAME:
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
- # Can this be displayed?
if error_message:
- log.debug( str( 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 )
@@ -378,8 +377,7 @@
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
if error_message:
- # Can this be displayed?
- log.debug( 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 )
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
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a1b7ad9035bf/
Changeset: a1b7ad9035bf
Branch: next-stable
User: greg
Date: 2013-11-01 16:38:04
Summary: Fixes for tool_dependencies.xml files whose recipe is missing either an <actions> tag set or an <actions_group> tag set. Upon upload of a single file, an appropriate error message will be displayed (currently it is logged when uploading a tarball). In either case, when the containing repository is installed, the tool dependency will be set to an error state and and the error message will be associated with the dependency record.
Affected #: 6 files
diff -r 4c1879921d9a0454879e84b3d3c064bad10c008b -r a1b7ad9035bf945f78edfb84204d746fd74a8929 lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -250,8 +250,11 @@
removed = False
if removed:
tool_shed_repository.uninstalled = True
- # Remove all installed tool dependencies, but don't touch any repository dependencies..
- for tool_dependency in tool_shed_repository.installed_tool_dependencies:
+ # Remove all installed tool dependencies and tool dependencies stuck in the INSTALLING state, but don't touch any
+ # repository dependencies.
+ tool_dependencies_to_uninstall = tool_shed_repository.installed_tool_dependencies
+ tool_dependencies_to_uninstall.extend( tool_shed_repository.tool_dependencies_being_installed )
+ for tool_dependency in tool_dependencies_to_uninstall:
uninstalled, error_message = tool_dependency_util.remove_tool_dependency( trans.app, tool_dependency )
if error_message:
errors = '%s %s' % ( errors, error_message )
@@ -433,7 +436,7 @@
tool_dependencies=tool_dependencies )
for installed_tool_dependency in installed_tool_dependencies:
if installed_tool_dependency.status == trans.app.model.ToolDependency.installation_status.ERROR:
- message += ' %s' % suc.to_html_string( installed_tool_dependency.error_message )
+ message += ' %s' % str( installed_tool_dependency.error_message )
tool_dependency_ids = [ trans.security.encode_id( td.id ) for td in tool_dependencies ]
if message:
status = 'error'
diff -r 4c1879921d9a0454879e84b3d3c064bad10c008b -r a1b7ad9035bf945f78edfb84204d746fd74a8929 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
@@ -144,12 +144,17 @@
elif uploaded_file_filename in [ suc.TOOL_DEPENDENCY_DEFINITION_FILENAME ]:
# Inspect the contents of the file to see if it defines a complex repository dependency definition whose changeset_revision values
# are missing and if so, set them appropriately.
- altered, root_elem = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
- if altered:
- tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
- shutil.move( tmp_filename, full_path )
- else:
- shutil.move( uploaded_file_name, full_path )
+ altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
+ if error_message:
+ ok = False
+ message = error_message
+ status = 'error'
+ if ok:
+ if altered:
+ tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
+ shutil.move( tmp_filename, full_path )
+ else:
+ shutil.move( uploaded_file_name, full_path )
else:
shutil.move( uploaded_file_name, full_path )
# See if any admin users have chosen to receive email alerts when a repository is updated. If so, check every uploaded file to ensure
@@ -289,7 +294,10 @@
shutil.move( tmp_filename, uploaded_file_name )
elif os.path.split( uploaded_file_name )[ -1 ] == suc.TOOL_DEPENDENCY_DEFINITION_FILENAME:
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
- altered, root_elem = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
+ altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
+ # Can this be displayed?
+ if error_message:
+ log.debug( str( error_message ) )
if altered:
tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
shutil.move( tmp_filename, uploaded_file_name )
@@ -350,7 +358,10 @@
shutil.move( tmp_filename, uploaded_file_name )
elif os.path.split( uploaded_file_name )[ -1 ] == suc.TOOL_DEPENDENCY_DEFINITION_FILENAME:
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
- altered, root_elem = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
+ altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
+ if error_message:
+ # Can this be displayed?
+ log.debug( error_message )
if altered:
tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
shutil.move( tmp_filename, uploaded_file_name )
diff -r 4c1879921d9a0454879e84b3d3c064bad10c008b -r a1b7ad9035bf945f78edfb84204d746fd74a8929 lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
@@ -340,7 +340,7 @@
version=package_version,
type='package',
status=app.model.ToolDependency.installation_status.INSTALLING,
- set_status=True )
+ set_status=False )
# Get the information about the current platform in case the tool dependency definition includes tag sets for installing
# compiled binaries.
platform_info_dict = tool_dependency_util.get_platform_info_dict()
@@ -349,83 +349,93 @@
actions_elem_tuples = td_common_util.parse_package_elem( package_elem,
platform_info_dict=platform_info_dict,
include_after_install_actions=True )
- # At this point we have a list of <actions> elems that are either defined within an <actions_group> tag set with <actions>
- # sub-elements that contains os and architecture attributes filtered by the platform into which the appropriate compiled
- # binary will be installed, or not defined within an <actions_group> tag set and not filtered.
- binary_installed = False
- for in_actions_group, actions_elems in actions_elem_tuples:
- if in_actions_group:
- # Platform matching is only performed inside <actions_group> tag sets, os and architecture attributes are otherwise
- # ignored.
- for actions_elem in actions_elems:
- system = actions_elem.get( 'os' )
- architecture = actions_elem.get( 'architecture' )
- # If this <actions> element has the os and architecture attributes defined, then we only want to process until a
- # successful installation is achieved.
- if system and architecture:
- # If an <actions> tag has been defined that matches our current platform, and the recipe specified within
- # that <actions> tag has been successfully processed, skip any remaining platform-specific <actions> tags.
- # We cannot break out of the look here because there may be <action> tags at the end of the <actions_group>
- # tag set that must be processed.
- if binary_installed:
- continue
- # No platform-specific <actions> recipe has yet resulted in a successful installation.
- install_via_fabric( app,
- tool_dependency,
- install_dir,
- package_name=package_name,
- actions_elem=actions_elem,
- action_elem=None )
- sa_session.refresh( tool_dependency )
- if tool_dependency.status == app.model.ToolDependency.installation_status.INSTALLED:
- # If an <actions> tag was found that matches the current platform, and the install_via_fabric method
- # did not result in an error state, set binary_installed to True in order to skip any remaining
- # platform-specific <actions> tags.
- binary_installed = True
- else:
- # Process the next matching <actions> tag, or any defined <actions> tags that do not contain platform
- # dependent recipes.
- print 'Error downloading binary for %s version %s: %s' % \
- ( package_name, package_version, tool_dependency.error_message )
- else:
- # If no <actions> tags have been defined that match our current platform, or none of the matching
- # <actions> tags resulted in a successful tool dependency status, proceed with one and only one
- # <actions> tag that is not defined to be platform-specific.
- if not binary_installed:
- print 'Binary installation did not occur, so proceeding with install and compile recipe.'
- # Make sure to reset for installation if attempt at binary installation resulted in an error.
- if tool_dependency.status != app.model.ToolDependency.installation_status.NEVER_INSTALLED:
- removed, error_message = tool_dependency_util.remove_tool_dependency( app, tool_dependency )
+ if actions_elem_tuples:
+ # At this point we have a list of <actions> elems that are either defined within an <actions_group> tag set with <actions>
+ # sub-elements that contains os and architecture attributes filtered by the platform into which the appropriate compiled
+ # binary will be installed, or not defined within an <actions_group> tag set and not filtered.
+ binary_installed = False
+ for in_actions_group, actions_elems in actions_elem_tuples:
+ if in_actions_group:
+ # Platform matching is only performed inside <actions_group> tag sets, os and architecture attributes are otherwise
+ # ignored.
+ for actions_elem in actions_elems:
+ system = actions_elem.get( 'os' )
+ architecture = actions_elem.get( 'architecture' )
+ # If this <actions> element has the os and architecture attributes defined, then we only want to process until a
+ # successful installation is achieved.
+ if system and architecture:
+ # If an <actions> tag has been defined that matches our current platform, and the recipe specified within
+ # that <actions> tag has been successfully processed, skip any remaining platform-specific <actions> tags.
+ # We cannot break out of the look here because there may be <action> tags at the end of the <actions_group>
+ # tag set that must be processed.
+ if binary_installed:
+ continue
+ # No platform-specific <actions> recipe has yet resulted in a successful installation.
install_via_fabric( app,
tool_dependency,
install_dir,
package_name=package_name,
actions_elem=actions_elem,
action_elem=None )
- # Perform any final actions that have been defined within the actions_group tag set, but outside of
- # an <actions> tag, such as a set_environment entry, or a download_file or download_by_url command to
- # retrieve extra data for this tool dependency. Only do this if the tool dependency is not in an error
- # state, otherwise skip this action.
- if actions_elem.tag == 'action' and tool_dependency.status != app.model.ToolDependency.installation_status.ERROR:
- install_via_fabric( app,
- tool_dependency,
- install_dir,
- package_name=package_name,
- actions_elem=None,
- action_elem=actions_elem )
- else:
- # <actions> tags outside of an <actions_group> tag shall not check os or architecture, and if the attributes are
- # defined, they will be ignored. All <actions> tags outside of an <actions_group> tag set shall always be processed.
- # This is the default and original behavior of the install_package method.
- install_via_fabric( app,
- tool_dependency,
- install_dir,
- package_name=package_name,
- actions_elem=actions_elems,
- action_elem=None )
- sa_session.refresh( tool_dependency )
- if tool_dependency.status != app.model.ToolDependency.installation_status.ERROR:
- print package_name, 'version', package_version, 'installed in', install_dir
+ sa_session.refresh( tool_dependency )
+ if tool_dependency.status == app.model.ToolDependency.installation_status.INSTALLED:
+ # If an <actions> tag was found that matches the current platform, and the install_via_fabric method
+ # did not result in an error state, set binary_installed to True in order to skip any remaining
+ # platform-specific <actions> tags.
+ binary_installed = True
+ else:
+ # Process the next matching <actions> tag, or any defined <actions> tags that do not contain platform
+ # dependent recipes.
+ print 'Error downloading binary for %s version %s: %s' % \
+ ( package_name, package_version, tool_dependency.error_message )
+ else:
+ # If no <actions> tags have been defined that match our current platform, or none of the matching
+ # <actions> tags resulted in a successful tool dependency status, proceed with one and only one
+ # <actions> tag that is not defined to be platform-specific.
+ if not binary_installed:
+ print 'Binary installation did not occur, so proceeding with install and compile recipe.'
+ # Make sure to reset for installation if attempt at binary installation resulted in an error.
+ if tool_dependency.status != app.model.ToolDependency.installation_status.NEVER_INSTALLED:
+ removed, error_message = tool_dependency_util.remove_tool_dependency( app, tool_dependency )
+ install_via_fabric( app,
+ tool_dependency,
+ install_dir,
+ package_name=package_name,
+ actions_elem=actions_elem,
+ action_elem=None )
+ # Perform any final actions that have been defined within the actions_group tag set, but outside of
+ # an <actions> tag, such as a set_environment entry, or a download_file or download_by_url command to
+ # retrieve extra data for this tool dependency. Only do this if the tool dependency is not in an error
+ # state, otherwise skip this action.
+ if actions_elem.tag == 'action' and tool_dependency.status != app.model.ToolDependency.installation_status.ERROR:
+ install_via_fabric( app,
+ tool_dependency,
+ install_dir,
+ package_name=package_name,
+ actions_elem=None,
+ action_elem=actions_elem )
+ else:
+ # <actions> tags outside of an <actions_group> tag shall not check os or architecture, and if the attributes are
+ # defined, they will be ignored. All <actions> tags outside of an <actions_group> tag set shall always be processed.
+ # This is the default and original behavior of the install_package method.
+ install_via_fabric( app,
+ tool_dependency,
+ install_dir,
+ package_name=package_name,
+ actions_elem=actions_elems,
+ action_elem=None )
+ sa_session.refresh( tool_dependency )
+ if tool_dependency.status != app.model.ToolDependency.installation_status.ERROR:
+ print package_name, 'version', package_version, 'installed in', install_dir
+ else:
+ error_message = 'Version %s of the %s package cannot be installed because ' % ( str( package_version ), str( package_name ) )
+ error_message += 'the recipe for installing the package is missing either an <actions> tag set or an <actions_group> '
+ error_message += 'tag set.'
+ tool_dependency.status = app.model.ToolDependency.installation_status.ERROR
+ tool_dependency.error_message = error_message
+ sa_session.add( tool_dependency )
+ sa_session.flush()
+ return tool_dependency
else:
raise NotImplementedError( 'Only install version 1.0 is currently supported (i.e., change your tag to be <install version="1.0">).' )
elif package_elem.tag == 'readme':
diff -r 4c1879921d9a0454879e84b3d3c064bad10c008b -r a1b7ad9035bf945f78edfb84204d746fd74a8929 lib/tool_shed/util/commit_util.py
--- a/lib/tool_shed/util/commit_util.py
+++ b/lib/tool_shed/util/commit_util.py
@@ -344,10 +344,11 @@
Populate or unpopulate the tooshed and changeset_revision attributes of each <repository> tag defined within a tool_dependencies.xml file.
"""
altered = False
+ error_message = ''
# Make sure we're looking at a valid tool_dependencies.xml file.
tree, error_message = xml_util.parse_xml( tool_dependencies_config )
if tree is None:
- return False, None
+ return False, None, error_message
root = tree.getroot()
if root.tag == 'tool_dependency':
package_altered = False
@@ -425,14 +426,20 @@
action_index,
action_elem,
unpopulate=unpopulate )
+ else:
+ package_name = root_elem.get( 'name', '' )
+ package_version = root_elem.get( 'version', '' )
+ error_message = 'Version %s of the %s package cannot be installed because ' % ( str( package_version ), str( package_name ) )
+ error_message += 'the recipe for installing the package is missing either an <actions> tag set or an <actions_group> '
+ error_message += 'tag set.'
if package_altered:
package_elem[ actions_index ] = actions_elem
if package_altered:
root_elem[ package_index ] = package_elem
if package_altered:
root[ root_index ] = root_elem
- return altered, root
- return False, None
+ return altered, root, error_message
+ return False, None, error_message
def repository_tag_is_valid( filename, line ):
"""
diff -r 4c1879921d9a0454879e84b3d3c064bad10c008b -r a1b7ad9035bf945f78edfb84204d746fd74a8929 lib/tool_shed/util/export_util.py
--- a/lib/tool_shed/util/export_util.py
+++ b/lib/tool_shed/util/export_util.py
@@ -148,7 +148,9 @@
shutil.move( tmp_filename, full_path )
elif name == suc.TOOL_DEPENDENCY_DEFINITION_FILENAME:
# Eliminate the toolshed, and changeset_revision attributes from all <repository> tags.
- altered, root_elem = commit_util.handle_tool_dependencies_definition( trans, full_path, unpopulate=True )
+ altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, full_path, unpopulate=True )
+ if error_message:
+ return None, error_message
if altered:
tmp_filename = xml_util.create_and_write_tmp_file( root_elem, use_indent=True )
shutil.move( tmp_filename, full_path )
diff -r 4c1879921d9a0454879e84b3d3c064bad10c008b -r a1b7ad9035bf945f78edfb84204d746fd74a8929 lib/tool_shed/util/tool_dependency_util.py
--- a/lib/tool_shed/util/tool_dependency_util.py
+++ b/lib/tool_shed/util/tool_dependency_util.py
@@ -48,6 +48,7 @@
else:
tool_dependency = get_tool_dependency_by_name_type_repository( app, tool_shed_repository, name, type )
if tool_dependency:
+ # In some cases we should not override the current status of an existing tool_dependency, so do so only if set_status is True.
if set_status:
tool_dependency.status = status
else:
https://bitbucket.org/galaxy/galaxy-central/commits/c4a715455519/
Changeset: c4a715455519
User: greg
Date: 2013-11-01 16:38:38
Summary: Merged from next-stable
Affected #: 6 files
diff -r 4beddecc0ebc6bf13b5d00dc48cbabe65c401e10 -r c4a7154555192be09051a3fbb1ad655a3926be3b lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -250,8 +250,11 @@
removed = False
if removed:
tool_shed_repository.uninstalled = True
- # Remove all installed tool dependencies, but don't touch any repository dependencies..
- for tool_dependency in tool_shed_repository.installed_tool_dependencies:
+ # Remove all installed tool dependencies and tool dependencies stuck in the INSTALLING state, but don't touch any
+ # repository dependencies.
+ tool_dependencies_to_uninstall = tool_shed_repository.installed_tool_dependencies
+ tool_dependencies_to_uninstall.extend( tool_shed_repository.tool_dependencies_being_installed )
+ for tool_dependency in tool_dependencies_to_uninstall:
uninstalled, error_message = tool_dependency_util.remove_tool_dependency( trans.app, tool_dependency )
if error_message:
errors = '%s %s' % ( errors, error_message )
@@ -433,7 +436,7 @@
tool_dependencies=tool_dependencies )
for installed_tool_dependency in installed_tool_dependencies:
if installed_tool_dependency.status == trans.app.model.ToolDependency.installation_status.ERROR:
- message += ' %s' % suc.to_html_string( installed_tool_dependency.error_message )
+ message += ' %s' % str( installed_tool_dependency.error_message )
tool_dependency_ids = [ trans.security.encode_id( td.id ) for td in tool_dependencies ]
if message:
status = 'error'
diff -r 4beddecc0ebc6bf13b5d00dc48cbabe65c401e10 -r c4a7154555192be09051a3fbb1ad655a3926be3b 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
@@ -150,12 +150,17 @@
elif uploaded_file_filename in [ suc.TOOL_DEPENDENCY_DEFINITION_FILENAME ]:
# Inspect the contents of the file to see if it defines a complex repository dependency definition whose changeset_revision values
# are missing and if so, set them appropriately.
- altered, root_elem = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
- if altered:
- tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
- shutil.move( tmp_filename, full_path )
- else:
- shutil.move( uploaded_file_name, full_path )
+ altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
+ if error_message:
+ ok = False
+ message = error_message
+ status = 'error'
+ if ok:
+ if altered:
+ tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
+ shutil.move( tmp_filename, full_path )
+ else:
+ shutil.move( uploaded_file_name, full_path )
else:
shutil.move( uploaded_file_name, full_path )
if ok:
@@ -303,7 +308,10 @@
shutil.move( tmp_filename, uploaded_file_name )
elif os.path.split( uploaded_file_name )[ -1 ] == suc.TOOL_DEPENDENCY_DEFINITION_FILENAME:
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
- altered, root_elem = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
+ altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
+ # Can this be displayed?
+ if error_message:
+ log.debug( str( error_message ) )
if altered:
tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
shutil.move( tmp_filename, uploaded_file_name )
@@ -368,7 +376,10 @@
shutil.move( tmp_filename, uploaded_file_name )
elif os.path.split( uploaded_file_name )[ -1 ] == suc.TOOL_DEPENDENCY_DEFINITION_FILENAME:
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
- altered, root_elem = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
+ altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
+ if error_message:
+ # Can this be displayed?
+ log.debug( error_message )
if altered:
tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
shutil.move( tmp_filename, uploaded_file_name )
diff -r 4beddecc0ebc6bf13b5d00dc48cbabe65c401e10 -r c4a7154555192be09051a3fbb1ad655a3926be3b lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
@@ -340,7 +340,7 @@
version=package_version,
type='package',
status=app.model.ToolDependency.installation_status.INSTALLING,
- set_status=True )
+ set_status=False )
# Get the information about the current platform in case the tool dependency definition includes tag sets for installing
# compiled binaries.
platform_info_dict = tool_dependency_util.get_platform_info_dict()
@@ -349,83 +349,93 @@
actions_elem_tuples = td_common_util.parse_package_elem( package_elem,
platform_info_dict=platform_info_dict,
include_after_install_actions=True )
- # At this point we have a list of <actions> elems that are either defined within an <actions_group> tag set with <actions>
- # sub-elements that contains os and architecture attributes filtered by the platform into which the appropriate compiled
- # binary will be installed, or not defined within an <actions_group> tag set and not filtered.
- binary_installed = False
- for in_actions_group, actions_elems in actions_elem_tuples:
- if in_actions_group:
- # Platform matching is only performed inside <actions_group> tag sets, os and architecture attributes are otherwise
- # ignored.
- for actions_elem in actions_elems:
- system = actions_elem.get( 'os' )
- architecture = actions_elem.get( 'architecture' )
- # If this <actions> element has the os and architecture attributes defined, then we only want to process until a
- # successful installation is achieved.
- if system and architecture:
- # If an <actions> tag has been defined that matches our current platform, and the recipe specified within
- # that <actions> tag has been successfully processed, skip any remaining platform-specific <actions> tags.
- # We cannot break out of the look here because there may be <action> tags at the end of the <actions_group>
- # tag set that must be processed.
- if binary_installed:
- continue
- # No platform-specific <actions> recipe has yet resulted in a successful installation.
- install_via_fabric( app,
- tool_dependency,
- install_dir,
- package_name=package_name,
- actions_elem=actions_elem,
- action_elem=None )
- sa_session.refresh( tool_dependency )
- if tool_dependency.status == app.model.ToolDependency.installation_status.INSTALLED:
- # If an <actions> tag was found that matches the current platform, and the install_via_fabric method
- # did not result in an error state, set binary_installed to True in order to skip any remaining
- # platform-specific <actions> tags.
- binary_installed = True
- else:
- # Process the next matching <actions> tag, or any defined <actions> tags that do not contain platform
- # dependent recipes.
- print 'Error downloading binary for %s version %s: %s' % \
- ( package_name, package_version, tool_dependency.error_message )
- else:
- # If no <actions> tags have been defined that match our current platform, or none of the matching
- # <actions> tags resulted in a successful tool dependency status, proceed with one and only one
- # <actions> tag that is not defined to be platform-specific.
- if not binary_installed:
- print 'Binary installation did not occur, so proceeding with install and compile recipe.'
- # Make sure to reset for installation if attempt at binary installation resulted in an error.
- if tool_dependency.status != app.model.ToolDependency.installation_status.NEVER_INSTALLED:
- removed, error_message = tool_dependency_util.remove_tool_dependency( app, tool_dependency )
+ if actions_elem_tuples:
+ # At this point we have a list of <actions> elems that are either defined within an <actions_group> tag set with <actions>
+ # sub-elements that contains os and architecture attributes filtered by the platform into which the appropriate compiled
+ # binary will be installed, or not defined within an <actions_group> tag set and not filtered.
+ binary_installed = False
+ for in_actions_group, actions_elems in actions_elem_tuples:
+ if in_actions_group:
+ # Platform matching is only performed inside <actions_group> tag sets, os and architecture attributes are otherwise
+ # ignored.
+ for actions_elem in actions_elems:
+ system = actions_elem.get( 'os' )
+ architecture = actions_elem.get( 'architecture' )
+ # If this <actions> element has the os and architecture attributes defined, then we only want to process until a
+ # successful installation is achieved.
+ if system and architecture:
+ # If an <actions> tag has been defined that matches our current platform, and the recipe specified within
+ # that <actions> tag has been successfully processed, skip any remaining platform-specific <actions> tags.
+ # We cannot break out of the look here because there may be <action> tags at the end of the <actions_group>
+ # tag set that must be processed.
+ if binary_installed:
+ continue
+ # No platform-specific <actions> recipe has yet resulted in a successful installation.
install_via_fabric( app,
tool_dependency,
install_dir,
package_name=package_name,
actions_elem=actions_elem,
action_elem=None )
- # Perform any final actions that have been defined within the actions_group tag set, but outside of
- # an <actions> tag, such as a set_environment entry, or a download_file or download_by_url command to
- # retrieve extra data for this tool dependency. Only do this if the tool dependency is not in an error
- # state, otherwise skip this action.
- if actions_elem.tag == 'action' and tool_dependency.status != app.model.ToolDependency.installation_status.ERROR:
- install_via_fabric( app,
- tool_dependency,
- install_dir,
- package_name=package_name,
- actions_elem=None,
- action_elem=actions_elem )
- else:
- # <actions> tags outside of an <actions_group> tag shall not check os or architecture, and if the attributes are
- # defined, they will be ignored. All <actions> tags outside of an <actions_group> tag set shall always be processed.
- # This is the default and original behavior of the install_package method.
- install_via_fabric( app,
- tool_dependency,
- install_dir,
- package_name=package_name,
- actions_elem=actions_elems,
- action_elem=None )
- sa_session.refresh( tool_dependency )
- if tool_dependency.status != app.model.ToolDependency.installation_status.ERROR:
- print package_name, 'version', package_version, 'installed in', install_dir
+ sa_session.refresh( tool_dependency )
+ if tool_dependency.status == app.model.ToolDependency.installation_status.INSTALLED:
+ # If an <actions> tag was found that matches the current platform, and the install_via_fabric method
+ # did not result in an error state, set binary_installed to True in order to skip any remaining
+ # platform-specific <actions> tags.
+ binary_installed = True
+ else:
+ # Process the next matching <actions> tag, or any defined <actions> tags that do not contain platform
+ # dependent recipes.
+ print 'Error downloading binary for %s version %s: %s' % \
+ ( package_name, package_version, tool_dependency.error_message )
+ else:
+ # If no <actions> tags have been defined that match our current platform, or none of the matching
+ # <actions> tags resulted in a successful tool dependency status, proceed with one and only one
+ # <actions> tag that is not defined to be platform-specific.
+ if not binary_installed:
+ print 'Binary installation did not occur, so proceeding with install and compile recipe.'
+ # Make sure to reset for installation if attempt at binary installation resulted in an error.
+ if tool_dependency.status != app.model.ToolDependency.installation_status.NEVER_INSTALLED:
+ removed, error_message = tool_dependency_util.remove_tool_dependency( app, tool_dependency )
+ install_via_fabric( app,
+ tool_dependency,
+ install_dir,
+ package_name=package_name,
+ actions_elem=actions_elem,
+ action_elem=None )
+ # Perform any final actions that have been defined within the actions_group tag set, but outside of
+ # an <actions> tag, such as a set_environment entry, or a download_file or download_by_url command to
+ # retrieve extra data for this tool dependency. Only do this if the tool dependency is not in an error
+ # state, otherwise skip this action.
+ if actions_elem.tag == 'action' and tool_dependency.status != app.model.ToolDependency.installation_status.ERROR:
+ install_via_fabric( app,
+ tool_dependency,
+ install_dir,
+ package_name=package_name,
+ actions_elem=None,
+ action_elem=actions_elem )
+ else:
+ # <actions> tags outside of an <actions_group> tag shall not check os or architecture, and if the attributes are
+ # defined, they will be ignored. All <actions> tags outside of an <actions_group> tag set shall always be processed.
+ # This is the default and original behavior of the install_package method.
+ install_via_fabric( app,
+ tool_dependency,
+ install_dir,
+ package_name=package_name,
+ actions_elem=actions_elems,
+ action_elem=None )
+ sa_session.refresh( tool_dependency )
+ if tool_dependency.status != app.model.ToolDependency.installation_status.ERROR:
+ print package_name, 'version', package_version, 'installed in', install_dir
+ else:
+ error_message = 'Version %s of the %s package cannot be installed because ' % ( str( package_version ), str( package_name ) )
+ error_message += 'the recipe for installing the package is missing either an <actions> tag set or an <actions_group> '
+ error_message += 'tag set.'
+ tool_dependency.status = app.model.ToolDependency.installation_status.ERROR
+ tool_dependency.error_message = error_message
+ sa_session.add( tool_dependency )
+ sa_session.flush()
+ return tool_dependency
else:
raise NotImplementedError( 'Only install version 1.0 is currently supported (i.e., change your tag to be <install version="1.0">).' )
elif package_elem.tag == 'readme':
diff -r 4beddecc0ebc6bf13b5d00dc48cbabe65c401e10 -r c4a7154555192be09051a3fbb1ad655a3926be3b lib/tool_shed/util/commit_util.py
--- a/lib/tool_shed/util/commit_util.py
+++ b/lib/tool_shed/util/commit_util.py
@@ -344,10 +344,11 @@
Populate or unpopulate the tooshed and changeset_revision attributes of each <repository> tag defined within a tool_dependencies.xml file.
"""
altered = False
+ error_message = ''
# Make sure we're looking at a valid tool_dependencies.xml file.
tree, error_message = xml_util.parse_xml( tool_dependencies_config )
if tree is None:
- return False, None
+ return False, None, error_message
root = tree.getroot()
if root.tag == 'tool_dependency':
package_altered = False
@@ -425,14 +426,20 @@
action_index,
action_elem,
unpopulate=unpopulate )
+ else:
+ package_name = root_elem.get( 'name', '' )
+ package_version = root_elem.get( 'version', '' )
+ error_message = 'Version %s of the %s package cannot be installed because ' % ( str( package_version ), str( package_name ) )
+ error_message += 'the recipe for installing the package is missing either an <actions> tag set or an <actions_group> '
+ error_message += 'tag set.'
if package_altered:
package_elem[ actions_index ] = actions_elem
if package_altered:
root_elem[ package_index ] = package_elem
if package_altered:
root[ root_index ] = root_elem
- return altered, root
- return False, None
+ return altered, root, error_message
+ return False, None, error_message
def repository_tag_is_valid( filename, line ):
"""
diff -r 4beddecc0ebc6bf13b5d00dc48cbabe65c401e10 -r c4a7154555192be09051a3fbb1ad655a3926be3b lib/tool_shed/util/export_util.py
--- a/lib/tool_shed/util/export_util.py
+++ b/lib/tool_shed/util/export_util.py
@@ -150,7 +150,9 @@
shutil.move( tmp_filename, full_path )
elif name == suc.TOOL_DEPENDENCY_DEFINITION_FILENAME:
# Eliminate the toolshed, and changeset_revision attributes from all <repository> tags.
- altered, root_elem = commit_util.handle_tool_dependencies_definition( trans, full_path, unpopulate=True )
+ altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, full_path, unpopulate=True )
+ if error_message:
+ return None, error_message
if altered:
tmp_filename = xml_util.create_and_write_tmp_file( root_elem, use_indent=True )
shutil.move( tmp_filename, full_path )
diff -r 4beddecc0ebc6bf13b5d00dc48cbabe65c401e10 -r c4a7154555192be09051a3fbb1ad655a3926be3b lib/tool_shed/util/tool_dependency_util.py
--- a/lib/tool_shed/util/tool_dependency_util.py
+++ b/lib/tool_shed/util/tool_dependency_util.py
@@ -48,6 +48,7 @@
else:
tool_dependency = get_tool_dependency_by_name_type_repository( app, tool_shed_repository, name, type )
if tool_dependency:
+ # In some cases we should not override the current status of an existing tool_dependency, so do so only if set_status is True.
if set_status:
tool_dependency.status = status
else:
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
5 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/b716eb3501ce/
Changeset: b716eb3501ce
User: jmchilton
Date: 2013-10-30 06:01:15
Summary: PEP-8 fixes and style touch-ups for test/functional/test_toolbox.py.
Affected #: 1 file
diff -r 77d58fdd1c2e0dfde374276209279c5e94b1e4c0 -r b716eb3501cebab20ee2ff2222310aff915750bb test/functional/test_toolbox.py
--- a/test/functional/test_toolbox.py
+++ b/test/functional/test_toolbox.py
@@ -1,6 +1,6 @@
-import sys, new
+import sys
+import new
from galaxy.tools.parameters import grouping
-from galaxy.tools.parameters import basic
from base.twilltestcase import TwillTestCase
import galaxy.model
from galaxy.model.orm import *
@@ -8,8 +8,10 @@
toolbox = None
+
class ToolTestCase( TwillTestCase ):
"""Abstract test case that runs tests based on a `galaxy.tools.test.ToolTest`"""
+
def do_it( self, testdef, shed_tool_id=None ):
# If the test generation had an error, raise
if testdef.error:
@@ -17,22 +19,23 @@
raise testdef.exception
else:
raise Exception( "Test parse failure" )
+
# Start with a new history
self.logout()
self.login( email='test(a)bx.psu.edu' )
- admin_user = sa_session.query( galaxy.model.User ).filter( galaxy.model.User.table.c.email=='test(a)bx.psu.edu' ).one()
+ admin_user = sa_session.query( galaxy.model.User ).filter( galaxy.model.User.table.c.email == 'test(a)bx.psu.edu' ).one()
self.new_history()
latest_history = sa_session.query( galaxy.model.History ) \
- .filter( and_( galaxy.model.History.table.c.deleted==False,
- galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .filter( and_( galaxy.model.History.table.c.deleted == False,
+ galaxy.model.History.table.c.user_id == admin_user.id ) ) \
.order_by( desc( galaxy.model.History.table.c.create_time ) ) \
.first()
assert latest_history is not None, "Problem retrieving latest_history from database"
if len( self.get_history_as_data_list() ) > 0:
raise AssertionError("ToolTestCase.do_it failed")
+
# Upload any needed files
for fname, extra in testdef.required_files:
- children = extra.get( 'children', [] )
metadata = extra.get( 'metadata', [] )
composite_data = extra.get( 'composite_data', [] )
self.upload_file( fname,
@@ -51,29 +54,34 @@
assert new_name, 'You must supply the new dataset name as the value tag of the edit_attributes tag'
hda_id = self.get_history_as_data_list()[-1].get( 'id' )
try:
- self.edit_hda_attribute_info( hda_id = str(hda_id), new_name = new_name )
+ self.edit_hda_attribute_info( hda_id=str(hda_id), new_name=new_name )
except:
- print "### call to edit_hda failed for hda_id %s, new_name=%s" % (hda_id,new_name)
+ print "### call to edit_hda failed for hda_id %s, new_name=%s" % (hda_id, new_name)
else:
raise Exception( 'edit_attributes type (%s) is unimplemented' % edit_att.get( 'type', None ) )
+
# We need to handle the case where we've uploaded a valid compressed file since the upload
# tool will have uncompressed it on the fly.
all_inputs = {}
for name, value, _ in testdef.inputs:
all_inputs[ name ] = value
+
# See if we have a grouping.Repeat element
repeat_name = None
for input_name, input_value in testdef.tool.inputs_by_page[0].items():
- if isinstance( input_value, grouping.Repeat ) and all_inputs.get( input_name, 1 ) not in [ 0, "0" ]: #default behavior is to test 1 repeat, for backwards compatibility
+ if isinstance( input_value, grouping.Repeat ) and all_inputs.get( input_name, 1 ) not in [ 0, "0" ]: # default behavior is to test 1 repeat, for backwards compatibility
repeat_name = input_name
break
+
#check if we need to verify number of outputs created dynamically by tool
if testdef.tool.force_history_refresh:
job_finish_by_output_count = len( self.get_history_as_data_list() )
else:
job_finish_by_output_count = False
+
# Do the first page
- page_inputs = self.__expand_grouping(testdef.tool.inputs_by_page[0], all_inputs)
+ page_inputs = self.__expand_grouping(testdef.tool.inputs_by_page[0], all_inputs)
+
# Run the tool
self.run_tool( testdef.tool.id, repeat_name=repeat_name, **page_inputs )
print "page_inputs (0)", page_inputs
@@ -82,6 +90,7 @@
page_inputs = self.__expand_grouping(testdef.tool.inputs_by_page[i], all_inputs)
self.submit_form( **page_inputs )
print "page_inputs (%i)" % i, page_inputs
+
# Check the results ( handles single or multiple tool outputs ). Make sure to pass the correct hid.
# The output datasets from the tool should be in the same order as the testdef.outputs.
data_list = None
@@ -100,7 +109,7 @@
elem_index += 1
try:
self.verify_dataset_correctness( outfile, hid=elem_hid, maxseconds=testdef.maxseconds, attributes=attributes, shed_tool_id=shed_tool_id )
- except Exception, e:
+ except Exception:
print >>sys.stderr, self.get_job_stdout( elem.get( 'id' ), format=True )
print >>sys.stderr, self.get_job_stderr( elem.get( 'id' ), format=True )
raise
@@ -121,14 +130,14 @@
else:
expanded_inputs[ "%s|%s" % ( new_prefix, value.test_param.name ) ] = case.value
for input_name, input_value in case.inputs.items():
- expanded_inputs.update( self.__expand_grouping( { input_name:input_value }, declared_inputs, prefix = new_prefix ) )
+ expanded_inputs.update( self.__expand_grouping( { input_name: input_value }, declared_inputs, prefix=new_prefix ) )
elif isinstance( value, grouping.Repeat ):
- for repeat_index in xrange( 0, 1 ): #need to allow for and figure out how many repeats we have
+ for repeat_index in xrange( 0, 1 ): # need to allow for and figure out how many repeats we have
for r_name, r_value in value.inputs.iteritems():
- new_prefix = "%s_%d" % ( value.name, repeat_index )
- if prefix:
- new_prefix = "%s|%s" % ( prefix, new_prefix )
- expanded_inputs.update( self.__expand_grouping( { new_prefix : r_value }, declared_inputs, prefix = new_prefix ) )
+ new_prefix = "%s_%d" % ( value.name, repeat_index )
+ if prefix:
+ new_prefix = "%s|%s" % ( prefix, new_prefix )
+ expanded_inputs.update( self.__expand_grouping( { new_prefix : r_value }, declared_inputs, prefix=new_prefix ) )
elif value.name not in declared_inputs:
print "%s not declared in tool test, will not change default value." % value.name
elif isinstance(declared_inputs[value.name], str):
@@ -143,6 +152,7 @@
expanded_inputs[value.name] = declared_inputs[value.name]
return expanded_inputs
+
def build_tests( testing_shed_tools=False ):
"""
If the module level variable `toolbox` is set, generate `ToolTestCase`
@@ -151,12 +161,15 @@
"""
if toolbox is None:
return
+
# Push all the toolbox tests to module level
G = globals()
+
# Eliminate all previous tests from G.
for key, val in G.items():
if key.startswith( 'TestForTool_' ):
del G[ key ]
+
for i, tool_id in enumerate( toolbox.tools_by_id ):
tool = toolbox.get_tool( tool_id )
if tool.tests:
https://bitbucket.org/galaxy/galaxy-central/commits/4ef6fa54e0b9/
Changeset: 4ef6fa54e0b9
User: jmchilton
Date: 2013-10-30 06:01:15
Summary: PEP-8 fixes for scripts/functional_tests.py.
Still contains some unused imports. Perhaps these are reimported elsewhere?
Affected #: 1 file
diff -r b716eb3501cebab20ee2ff2222310aff915750bb -r 4ef6fa54e0b98e24979742c775c287cb9fbf3d27 scripts/functional_tests.py
--- a/scripts/functional_tests.py
+++ b/scripts/functional_tests.py
@@ -1,6 +1,10 @@
#!/usr/bin/env python
-import os, sys, shutil, tempfile, re
+import os
+import sys
+import shutil
+import tempfile
+import re
from ConfigParser import SafeConfigParser
# Assume we are run from the galaxy root directory, add lib to the python path
@@ -25,10 +29,17 @@
# http://code.google.com/p/python-nose/issues/detail?id=284
eggs.require( "pysqlite" )
-import atexit, logging, os, os.path, sys, tempfile
-import twill, unittest, time
-import subprocess, sys, threading, random
-import httplib, socket
+import atexit
+import logging
+import os.path
+import twill
+import unittest
+import time
+import subprocess
+import threading
+import random
+import httplib
+import socket
from paste import httpserver
import galaxy.app
from galaxy.app import UniverseApplication
@@ -56,6 +67,7 @@
# should this serve static resources (scripts, images, styles, etc.)
STATIC_ENABLED = True
+
def get_static_settings():
"""Returns dictionary of the settings necessary for a galaxy App
to be wrapped in the static middleware.
@@ -68,16 +80,17 @@
#TODO: these should be copied from universe_wsgi.ini
return dict(
#TODO: static_enabled needed here?
- static_enabled = True,
- static_cache_time = 360,
- static_dir = static_dir,
- static_images_dir = os.path.join( static_dir, 'images', '' ),
- static_favicon_dir = os.path.join( static_dir, 'favicon.ico' ),
- static_scripts_dir = os.path.join( static_dir, 'scripts', '' ),
- static_style_dir = os.path.join( static_dir, 'june_2007_style', 'blue' ),
- static_robots_txt = os.path.join( static_dir, 'robots.txt' ),
+ static_enabled=True,
+ static_cache_time=360,
+ static_dir=static_dir,
+ static_images_dir=os.path.join( static_dir, 'images', '' ),
+ static_favicon_dir=os.path.join( static_dir, 'favicon.ico' ),
+ static_scripts_dir=os.path.join( static_dir, 'scripts', '' ),
+ static_style_dir=os.path.join( static_dir, 'june_2007_style', 'blue' ),
+ static_robots_txt=os.path.join( static_dir, 'robots.txt' ),
)
+
def get_webapp_global_conf():
"""Get the global_conf dictionary sent as the first argument to app_factory.
"""
@@ -87,12 +100,13 @@
global_conf.update( get_static_settings() )
return global_conf
+
def generate_config_file( input_filename, output_filename, config_items ):
'''
Generate a config file with the configuration that has been defined for the embedded web application.
This is mostly relevant when setting metadata externally, since the script for doing that does not
have access to app.config.
- '''
+ '''
cp = SafeConfigParser()
cp.read( input_filename )
config_items_by_section = []
@@ -110,9 +124,9 @@
config_tuple = 'app:main', label, value
config_items_by_section.append( config_tuple )
print( config_items_by_section )
+
# Replace the default values with the provided configuration.
for section, label, value in config_items_by_section:
-
if cp.has_option( section, label ):
cp.remove_option( section, label )
cp.set( section, label, str( value ) )
@@ -120,6 +134,7 @@
cp.write( fh )
fh.close()
+
def run_tests( test_config ):
loader = nose.loader.TestLoader( config=test_config )
plug_loader = test_config.plugins.prepareTestLoader( loader )
@@ -134,7 +149,8 @@
test_runner = plug_runner
return test_runner.run( tests )
-def main():
+
+def main():
# ---- Configuration ------------------------------------------------------
galaxy_test_host = os.environ.get( 'GALAXY_TEST_HOST', default_galaxy_test_host )
galaxy_test_port = os.environ.get( 'GALAXY_TEST_PORT', None )
@@ -174,7 +190,7 @@
start_server = 'GALAXY_TEST_EXTERNAL' not in os.environ
if os.path.exists( 'tool_data_table_conf.test.xml' ):
tool_data_table_config_path = 'tool_data_table_conf.test.xml'
- else:
+ else:
tool_data_table_config_path = 'tool_data_table_conf.xml'
shed_tool_data_table_config = 'shed_tool_data_table_conf.xml'
tool_dependency_dir = os.environ.get( 'GALAXY_TOOL_DEPENDENCY_DIR', None )
@@ -182,7 +198,7 @@
galaxy_test_tmp_dir = os.environ.get( 'GALAXY_TEST_TMP_DIR', None )
if galaxy_test_tmp_dir is None:
galaxy_test_tmp_dir = tempfile.mkdtemp()
-
+
if start_server:
psu_production = False
galaxy_test_proxy_port = None
@@ -212,29 +228,29 @@
job_working_directory = os.path.join( new_file_path, 'job_working_directory' )
os.mkdir( cluster_files_directory )
os.mkdir( job_working_directory )
- kwargs = dict( database_engine_option_pool_size = '10',
- database_engine_option_max_overflow = '20',
- database_engine_option_strategy = 'threadlocal',
- nginx_x_accel_redirect_base = '/_x_accel_redirect',
- nginx_upload_store = nginx_upload_store,
- nginx_upload_path = '/_upload',
- allow_library_path_paste = 'True',
- cluster_files_directory = cluster_files_directory,
- job_working_directory = job_working_directory,
- outputs_to_working_directory = 'True',
- static_enabled = 'False',
- debug = 'False',
- track_jobs_in_database = 'True',
- job_scheduler_policy = 'FIFO',
- start_job_runners = 'pbs',
- default_cluster_job_runner = default_cluster_job_runner )
+ kwargs = dict( database_engine_option_pool_size='10',
+ database_engine_option_max_overflow='20',
+ database_engine_option_strategy='threadlocal',
+ nginx_x_accel_redirect_base='/_x_accel_redirect',
+ nginx_upload_store=nginx_upload_store,
+ nginx_upload_path='/_upload',
+ allow_library_path_paste='True',
+ cluster_files_directory=cluster_files_directory,
+ job_working_directory=job_working_directory,
+ outputs_to_working_directory='True',
+ static_enabled='False',
+ debug='False',
+ track_jobs_in_database='True',
+ job_scheduler_policy='FIFO',
+ start_job_runners='pbs',
+ default_cluster_job_runner=default_cluster_job_runner )
psu_production = True
else:
tempdir = tempfile.mkdtemp( dir=galaxy_test_tmp_dir )
# Configure the database path.
if 'GALAXY_TEST_DBPATH' in os.environ:
galaxy_db_path = os.environ[ 'GALAXY_TEST_DBPATH' ]
- else:
+ else:
galaxy_db_path = os.path.join( tempdir, 'database' )
# Configure the paths Galaxy needs to test tools.
file_path = os.path.join( galaxy_db_path, 'files' )
@@ -252,33 +268,33 @@
except OSError:
pass
- # ---- Build Application --------------------------------------------------
- app = None
+ # ---- Build Application --------------------------------------------------
+ app = None
if start_server:
- kwargs = dict( admin_users = 'test(a)bx.psu.edu',
- allow_library_path_paste = True,
- allow_user_creation = True,
- allow_user_deletion = True,
- database_connection = database_connection,
- datatype_converters_config_file = "datatype_converters_conf.xml.sample",
- file_path = file_path,
- id_secret = 'changethisinproductiontoo',
- job_queue_workers = 5,
- job_working_directory = job_working_directory,
- library_import_dir = library_import_dir,
- log_destination = "stdout",
- new_file_path = new_file_path,
- running_functional_tests = True,
- shed_tool_data_table_config = shed_tool_data_table_config,
- template_path = "templates",
- test_conf = "test.conf",
- tool_config_file = tool_config_file,
- tool_data_table_config_path = tool_data_table_config_path,
- tool_path = tool_path,
- tool_parse_help = False,
- update_integrated_tool_panel = False,
- use_heartbeat = False,
- user_library_import_dir = user_library_import_dir )
+ kwargs = dict( admin_users='test(a)bx.psu.edu',
+ allow_library_path_paste=True,
+ allow_user_creation=True,
+ allow_user_deletion=True,
+ database_connection=database_connection,
+ datatype_converters_config_file="datatype_converters_conf.xml.sample",
+ file_path=file_path,
+ id_secret='changethisinproductiontoo',
+ job_queue_workers=5,
+ job_working_directory=job_working_directory,
+ library_import_dir=library_import_dir,
+ log_destination="stdout",
+ new_file_path=new_file_path,
+ running_functional_tests=True,
+ shed_tool_data_table_config=shed_tool_data_table_config,
+ template_path="templates",
+ test_conf="test.conf",
+ tool_config_file=tool_config_file,
+ tool_data_table_config_path=tool_data_table_config_path,
+ tool_path=tool_path,
+ tool_parse_help=False,
+ update_integrated_tool_panel=False,
+ use_heartbeat=False,
+ user_library_import_dir=user_library_import_dir )
if psu_production:
kwargs[ 'global_conf' ] = None
if not database_connection.startswith( 'sqlite://' ):
@@ -309,7 +325,7 @@
# ---- Run webserver ------------------------------------------------------
server = None
-
+
if start_server:
webapp = buildapp.app_factory( kwargs[ 'global_conf' ], app=app,
use_translogger=False, static_enabled=STATIC_ENABLED )
@@ -338,7 +354,7 @@
t.start()
# Test if the server is up
for i in range( 10 ):
- conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_port ) # directly test the app, not the proxy
+ conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_port ) # directly test the app, not the proxy
conn.request( "GET", "/" )
if conn.getresponse().status == 200:
break
@@ -347,7 +363,7 @@
raise Exception( "Test HTTP server did not return '200 OK' after 10 tries" )
# Test if the proxy server is up
if psu_production:
- conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_proxy_port ) # directly test the app, not the proxy
+ conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_proxy_port ) # directly test the app, not the proxy
conn.request( "GET", "/" )
if not conn.getresponse().status == 200:
raise Exception( "Test HTTP proxy server did not return '200 OK'" )
@@ -366,10 +382,10 @@
success = False
try:
tool_configs = app.config.tool_configs
- # What requires these? Handy for (eg) functional tests to save outputs?
+ # What requires these? Handy for (eg) functional tests to save outputs?
if galaxy_test_save:
os.environ[ 'GALAXY_TEST_SAVE' ] = galaxy_test_save
- # Pass in through script setenv, will leave a copy of ALL test validate files
+ # Pass in through script setenv, will leave a copy of ALL test validate files
os.environ[ 'GALAXY_TEST_HOST' ] = galaxy_test_host
if testing_migrated_tools or testing_installed_tools:
shed_tools_dict = {}
@@ -398,7 +414,7 @@
functional.test_toolbox.build_tests( testing_shed_tools=True )
test_config = nose.config.Config( env=os.environ, ignoreFiles=ignore_files, plugins=nose.plugins.manager.DefaultPluginManager() )
test_config.configure( sys.argv )
- result = run_tests( test_config )
+ result = run_tests( test_config )
success = result.wasSuccessful()
try:
os.unlink( tmp_tool_panel_conf )
@@ -415,11 +431,11 @@
os.environ[ 'GALAXY_TEST_FILE_DIR' ] = galaxy_test_file_dir
test_config = nose.config.Config( env=os.environ, ignoreFiles=ignore_files, plugins=nose.plugins.manager.DefaultPluginManager() )
test_config.configure( sys.argv )
- result = run_tests( test_config )
+ result = run_tests( test_config )
success = result.wasSuccessful()
except:
log.exception( "Failure running tests" )
-
+
log.info( "Shutting down" )
# ---- Tear down -----------------------------------------------------------
if server:
https://bitbucket.org/galaxy/galaxy-central/commits/2dfc7b0390d0/
Changeset: 2dfc7b0390d0
User: jmchilton
Date: 2013-10-30 06:01:15
Summary: Refactor test_toolbox.py into smaller methods.
Enhanced readability, hopefully will aide in plugging in an alternative API driven backend.
Affected #: 1 file
diff -r 4ef6fa54e0b98e24979742c775c287cb9fbf3d27 -r 2dfc7b0390d056d30142010f830fe82257f1307c test/functional/test_toolbox.py
--- a/test/functional/test_toolbox.py
+++ b/test/functional/test_toolbox.py
@@ -3,7 +3,7 @@
from galaxy.tools.parameters import grouping
from base.twilltestcase import TwillTestCase
import galaxy.model
-from galaxy.model.orm import *
+from galaxy.model.orm import and_, desc
from galaxy.model.mapping import context as sa_session
toolbox = None
@@ -13,6 +13,23 @@
"""Abstract test case that runs tests based on a `galaxy.tools.test.ToolTest`"""
def do_it( self, testdef, shed_tool_id=None ):
+ """
+ Run through a tool test case.
+ """
+ self.__handle_test_def_errors( testdef )
+
+ latest_history = self.__setup_test_history()
+
+ self.__setup_test_data( testdef, shed_tool_id )
+
+ data_list = self.__run_tool( testdef )
+ self.assertTrue( data_list )
+
+ self.__verify_outputs( testdef, shed_tool_id, data_list )
+
+ self.__delete_history( latest_history )
+
+ def __handle_test_def_errors(self, testdef):
# If the test generation had an error, raise
if testdef.error:
if testdef.exception:
@@ -20,6 +37,7 @@
else:
raise Exception( "Test parse failure" )
+ def __setup_test_history( self ):
# Start with a new history
self.logout()
self.login( email='test(a)bx.psu.edu' )
@@ -33,7 +51,9 @@
assert latest_history is not None, "Problem retrieving latest_history from database"
if len( self.get_history_as_data_list() ) > 0:
raise AssertionError("ToolTestCase.do_it failed")
+ return latest_history
+ def __setup_test_data( self, testdef, shed_tool_id ):
# Upload any needed files
for fname, extra in testdef.required_files:
metadata = extra.get( 'metadata', [] )
@@ -44,9 +64,11 @@
metadata=metadata,
composite_data=composite_data,
shed_tool_id=shed_tool_id )
+
print "Uploaded file: ", fname, ", ftype: ", extra.get( 'ftype', 'auto' ), ", extra: ", extra
#Post upload attribute editing
edit_attributes = extra.get( 'edit_attributes', [] )
+
#currently only renaming is supported
for edit_att in edit_attributes:
if edit_att.get( 'type', None ) == 'name':
@@ -60,6 +82,7 @@
else:
raise Exception( 'edit_attributes type (%s) is unimplemented' % edit_att.get( 'type', None ) )
+ def __run_tool( self, testdef ):
# We need to handle the case where we've uploaded a valid compressed file since the upload
# tool will have uncompressed it on the fly.
all_inputs = {}
@@ -98,21 +121,30 @@
data_list = self.get_history_as_data_list()
if job_finish_by_output_count and len( testdef.outputs ) > ( len( data_list ) - job_finish_by_output_count ):
data_list = None
- self.assertTrue( data_list )
+ return data_list
+
+ def __verify_outputs( self, testdef, shed_tool_id, data_list ):
+ maxseconds = testdef.maxseconds
+
elem_index = 0 - len( testdef.outputs )
for output_tuple in testdef.outputs:
- name, outfile, attributes = output_tuple
# Get the correct hid
elem = data_list[ elem_index ]
self.assertTrue( elem is not None )
+ self.__verify_output( output_tuple, shed_tool_id, elem, maxseconds=maxseconds )
+ elem_index += 1
+
+ def __verify_output( self, output_tuple, shed_tool_id, elem, maxseconds ):
+ name, outfile, attributes = output_tuple
elem_hid = elem.get( 'hid' )
- elem_index += 1
try:
- self.verify_dataset_correctness( outfile, hid=elem_hid, maxseconds=testdef.maxseconds, attributes=attributes, shed_tool_id=shed_tool_id )
+ self.verify_dataset_correctness( outfile, hid=elem_hid, attributes=attributes, shed_tool_id=shed_tool_id )
except Exception:
print >>sys.stderr, self.get_job_stdout( elem.get( 'id' ), format=True )
print >>sys.stderr, self.get_job_stderr( elem.get( 'id' ), format=True )
raise
+
+ def __delete_history( self, latest_history ):
self.delete_history( id=self.security.encode_id( latest_history.id ) )
def __expand_grouping( self, tool_inputs, declared_inputs, prefix='' ):
https://bitbucket.org/galaxy/galaxy-central/commits/83a95ea606d9/
Changeset: 83a95ea606d9
User: jmchilton
Date: 2013-10-30 06:01:15
Summary: Implement functional test env. variable GALAXY_TEST_DB_TEMPLATE.
Middle ground between recreating a completely new database and pointing at existing database with GALAXY_TEST_DBURI. The former requires a lot of setup time, the latter results in test failures in certain cases (namely tool shed tests expecting clean database).
GALAXY_TEST_DB_TEMPLATE can be either a file path (absolute) or URL.
In order to facilitate this, a new Galaxy config option (database_auto_migrate) has been added. If this option is enabled, when Galaxy starts up and points at an existing database, if that database is not at the newest version it will be automatically migrated. This option defaults to False, but is enabled in testing if GALAXY_TEST_DB_TEMPLATE is set.
I think we should go a step further and make this (database_auto_migrate) default to True if database_connection references an sqlite database - unless we believe there are Galaxy instances out there based on sqlite that are REALLY old or that are production enough to warrent requiring admins to do that database migration in a separate step (presumably encouraging them to make a backup pre-migration). Thoughts?
Affected #: 3 files
diff -r 2dfc7b0390d056d30142010f830fe82257f1307c -r 83a95ea606d9fcd15f7554b71804625e203eb1ed lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -35,10 +35,13 @@
self.gid = os.getgid() # if running under newgrp(1) we'll need to fix the group of data created on the cluster
# Database related configuration
self.database = resolve_path( kwargs.get( "database_file", "database/universe.sqlite" ), self.root )
- self.database_connection = kwargs.get( "database_connection", False )
+ self.database_connection = kwargs.get( "database_connection", False )
self.database_engine_options = get_database_engine_options( kwargs )
self.database_create_tables = string_as_bool( kwargs.get( "database_create_tables", "True" ) )
self.database_query_profiling_proxy = string_as_bool( kwargs.get( "database_query_profiling_proxy", "False" ) )
+ # Don't set this to true for production databases, but probably should
+ # default to True for sqlite databases.
+ self.database_auto_migrate = string_as_bool( kwargs.get( "database_auto_migrate", "False" ) )
# Where dataset files are stored
self.file_path = resolve_path( kwargs.get( "file_path", "database/files" ), self.root )
self.new_file_path = resolve_path( kwargs.get( "new_file_path", "database/tmp" ), self.root )
diff -r 2dfc7b0390d056d30142010f830fe82257f1307c -r 83a95ea606d9fcd15f7554b71804625e203eb1ed lib/galaxy/model/migrate/check.py
--- a/lib/galaxy/model/migrate/check.py
+++ b/lib/galaxy/model/migrate/check.py
@@ -45,7 +45,22 @@
log.error( "database_connection contains an unknown SQLAlchemy database dialect: %s" % dialect )
# Create engine and metadata
engine = create_engine( url, **engine_options )
+
+ def migrate():
+ try:
+ # Declare the database to be under a repository's version control
+ db_schema = schema.ControlledSchema.create( engine, migrate_repository )
+ except:
+ # The database is already under version control
+ db_schema = schema.ControlledSchema( engine, migrate_repository )
+ # Apply all scripts to get to current version
+ migrate_to_current_version( engine, db_schema )
+
meta = MetaData( bind=engine )
+ if getattr( app.config, 'database_auto_migrate', False ):
+ migrate()
+ return
+
# Try to load dataset table
try:
dataset_table = Table( "dataset", meta, autoload=True )
@@ -55,15 +70,7 @@
if app:
app.new_installation = True
log.info( "No database, initializing" )
- # Database might or might not be versioned
- try:
- # Declare the database to be under a repository's version control
- db_schema = schema.ControlledSchema.create( engine, migrate_repository )
- except:
- # The database is already under version control
- db_schema = schema.ControlledSchema( engine, migrate_repository )
- # Apply all scripts to get to current version
- migrate_to_current_version( engine, db_schema )
+ migrate()
return
try:
hda_table = Table( "history_dataset_association", meta, autoload=True )
diff -r 2dfc7b0390d056d30142010f830fe82257f1307c -r 83a95ea606d9fcd15f7554b71804625e203eb1ed scripts/functional_tests.py
--- a/scripts/functional_tests.py
+++ b/scripts/functional_tests.py
@@ -40,6 +40,7 @@
import random
import httplib
import socket
+import urllib
from paste import httpserver
import galaxy.app
from galaxy.app import UniverseApplication
@@ -150,6 +151,21 @@
return test_runner.run( tests )
+def __copy_database_template( source, db_path ):
+ """
+ Copy a 'clean' sqlite template database (from file or URL) to specified
+ database path.
+ """
+ os.makedirs( os.path.dirname( db_path ) )
+ if os.path.exists( source ):
+ shutil.copy( source, db_path )
+ assert os.path.exists( db_path )
+ elif source.startswith("http"):
+ urllib.urlretrieve( source, db_path )
+ else:
+ raise Exception( "Failed to copy database template from source %s" % source )
+
+
def main():
# ---- Configuration ------------------------------------------------------
galaxy_test_host = os.environ.get( 'GALAXY_TEST_HOST', default_galaxy_test_host )
@@ -199,6 +215,8 @@
if galaxy_test_tmp_dir is None:
galaxy_test_tmp_dir = tempfile.mkdtemp()
+ database_auto_migrate = False
+
if start_server:
psu_production = False
galaxy_test_proxy_port = None
@@ -259,7 +277,16 @@
if 'GALAXY_TEST_DBURI' in os.environ:
database_connection = os.environ['GALAXY_TEST_DBURI']
else:
- database_connection = 'sqlite:///' + os.path.join( galaxy_db_path, 'universe.sqlite' )
+ db_path = os.path.join( galaxy_db_path, 'universe.sqlite' )
+ if 'GALAXY_TEST_DB_TEMPLATE' in os.environ:
+ # Middle ground between recreating a completely new
+ # database and pointing at existing database with
+ # GALAXY_TEST_DBURI. The former requires a lot of setup
+ # time, the latter results in test failures in certain
+ # cases (namely tool shed tests expecting clean database).
+ __copy_database_template(os.environ['GALAXY_TEST_DB_TEMPLATE'], db_path)
+ database_auto_migrate = True
+ database_connection = 'sqlite:///%s' % db_path
kwargs = {}
for dir in file_path, new_file_path:
try:
@@ -276,6 +303,7 @@
allow_user_creation=True,
allow_user_deletion=True,
database_connection=database_connection,
+ database_auto_migrate=database_auto_migrate,
datatype_converters_config_file="datatype_converters_conf.xml.sample",
file_path=file_path,
id_secret='changethisinproductiontoo',
https://bitbucket.org/galaxy/galaxy-central/commits/4beddecc0ebc/
Changeset: 4beddecc0ebc
User: jmchilton
Date: 2013-11-01 16:14:24
Summary: Merged in jmchilton/galaxy-central-fork-1 (pull request #246)
Small functional test framework enhancements
Affected #: 4 files
diff -r 0dccf0970fefa7a54b036d20a7d512f3d27a54de -r 4beddecc0ebc6bf13b5d00dc48cbabe65c401e10 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -35,10 +35,13 @@
self.gid = os.getgid() # if running under newgrp(1) we'll need to fix the group of data created on the cluster
# Database related configuration
self.database = resolve_path( kwargs.get( "database_file", "database/universe.sqlite" ), self.root )
- self.database_connection = kwargs.get( "database_connection", False )
+ self.database_connection = kwargs.get( "database_connection", False )
self.database_engine_options = get_database_engine_options( kwargs )
self.database_create_tables = string_as_bool( kwargs.get( "database_create_tables", "True" ) )
self.database_query_profiling_proxy = string_as_bool( kwargs.get( "database_query_profiling_proxy", "False" ) )
+ # Don't set this to true for production databases, but probably should
+ # default to True for sqlite databases.
+ self.database_auto_migrate = string_as_bool( kwargs.get( "database_auto_migrate", "False" ) )
# Where dataset files are stored
self.file_path = resolve_path( kwargs.get( "file_path", "database/files" ), self.root )
self.new_file_path = resolve_path( kwargs.get( "new_file_path", "database/tmp" ), self.root )
diff -r 0dccf0970fefa7a54b036d20a7d512f3d27a54de -r 4beddecc0ebc6bf13b5d00dc48cbabe65c401e10 lib/galaxy/model/migrate/check.py
--- a/lib/galaxy/model/migrate/check.py
+++ b/lib/galaxy/model/migrate/check.py
@@ -45,7 +45,22 @@
log.error( "database_connection contains an unknown SQLAlchemy database dialect: %s" % dialect )
# Create engine and metadata
engine = create_engine( url, **engine_options )
+
+ def migrate():
+ try:
+ # Declare the database to be under a repository's version control
+ db_schema = schema.ControlledSchema.create( engine, migrate_repository )
+ except:
+ # The database is already under version control
+ db_schema = schema.ControlledSchema( engine, migrate_repository )
+ # Apply all scripts to get to current version
+ migrate_to_current_version( engine, db_schema )
+
meta = MetaData( bind=engine )
+ if getattr( app.config, 'database_auto_migrate', False ):
+ migrate()
+ return
+
# Try to load dataset table
try:
dataset_table = Table( "dataset", meta, autoload=True )
@@ -55,15 +70,7 @@
if app:
app.new_installation = True
log.info( "No database, initializing" )
- # Database might or might not be versioned
- try:
- # Declare the database to be under a repository's version control
- db_schema = schema.ControlledSchema.create( engine, migrate_repository )
- except:
- # The database is already under version control
- db_schema = schema.ControlledSchema( engine, migrate_repository )
- # Apply all scripts to get to current version
- migrate_to_current_version( engine, db_schema )
+ migrate()
return
try:
hda_table = Table( "history_dataset_association", meta, autoload=True )
diff -r 0dccf0970fefa7a54b036d20a7d512f3d27a54de -r 4beddecc0ebc6bf13b5d00dc48cbabe65c401e10 scripts/functional_tests.py
--- a/scripts/functional_tests.py
+++ b/scripts/functional_tests.py
@@ -1,6 +1,10 @@
#!/usr/bin/env python
-import os, sys, shutil, tempfile, re
+import os
+import sys
+import shutil
+import tempfile
+import re
from ConfigParser import SafeConfigParser
# Assume we are run from the galaxy root directory, add lib to the python path
@@ -25,10 +29,18 @@
# http://code.google.com/p/python-nose/issues/detail?id=284
eggs.require( "pysqlite" )
-import atexit, logging, os, os.path, sys, tempfile
-import twill, unittest, time
-import subprocess, sys, threading, random
-import httplib, socket
+import atexit
+import logging
+import os.path
+import twill
+import unittest
+import time
+import subprocess
+import threading
+import random
+import httplib
+import socket
+import urllib
from paste import httpserver
import galaxy.app
from galaxy.app import UniverseApplication
@@ -56,6 +68,7 @@
# should this serve static resources (scripts, images, styles, etc.)
STATIC_ENABLED = True
+
def get_static_settings():
"""Returns dictionary of the settings necessary for a galaxy App
to be wrapped in the static middleware.
@@ -68,16 +81,17 @@
#TODO: these should be copied from universe_wsgi.ini
return dict(
#TODO: static_enabled needed here?
- static_enabled = True,
- static_cache_time = 360,
- static_dir = static_dir,
- static_images_dir = os.path.join( static_dir, 'images', '' ),
- static_favicon_dir = os.path.join( static_dir, 'favicon.ico' ),
- static_scripts_dir = os.path.join( static_dir, 'scripts', '' ),
- static_style_dir = os.path.join( static_dir, 'june_2007_style', 'blue' ),
- static_robots_txt = os.path.join( static_dir, 'robots.txt' ),
+ static_enabled=True,
+ static_cache_time=360,
+ static_dir=static_dir,
+ static_images_dir=os.path.join( static_dir, 'images', '' ),
+ static_favicon_dir=os.path.join( static_dir, 'favicon.ico' ),
+ static_scripts_dir=os.path.join( static_dir, 'scripts', '' ),
+ static_style_dir=os.path.join( static_dir, 'june_2007_style', 'blue' ),
+ static_robots_txt=os.path.join( static_dir, 'robots.txt' ),
)
+
def get_webapp_global_conf():
"""Get the global_conf dictionary sent as the first argument to app_factory.
"""
@@ -87,12 +101,13 @@
global_conf.update( get_static_settings() )
return global_conf
+
def generate_config_file( input_filename, output_filename, config_items ):
'''
Generate a config file with the configuration that has been defined for the embedded web application.
This is mostly relevant when setting metadata externally, since the script for doing that does not
have access to app.config.
- '''
+ '''
cp = SafeConfigParser()
cp.read( input_filename )
config_items_by_section = []
@@ -110,9 +125,9 @@
config_tuple = 'app:main', label, value
config_items_by_section.append( config_tuple )
print( config_items_by_section )
+
# Replace the default values with the provided configuration.
for section, label, value in config_items_by_section:
-
if cp.has_option( section, label ):
cp.remove_option( section, label )
cp.set( section, label, str( value ) )
@@ -120,6 +135,7 @@
cp.write( fh )
fh.close()
+
def run_tests( test_config ):
loader = nose.loader.TestLoader( config=test_config )
plug_loader = test_config.plugins.prepareTestLoader( loader )
@@ -134,7 +150,23 @@
test_runner = plug_runner
return test_runner.run( tests )
-def main():
+
+def __copy_database_template( source, db_path ):
+ """
+ Copy a 'clean' sqlite template database (from file or URL) to specified
+ database path.
+ """
+ os.makedirs( os.path.dirname( db_path ) )
+ if os.path.exists( source ):
+ shutil.copy( source, db_path )
+ assert os.path.exists( db_path )
+ elif source.startswith("http"):
+ urllib.urlretrieve( source, db_path )
+ else:
+ raise Exception( "Failed to copy database template from source %s" % source )
+
+
+def main():
# ---- Configuration ------------------------------------------------------
galaxy_test_host = os.environ.get( 'GALAXY_TEST_HOST', default_galaxy_test_host )
galaxy_test_port = os.environ.get( 'GALAXY_TEST_PORT', None )
@@ -174,7 +206,7 @@
start_server = 'GALAXY_TEST_EXTERNAL' not in os.environ
if os.path.exists( 'tool_data_table_conf.test.xml' ):
tool_data_table_config_path = 'tool_data_table_conf.test.xml'
- else:
+ else:
tool_data_table_config_path = 'tool_data_table_conf.xml'
shed_tool_data_table_config = 'shed_tool_data_table_conf.xml'
tool_dependency_dir = os.environ.get( 'GALAXY_TOOL_DEPENDENCY_DIR', None )
@@ -182,7 +214,9 @@
galaxy_test_tmp_dir = os.environ.get( 'GALAXY_TEST_TMP_DIR', None )
if galaxy_test_tmp_dir is None:
galaxy_test_tmp_dir = tempfile.mkdtemp()
-
+
+ database_auto_migrate = False
+
if start_server:
psu_production = False
galaxy_test_proxy_port = None
@@ -212,29 +246,29 @@
job_working_directory = os.path.join( new_file_path, 'job_working_directory' )
os.mkdir( cluster_files_directory )
os.mkdir( job_working_directory )
- kwargs = dict( database_engine_option_pool_size = '10',
- database_engine_option_max_overflow = '20',
- database_engine_option_strategy = 'threadlocal',
- nginx_x_accel_redirect_base = '/_x_accel_redirect',
- nginx_upload_store = nginx_upload_store,
- nginx_upload_path = '/_upload',
- allow_library_path_paste = 'True',
- cluster_files_directory = cluster_files_directory,
- job_working_directory = job_working_directory,
- outputs_to_working_directory = 'True',
- static_enabled = 'False',
- debug = 'False',
- track_jobs_in_database = 'True',
- job_scheduler_policy = 'FIFO',
- start_job_runners = 'pbs',
- default_cluster_job_runner = default_cluster_job_runner )
+ kwargs = dict( database_engine_option_pool_size='10',
+ database_engine_option_max_overflow='20',
+ database_engine_option_strategy='threadlocal',
+ nginx_x_accel_redirect_base='/_x_accel_redirect',
+ nginx_upload_store=nginx_upload_store,
+ nginx_upload_path='/_upload',
+ allow_library_path_paste='True',
+ cluster_files_directory=cluster_files_directory,
+ job_working_directory=job_working_directory,
+ outputs_to_working_directory='True',
+ static_enabled='False',
+ debug='False',
+ track_jobs_in_database='True',
+ job_scheduler_policy='FIFO',
+ start_job_runners='pbs',
+ default_cluster_job_runner=default_cluster_job_runner )
psu_production = True
else:
tempdir = tempfile.mkdtemp( dir=galaxy_test_tmp_dir )
# Configure the database path.
if 'GALAXY_TEST_DBPATH' in os.environ:
galaxy_db_path = os.environ[ 'GALAXY_TEST_DBPATH' ]
- else:
+ else:
galaxy_db_path = os.path.join( tempdir, 'database' )
# Configure the paths Galaxy needs to test tools.
file_path = os.path.join( galaxy_db_path, 'files' )
@@ -243,7 +277,16 @@
if 'GALAXY_TEST_DBURI' in os.environ:
database_connection = os.environ['GALAXY_TEST_DBURI']
else:
- database_connection = 'sqlite:///' + os.path.join( galaxy_db_path, 'universe.sqlite' )
+ db_path = os.path.join( galaxy_db_path, 'universe.sqlite' )
+ if 'GALAXY_TEST_DB_TEMPLATE' in os.environ:
+ # Middle ground between recreating a completely new
+ # database and pointing at existing database with
+ # GALAXY_TEST_DBURI. The former requires a lot of setup
+ # time, the latter results in test failures in certain
+ # cases (namely tool shed tests expecting clean database).
+ __copy_database_template(os.environ['GALAXY_TEST_DB_TEMPLATE'], db_path)
+ database_auto_migrate = True
+ database_connection = 'sqlite:///%s' % db_path
kwargs = {}
for dir in file_path, new_file_path:
try:
@@ -252,33 +295,34 @@
except OSError:
pass
- # ---- Build Application --------------------------------------------------
- app = None
+ # ---- Build Application --------------------------------------------------
+ app = None
if start_server:
- kwargs = dict( admin_users = 'test(a)bx.psu.edu',
- allow_library_path_paste = True,
- allow_user_creation = True,
- allow_user_deletion = True,
- database_connection = database_connection,
- datatype_converters_config_file = "datatype_converters_conf.xml.sample",
- file_path = file_path,
- id_secret = 'changethisinproductiontoo',
- job_queue_workers = 5,
- job_working_directory = job_working_directory,
- library_import_dir = library_import_dir,
- log_destination = "stdout",
- new_file_path = new_file_path,
- running_functional_tests = True,
- shed_tool_data_table_config = shed_tool_data_table_config,
- template_path = "templates",
- test_conf = "test.conf",
- tool_config_file = tool_config_file,
- tool_data_table_config_path = tool_data_table_config_path,
- tool_path = tool_path,
- tool_parse_help = False,
- update_integrated_tool_panel = False,
- use_heartbeat = False,
- user_library_import_dir = user_library_import_dir )
+ kwargs = dict( admin_users='test(a)bx.psu.edu',
+ allow_library_path_paste=True,
+ allow_user_creation=True,
+ allow_user_deletion=True,
+ database_connection=database_connection,
+ database_auto_migrate=database_auto_migrate,
+ datatype_converters_config_file="datatype_converters_conf.xml.sample",
+ file_path=file_path,
+ id_secret='changethisinproductiontoo',
+ job_queue_workers=5,
+ job_working_directory=job_working_directory,
+ library_import_dir=library_import_dir,
+ log_destination="stdout",
+ new_file_path=new_file_path,
+ running_functional_tests=True,
+ shed_tool_data_table_config=shed_tool_data_table_config,
+ template_path="templates",
+ test_conf="test.conf",
+ tool_config_file=tool_config_file,
+ tool_data_table_config_path=tool_data_table_config_path,
+ tool_path=tool_path,
+ tool_parse_help=False,
+ update_integrated_tool_panel=False,
+ use_heartbeat=False,
+ user_library_import_dir=user_library_import_dir )
if psu_production:
kwargs[ 'global_conf' ] = None
if not database_connection.startswith( 'sqlite://' ):
@@ -309,7 +353,7 @@
# ---- Run webserver ------------------------------------------------------
server = None
-
+
if start_server:
webapp = buildapp.app_factory( kwargs[ 'global_conf' ], app=app,
use_translogger=False, static_enabled=STATIC_ENABLED )
@@ -338,7 +382,7 @@
t.start()
# Test if the server is up
for i in range( 10 ):
- conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_port ) # directly test the app, not the proxy
+ conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_port ) # directly test the app, not the proxy
conn.request( "GET", "/" )
if conn.getresponse().status == 200:
break
@@ -347,7 +391,7 @@
raise Exception( "Test HTTP server did not return '200 OK' after 10 tries" )
# Test if the proxy server is up
if psu_production:
- conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_proxy_port ) # directly test the app, not the proxy
+ conn = httplib.HTTPConnection( galaxy_test_host, galaxy_test_proxy_port ) # directly test the app, not the proxy
conn.request( "GET", "/" )
if not conn.getresponse().status == 200:
raise Exception( "Test HTTP proxy server did not return '200 OK'" )
@@ -366,10 +410,10 @@
success = False
try:
tool_configs = app.config.tool_configs
- # What requires these? Handy for (eg) functional tests to save outputs?
+ # What requires these? Handy for (eg) functional tests to save outputs?
if galaxy_test_save:
os.environ[ 'GALAXY_TEST_SAVE' ] = galaxy_test_save
- # Pass in through script setenv, will leave a copy of ALL test validate files
+ # Pass in through script setenv, will leave a copy of ALL test validate files
os.environ[ 'GALAXY_TEST_HOST' ] = galaxy_test_host
if testing_migrated_tools or testing_installed_tools:
shed_tools_dict = {}
@@ -398,7 +442,7 @@
functional.test_toolbox.build_tests( testing_shed_tools=True )
test_config = nose.config.Config( env=os.environ, ignoreFiles=ignore_files, plugins=nose.plugins.manager.DefaultPluginManager() )
test_config.configure( sys.argv )
- result = run_tests( test_config )
+ result = run_tests( test_config )
success = result.wasSuccessful()
try:
os.unlink( tmp_tool_panel_conf )
@@ -415,11 +459,11 @@
os.environ[ 'GALAXY_TEST_FILE_DIR' ] = galaxy_test_file_dir
test_config = nose.config.Config( env=os.environ, ignoreFiles=ignore_files, plugins=nose.plugins.manager.DefaultPluginManager() )
test_config.configure( sys.argv )
- result = run_tests( test_config )
+ result = run_tests( test_config )
success = result.wasSuccessful()
except:
log.exception( "Failure running tests" )
-
+
log.info( "Shutting down" )
# ---- Tear down -----------------------------------------------------------
if server:
diff -r 0dccf0970fefa7a54b036d20a7d512f3d27a54de -r 4beddecc0ebc6bf13b5d00dc48cbabe65c401e10 test/functional/test_toolbox.py
--- a/test/functional/test_toolbox.py
+++ b/test/functional/test_toolbox.py
@@ -1,38 +1,61 @@
-import sys, new
+import sys
+import new
from galaxy.tools.parameters import grouping
-from galaxy.tools.parameters import basic
from base.twilltestcase import TwillTestCase
import galaxy.model
-from galaxy.model.orm import *
+from galaxy.model.orm import and_, desc
from galaxy.model.mapping import context as sa_session
toolbox = None
+
class ToolTestCase( TwillTestCase ):
"""Abstract test case that runs tests based on a `galaxy.tools.test.ToolTest`"""
+
def do_it( self, testdef, shed_tool_id=None ):
+ """
+ Run through a tool test case.
+ """
+ self.__handle_test_def_errors( testdef )
+
+ latest_history = self.__setup_test_history()
+
+ self.__setup_test_data( testdef, shed_tool_id )
+
+ data_list = self.__run_tool( testdef )
+ self.assertTrue( data_list )
+
+ self.__verify_outputs( testdef, shed_tool_id, data_list )
+
+ self.__delete_history( latest_history )
+
+ def __handle_test_def_errors(self, testdef):
# If the test generation had an error, raise
if testdef.error:
if testdef.exception:
raise testdef.exception
else:
raise Exception( "Test parse failure" )
+
+ def __setup_test_history( self ):
# Start with a new history
self.logout()
self.login( email='test(a)bx.psu.edu' )
- admin_user = sa_session.query( galaxy.model.User ).filter( galaxy.model.User.table.c.email=='test(a)bx.psu.edu' ).one()
+ admin_user = sa_session.query( galaxy.model.User ).filter( galaxy.model.User.table.c.email == 'test(a)bx.psu.edu' ).one()
self.new_history()
latest_history = sa_session.query( galaxy.model.History ) \
- .filter( and_( galaxy.model.History.table.c.deleted==False,
- galaxy.model.History.table.c.user_id==admin_user.id ) ) \
+ .filter( and_( galaxy.model.History.table.c.deleted == False,
+ galaxy.model.History.table.c.user_id == admin_user.id ) ) \
.order_by( desc( galaxy.model.History.table.c.create_time ) ) \
.first()
assert latest_history is not None, "Problem retrieving latest_history from database"
if len( self.get_history_as_data_list() ) > 0:
raise AssertionError("ToolTestCase.do_it failed")
+ return latest_history
+
+ def __setup_test_data( self, testdef, shed_tool_id ):
# Upload any needed files
for fname, extra in testdef.required_files:
- children = extra.get( 'children', [] )
metadata = extra.get( 'metadata', [] )
composite_data = extra.get( 'composite_data', [] )
self.upload_file( fname,
@@ -41,9 +64,11 @@
metadata=metadata,
composite_data=composite_data,
shed_tool_id=shed_tool_id )
+
print "Uploaded file: ", fname, ", ftype: ", extra.get( 'ftype', 'auto' ), ", extra: ", extra
#Post upload attribute editing
edit_attributes = extra.get( 'edit_attributes', [] )
+
#currently only renaming is supported
for edit_att in edit_attributes:
if edit_att.get( 'type', None ) == 'name':
@@ -51,29 +76,35 @@
assert new_name, 'You must supply the new dataset name as the value tag of the edit_attributes tag'
hda_id = self.get_history_as_data_list()[-1].get( 'id' )
try:
- self.edit_hda_attribute_info( hda_id = str(hda_id), new_name = new_name )
+ self.edit_hda_attribute_info( hda_id=str(hda_id), new_name=new_name )
except:
- print "### call to edit_hda failed for hda_id %s, new_name=%s" % (hda_id,new_name)
+ print "### call to edit_hda failed for hda_id %s, new_name=%s" % (hda_id, new_name)
else:
raise Exception( 'edit_attributes type (%s) is unimplemented' % edit_att.get( 'type', None ) )
+
+ def __run_tool( self, testdef ):
# We need to handle the case where we've uploaded a valid compressed file since the upload
# tool will have uncompressed it on the fly.
all_inputs = {}
for name, value, _ in testdef.inputs:
all_inputs[ name ] = value
+
# See if we have a grouping.Repeat element
repeat_name = None
for input_name, input_value in testdef.tool.inputs_by_page[0].items():
- if isinstance( input_value, grouping.Repeat ) and all_inputs.get( input_name, 1 ) not in [ 0, "0" ]: #default behavior is to test 1 repeat, for backwards compatibility
+ if isinstance( input_value, grouping.Repeat ) and all_inputs.get( input_name, 1 ) not in [ 0, "0" ]: # default behavior is to test 1 repeat, for backwards compatibility
repeat_name = input_name
break
+
#check if we need to verify number of outputs created dynamically by tool
if testdef.tool.force_history_refresh:
job_finish_by_output_count = len( self.get_history_as_data_list() )
else:
job_finish_by_output_count = False
+
# Do the first page
- page_inputs = self.__expand_grouping(testdef.tool.inputs_by_page[0], all_inputs)
+ page_inputs = self.__expand_grouping(testdef.tool.inputs_by_page[0], all_inputs)
+
# Run the tool
self.run_tool( testdef.tool.id, repeat_name=repeat_name, **page_inputs )
print "page_inputs (0)", page_inputs
@@ -82,6 +113,7 @@
page_inputs = self.__expand_grouping(testdef.tool.inputs_by_page[i], all_inputs)
self.submit_form( **page_inputs )
print "page_inputs (%i)" % i, page_inputs
+
# Check the results ( handles single or multiple tool outputs ). Make sure to pass the correct hid.
# The output datasets from the tool should be in the same order as the testdef.outputs.
data_list = None
@@ -89,21 +121,30 @@
data_list = self.get_history_as_data_list()
if job_finish_by_output_count and len( testdef.outputs ) > ( len( data_list ) - job_finish_by_output_count ):
data_list = None
- self.assertTrue( data_list )
+ return data_list
+
+ def __verify_outputs( self, testdef, shed_tool_id, data_list ):
+ maxseconds = testdef.maxseconds
+
elem_index = 0 - len( testdef.outputs )
for output_tuple in testdef.outputs:
- name, outfile, attributes = output_tuple
# Get the correct hid
elem = data_list[ elem_index ]
self.assertTrue( elem is not None )
+ self.__verify_output( output_tuple, shed_tool_id, elem, maxseconds=maxseconds )
+ elem_index += 1
+
+ def __verify_output( self, output_tuple, shed_tool_id, elem, maxseconds ):
+ name, outfile, attributes = output_tuple
elem_hid = elem.get( 'hid' )
- elem_index += 1
try:
- self.verify_dataset_correctness( outfile, hid=elem_hid, maxseconds=testdef.maxseconds, attributes=attributes, shed_tool_id=shed_tool_id )
- except Exception, e:
+ self.verify_dataset_correctness( outfile, hid=elem_hid, attributes=attributes, shed_tool_id=shed_tool_id )
+ except Exception:
print >>sys.stderr, self.get_job_stdout( elem.get( 'id' ), format=True )
print >>sys.stderr, self.get_job_stderr( elem.get( 'id' ), format=True )
raise
+
+ def __delete_history( self, latest_history ):
self.delete_history( id=self.security.encode_id( latest_history.id ) )
def __expand_grouping( self, tool_inputs, declared_inputs, prefix='' ):
@@ -121,14 +162,14 @@
else:
expanded_inputs[ "%s|%s" % ( new_prefix, value.test_param.name ) ] = case.value
for input_name, input_value in case.inputs.items():
- expanded_inputs.update( self.__expand_grouping( { input_name:input_value }, declared_inputs, prefix = new_prefix ) )
+ expanded_inputs.update( self.__expand_grouping( { input_name: input_value }, declared_inputs, prefix=new_prefix ) )
elif isinstance( value, grouping.Repeat ):
- for repeat_index in xrange( 0, 1 ): #need to allow for and figure out how many repeats we have
+ for repeat_index in xrange( 0, 1 ): # need to allow for and figure out how many repeats we have
for r_name, r_value in value.inputs.iteritems():
- new_prefix = "%s_%d" % ( value.name, repeat_index )
- if prefix:
- new_prefix = "%s|%s" % ( prefix, new_prefix )
- expanded_inputs.update( self.__expand_grouping( { new_prefix : r_value }, declared_inputs, prefix = new_prefix ) )
+ new_prefix = "%s_%d" % ( value.name, repeat_index )
+ if prefix:
+ new_prefix = "%s|%s" % ( prefix, new_prefix )
+ expanded_inputs.update( self.__expand_grouping( { new_prefix : r_value }, declared_inputs, prefix=new_prefix ) )
elif value.name not in declared_inputs:
print "%s not declared in tool test, will not change default value." % value.name
elif isinstance(declared_inputs[value.name], str):
@@ -143,6 +184,7 @@
expanded_inputs[value.name] = declared_inputs[value.name]
return expanded_inputs
+
def build_tests( testing_shed_tools=False ):
"""
If the module level variable `toolbox` is set, generate `ToolTestCase`
@@ -151,12 +193,15 @@
"""
if toolbox is None:
return
+
# Push all the toolbox tests to module level
G = globals()
+
# Eliminate all previous tests from G.
for key, val in G.items():
if key.startswith( 'TestForTool_' ):
del G[ key ]
+
for i, tool_id in enumerate( toolbox.tools_by_id ):
tool = toolbox.get_tool( tool_id )
if tool.tests:
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