history panel: handle errors better on the client side, remove alerts, continue update attempts if error is 'Bad Gateway'

This commit is contained in:
Carl Eberhard
2013-04-09 19:17:51 -04:00
parent e2e4448c3c
commit f799e0fded
15 changed files with 168 additions and 166 deletions
@@ -59,6 +59,7 @@ class HistoriesController( BaseAPIController, UsesHistoryMixin ):
if trans.user and len( trans.user.galaxy_sessions ) > 0:
# Most recent active history for user sessions, not deleted
history = trans.user.galaxy_sessions[0].histories[-1].history
history_id = trans.security.encode_id( history.id )
else:
return None
else:
@@ -43,14 +43,10 @@ class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociatio
else:
history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True )
# build the return hda data list
# if ids, return _FULL_ data (as show) for each id passed
if ids:
# if ids, return _FULL_ data (as show) for each id passed
#NOTE: this might not be the best form (passing all info),
# but we(I?) need an hda collection with full data somewhere
ids = ids.split( ',' )
for hda in history.datasets:
#TODO: curr. ordered by history, change to order from ids list
for index, hda in enumerate( history.datasets ):
encoded_hda_id = trans.security.encode_id( hda.id )
if encoded_hda_id in ids:
#TODO: share code with show
@@ -63,21 +59,19 @@ class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociatio
except Exception, exc:
# don't fail entire list if hda err's, record and move on
# (making sure http recvr knows it's err'd)
trans.response.status = 500
log.error( "Error in history API at listing contents with history %s, hda %s: (%s) %s",
history_id, encoded_hda_id, type( exc ), str( exc ) )
rval.append( self._exception_as_hda_dict( trans, encoded_hda_id, exc ) )
history_id, encoded_hda_id, type( exc ), str( exc ), exc_info=True )
rval.append( self.get_hda_dict_with_error( trans, hda, str( exc ) ) )
# if no ids passed, return a _SUMMARY_ of _all_ datasets in the history
else:
# if no ids passed, return a _SUMMARY_ of _all_ datasets in the history
for hda in history.datasets:
rval.append( self._summary_hda_dict( trans, history_id, hda ) )
except Exception, e:
# for errors that are not specific to one hda (history lookup or summary list)
rval = "Error in history API at listing contents: " + str( e )
log.error( rval + ": %s, %s" % ( type( e ), str( e ) ) )
log.error( rval + ": %s, %s" % ( type( e ), str( e ) ), exc_info=True )
trans.response.status = 500
return rval
@@ -102,21 +96,6 @@ class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociatio
'url' : url_for( 'history_content', history_id=history_id, id=encoded_id, ),
}
#TODO: move to model or Mixin
def _exception_as_hda_dict( self, trans, hda_id, exception ):
"""
Returns a dictionary for an HDA that raised an exception when it's
dictionary was being built.
"""
return {
'id' : hda_id,
'state' : trans.app.model.Dataset.states.ERROR,
'visible' : True,
'misc_info' : str( exception ),
'misc_blurb': 'Failed to retrieve dataset information.',
'error' : str( exception )
}
@web.expose_api_anonymous
def show( self, trans, id, history_id, **kwd ):
"""
+34 -10
View File
@@ -1,11 +1,15 @@
"""
Tags Controller: handles tagging/untagging of entities and provides autocomplete support.
Tags Controller: handles tagging/untagging of entities
and provides autocomplete support.
"""
import logging
from galaxy.web.base.controller import *
from sqlalchemy.sql.expression import func, and_
from sqlalchemy.sql import select
from galaxy import web
from galaxy.web.base.controller import BaseUIController, UsesTagsMixin
import logging
log = logging.getLogger( __name__ )
class TagsController ( BaseUIController, UsesTagsMixin ):
@@ -13,7 +17,9 @@ class TagsController ( BaseUIController, UsesTagsMixin ):
@web.expose
@web.require_login( "edit item tags" )
def get_tagging_elt_async( self, trans, item_id, item_class, elt_context="" ):
""" Returns HTML for editing an item's tags. """
"""
Returns HTML for editing an item's tags.
"""
item = self._get_item( trans, item_class, trans.security.decode_id( item_id ) )
if not item:
return trans.show_error_message( "No item of class %s with id %s " % ( item_class, item_id ) )
@@ -26,10 +32,13 @@ class TagsController ( BaseUIController, UsesTagsMixin ):
input_size="22",
tag_click_fn="default_tag_click_fn",
use_toggle_link=False )
@web.expose
@web.require_login( "add tag to an item" )
def add_tag_async( self, trans, item_id=None, item_class=None, new_tag=None, context=None ):
""" Add tag to an item. """
"""
Add tag to an item.
"""
# Apply tag.
item = self._get_item( trans, item_class, trans.security.decode_id( item_id ) )
user = trans.user
@@ -38,10 +47,13 @@ class TagsController ( BaseUIController, UsesTagsMixin ):
# Log.
params = dict( item_id=item.id, item_class=item_class, tag=new_tag )
trans.log_action( user, unicode( "tag" ), context, params )
@web.expose
@web.require_login( "remove tag from an item" )
def remove_tag_async( self, trans, item_id=None, item_class=None, tag_name=None, context=None ):
""" Remove tag from an item. """
"""
Remove tag from an item.
"""
# Remove tag.
item = self._get_item( trans, item_class, trans.security.decode_id( item_id ) )
user = trans.user
@@ -50,21 +62,27 @@ class TagsController ( BaseUIController, UsesTagsMixin ):
# Log.
params = dict( item_id=item.id, item_class=item_class, tag=tag_name )
trans.log_action( user, unicode( "untag" ), context, params )
# Retag an item. All previous tags are deleted and new tags are applied.
#@web.expose
@web.require_login( "Apply a new set of tags to an item; previous tags are deleted." )
def retag_async( self, trans, item_id=None, item_class=None, new_tags=None ):
""" Apply a new set of tags to an item; previous tags are deleted. """
"""
Apply a new set of tags to an item; previous tags are deleted.
"""
# Apply tags.
item = self._get_item( trans, item_class, trans.security.decode_id( item_id ) )
user = trans.user
self.get_tag_handler( trans ).delete_item_tags( trans, item )
self.get_tag_handler( trans ).apply_item_tags( trans, user, item, new_tags.encode( 'utf-8' ) )
trans.sa_session.flush()
trans.sa_session.flush()
@web.expose
@web.require_login( "get autocomplete data for an item's tags" )
def tag_autocomplete_data( self, trans, q=None, limit=None, timestamp=None, item_id=None, item_class=None ):
""" Get autocomplete data for an item's tags. """
"""
Get autocomplete data for an item's tags.
"""
# Get item, do security check, and get autocomplete data.
item = None
if item_id is not None:
@@ -76,6 +94,7 @@ class TagsController ( BaseUIController, UsesTagsMixin ):
return self._get_tag_autocomplete_names( trans, q, limit, timestamp, user, item, item_class )
else:
return self._get_tag_autocomplete_values( trans, q, limit, timestamp, user, item, item_class )
def _get_tag_autocomplete_names( self, trans, q, limit, timestamp, user=None, item=None, item_class=None ):
"""
Returns autocomplete data for tag names ordered from most frequently used to
@@ -115,6 +134,7 @@ class TagsController ( BaseUIController, UsesTagsMixin ):
tag_names = self._get_usernames_for_tag( trans, trans.user, tag, item_class, item_tag_assoc_class )
ac_data += tag_names[0] + "|" + tag_names[0] + "\n"
return ac_data
def _get_tag_autocomplete_values( self, trans, q, limit, timestamp, user=None, item=None, item_class=None ):
"""
Returns autocomplete data for tag values ordered from most frequently used to
@@ -155,6 +175,7 @@ class TagsController ( BaseUIController, UsesTagsMixin ):
for row in result_set:
ac_data += tag_uname + ":" + row[0] + "|" + row[0] + "\n"
return ac_data
def _get_usernames_for_tag( self, trans, user, tag, item_class, item_tag_assoc_class ):
"""
Returns an ordered list of the user names for a tag; list is ordered from
@@ -176,8 +197,11 @@ class TagsController ( BaseUIController, UsesTagsMixin ):
for row in result_set:
user_tag_names.append( row[0] )
return user_tag_names
def _get_item( self, trans, item_class_name, id ):
""" Get an item based on type and id. """
"""
Get an item based on type and id.
"""
item_class = self.get_tag_handler( trans ).item_tag_assoc_info[item_class_name].item_class
item = trans.sa_session.query( item_class ).filter( "id=" + str( id ) )[0]
return item
+6 -2
View File
@@ -456,7 +456,8 @@ var HDAEditView = HDABaseView.extend( LoggableMixin ).extend(
//BUG: broken with latest
//TODO: this is a drop in from history.mako - should use MV as well
this.log( this + '.loadAndDisplayTags', event );
var tagArea = this.$el.find( '.tag-area' ),
var view = this,
tagArea = this.$el.find( '.tag-area' ),
tagElt = tagArea.find( '.tag-elt' );
// Show or hide tag area; if showing tag area and it's empty, fill it.
@@ -466,7 +467,10 @@ var HDAEditView = HDABaseView.extend( LoggableMixin ).extend(
$.ajax({
//TODO: the html from this breaks a couple of times
url: this.urls.tags.get,
error: function() { alert( _l( "Tagging failed" ) ); },
error: function( xhr, status, error ){
view.log( "Tagging failed", xhr, status, error );
view.trigger( 'error', _l( "Tagging failed" ), xhr, status, error );
},
success: function(tag_elt_html) {
tagElt.html(tag_elt_html);
tagElt.find(".tooltip").tooltip();
+1 -2
View File
@@ -49,7 +49,6 @@ var HistoryDatasetAssociation = BaseModel.extend( LoggableMixin ).extend(
deleted : false,
purged : false,
visible : true,
// based on trans.user (is_admin or security_agent.can_access_dataset( <user_roles>, hda.dataset ))
accessible : true
},
@@ -57,7 +56,7 @@ var HistoryDatasetAssociation = BaseModel.extend( LoggableMixin ).extend(
urlRoot: 'api/histories/',
url : function(){
//TODO: get this via url router
return 'api/histories/' + this.get( 'history_id' ) + '/contents/' + this.get( 'id' );
return this.urlRoot + this.get( 'history_id' ) + '/contents/' + this.get( 'id' );
//TODO: this breaks on save()
},
+37 -82
View File
@@ -21,18 +21,12 @@ var History = BaseModel.extend( LoggableMixin ).extend(
// values from api (may need more)
defaults : {
id : '',
name : '',
state : '',
id : null,
name : 'Unnamed History',
state : 'new',
diskSize : 0,
deleted : false,
//tags : [],
annotation : null,
//TODO: message? how to get over the api?
message : null
deleted : false
},
//TODO: hardcoded
@@ -40,7 +34,7 @@ var History = BaseModel.extend( LoggableMixin ).extend(
/** url for fetch */
url : function(){
// api location of history resource
return 'api/histories/' + this.get( 'id' );
return this.urlRoot + this.get( 'id' );
},
/** Set up the hdas collection
@@ -48,30 +42,20 @@ var History = BaseModel.extend( LoggableMixin ).extend(
* @param {Object[]} initialHdas array of model data for this History's HDAs
* @see BaseModel#initialize
*/
initialize : function( initialSettings, initialHdas ){
initialize : function( initialSettings, initialHdas, logger ){
logger = logger || null;
this.log( this + ".initialize:", initialSettings, initialHdas );
/** HDACollection of the HDAs contained in this history. */
this.hdas = new HDACollection();
// if we've got hdas passed in the constructor, load them and set up updates if needed
if( initialHdas ){
if( _.isArray( initialHdas ) ){
this.hdas.reset( initialHdas );
this.checkForUpdates();
//TODO: don't call if force_history_refresh
if( this.hdas.length > 0 ){
this.updateDisplayApplications();
}
// handle errors in initialHdas
//TODO: errors from the api shouldn't be plain strings...
//TODO: remove when mappers and hda_dict are unified (or move to alt history)
} else if( _.isString( initialHdas ) ){
this.log( 'error in initialHdas: ', initialHdas );
Galaxy.show_modal( _l( 'Error loading datasets for history' ), initialHdas,
{ 'Ok': function(){ Galaxy.hide_modal(); } } );
//TODO: retry (via ajax), report
if( initialHdas && _.isArray( initialHdas ) ){
this.hdas.reset( initialHdas );
this.checkForUpdates();
//TODO: don't call if force_history_refresh
if( this.hdas.length > 0 ){
this.updateDisplayApplications();
}
}
@@ -87,52 +71,11 @@ var History = BaseModel.extend( LoggableMixin ).extend(
}
}, this );
// events
//this.on( 'change', function( currModel, changedList ){
// this.log( this + ' has changed:', currModel, changedList );
//});
//this.bind( 'all', function( event ){
// //this.log( this + '', arguments );
//});
},
/** get data via the api (alternative to sending options, hdas to initialize)
* @param {String} historyId encoded id
* @param {Object[]} success
* @see BaseModel#initialize
*/
//TODO: this needs work - move to more straightforward deferred
// events: loaded, loaded:user, loaded:hdas
loadFromApi : function( historyId, success ){
var history = this;
// fetch the history AND the user (mainly to see if they're logged in at this point)
history.attributes.id = historyId;
//TODO:?? really? fetch user here?
jQuery.when(
jQuery.ajax( 'api/users/current' ),
history.fetch()
).then( function( userResponse, historyResponse ){
history.attributes.user = userResponse[0]; //? meh.
history.trigger( 'loaded:user', userResponse[0] );
history.trigger( 'loaded', historyResponse[0] );
}).then( function(){
// ...then the hdas (using contents?ids=...)
jQuery.ajax( history.url() + '/contents?' + jQuery.param({
ids : history.hdaIdsFromStateIds().join( ',' )
// reset the collection to the hdas returned
})).success( function( hdas ){
history.hdas.reset( hdas );
history.checkForUpdates();
history.trigger( 'loaded:hdas', hdas );
if( success ){ callback( history ); }
});
});
if( this.logger ){
this.bind( 'all', function( event ){
this.log( this + '', arguments );
}, this );
}
},
// reduce the state_ids map of hda id lists -> a single list of ids
@@ -146,7 +89,7 @@ var History = BaseModel.extend( LoggableMixin ).extend(
// get the history's state from it's cummulative ds states, delay + update if needed
// events: ready
checkForUpdates : function( datasets ){
checkForUpdates : function(){
// get overall History state from collection, run updater if History has running/queued hdas
// boiling it down on the client to running/not
if( this.hdas.running().length ){
@@ -194,7 +137,6 @@ var History = BaseModel.extend( LoggableMixin ).extend(
}
// set up to keep pulling if this history in run/queue state
//TODO: magic number here
if( ( history.get( 'state' ) === HistoryDatasetAssociation.STATES.RUNNING )
|| ( history.get( 'state' ) === HistoryDatasetAssociation.STATES.QUEUED ) ){
setTimeout( function(){
@@ -207,11 +149,22 @@ var History = BaseModel.extend( LoggableMixin ).extend(
}
}).error( function( xhr, status, error ){
//TODO: use ajax.status handlers here
// keep rolling on a bad gateway - server restart
if( xhr.status === 502 ){
setTimeout( function(){
history.log( 'Bad Gateway error. Retrying...' );
//TODO: someway to throw error on X tries
history.stateUpdater();
}, History.UPDATE_DELAY );
// if not interruption by iframe reload
//TODO: remove when iframes are removed
if( !( ( xhr.readyState === 0 ) && ( xhr.status === 0 ) ) ){
alert( _l( 'Error getting history updates from the server:' ) + '\n' + error );
} else if( !( ( xhr.readyState === 0 ) && ( xhr.status === 0 ) ) ){
history.log( 'stateUpdater error:', error, 'responseText:', xhr.responseText );
var msg = _l( 'An error occurred while getting updates from the server.' ) + ' '
+ _l( 'Please contact a Galaxy administrator if the problem persists.' );
history.trigger( 'error', msg, xhr, status, error );
}
});
},
@@ -248,9 +201,11 @@ var History = BaseModel.extend( LoggableMixin ).extend(
history.updateHdas( errorJson );
} else {
var msg = _l( 'ERROR updating hdas from api history contents' ) + ': ';
history.log( msg, hdaIds, xhr, status, error, errorJson );
alert( msg + hdaIds.join(',') );
history.log( 'Error updating hdas from api history contents',
hdaIds, xhr, status, error, errorJson );
var msg = _l( 'An error occurred while getting dataset details from the server.' ) + ' '
+ _l( 'Please contact a Galaxy administrator if the problem persists.' );
history.trigger( 'error', msg, xhr, status, error );
}
},
+55 -11
View File
@@ -75,7 +75,8 @@ var HistoryPanel = BaseView.extend( LoggableMixin ).extend(
/** event map
*/
events : {
'click #history-tag' : 'loadAndDisplayTags'
'click #history-tag' : 'loadAndDisplayTags',
'click #message-container' : 'removeMessage'
},
// ......................................................................... SET UP
@@ -104,10 +105,28 @@ var HistoryPanel = BaseView.extend( LoggableMixin ).extend(
this._setUpWebStorage( attributes.initiallyExpanded, attributes.show_deleted, attributes.show_hidden );
this._setUpEventHandlers();
// set up instance vars
/** map of hda model ids to hda views */
this.hdaViews = {};
/** map web controller urls for history related actions */
this.urls = {};
},
_setUpEventHandlers : function(){
// ---- model
// don't need to re-render entire model on all changes, just render disk size when it changes
//this.model.bind( 'change', this.render, this );
this.model.bind( 'change:nice_size', this.updateHistoryDiskSize, this );
// don't need to re-render entire model on all changes, just render disk size when it changes
this.model.bind( 'error', function( msg, xhr, error, status ){
this.displayMessage( 'error', msg );
this.model.attributes.error = undefined;
}, this );
// ---- hdas
// bind events from the model's hda collection
this.model.hdas.bind( 'add', this.add, this );
this.model.hdas.bind( 'reset', this.addAll, this );
@@ -126,15 +145,16 @@ var HistoryPanel = BaseView.extend( LoggableMixin ).extend(
}
}, this );
//this.bind( 'all', function(){
// this.log( arguments );
//}, this );
// ---- self
this.bind( 'error', function( msg, xhr, error, status ){
this.displayMessage( 'error', msg );
});
// set up instance vars
/** map of hda model ids to hda views */
this.hdaViews = {};
/** map web controller urls for history related actions */
this.urls = {};
if( this.logger ){
this.bind( 'all', function( event ){
this.log( this + '', arguments );
}, this );
}
},
/** Set up client side storage. Currently PersistanStorage keyed under 'HistoryPanel.<id>'
@@ -341,6 +361,9 @@ var HistoryPanel = BaseView.extend( LoggableMixin ).extend(
hdaView.bind( 'body-collapsed', function( id ){
historyView.storage.get( 'expandedHdas' ).deleteKey( id );
});
hdaView.bind( 'error', function( msg, xhr, status, error ){
historyView.displayMessage( 'error', msg );
});
},
/** Set up HistoryPanel js/widget behaviours
@@ -431,7 +454,8 @@ var HistoryPanel = BaseView.extend( LoggableMixin ).extend(
//TODO: into sub-MV
loadAndDisplayTags : function( event ){
this.log( this + '.loadAndDisplayTags', event );
var tagArea = this.$el.find( '#history-tag-area' ),
var panel = this,
tagArea = this.$el.find( '#history-tag-area' ),
tagElt = tagArea.find( '.tag-elt' );
this.log( '\t tagArea', tagArea, ' tagElt', tagElt );
@@ -443,7 +467,10 @@ var HistoryPanel = BaseView.extend( LoggableMixin ).extend(
$.ajax({
//TODO: the html from this breaks a couple of times
url: view.urls.tag,
error: function() { alert( _l( "Tagging failed" ) ); },
error: function( xhr, error, status ) {
panel.log( 'Error loading tag area html', xhr, error, status );
panel.trigger( 'error', _l( "Tagging failed" ), xhr, error, status );
},
success: function(tag_elt_html) {
//view.log( view + ' tag elt html (ajax)', tag_elt_html );
tagElt.html(tag_elt_html);
@@ -463,6 +490,23 @@ var HistoryPanel = BaseView.extend( LoggableMixin ).extend(
return false;
},
/** display a message in the top of the panel
* @param {String} type type of message ('done', 'error', 'warning')
* @param {String} msg the message to display
*/
displayMessage : function( type, msg ){
var $msgContainer = this.$el.find( '#message-container' ),
$msg = $( '<div/>' ).addClass( type + 'message' ).text( msg );
$msgContainer.html( $msg );
},
/** Remove a message from the panel
*/
removeMessage : function(){
var $msgContainer = this.$el.find( '#message-container' );
$msgContainer.html( null );
},
// ......................................................................... MISC
/** Return a string rep of the history
*/
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
var HistoryDatasetAssociation=BaseModel.extend(LoggableMixin).extend({defaults:{history_id:null,model_class:"HistoryDatasetAssociation",hid:0,id:null,name:"(unnamed dataset)",state:"new",data_type:null,file_size:0,file_ext:"",meta_files:[],misc_blurb:"",misc_info:"",deleted:false,purged:false,visible:true,accessible:true},urlRoot:"api/histories/",url:function(){return"api/histories/"+this.get("history_id")+"/contents/"+this.get("id")},initialize:function(){this.log(this+".initialize",this.attributes);this.log("\tparent history_id: "+this.get("history_id"));if(!this.get("accessible")){this.set("state",HistoryDatasetAssociation.STATES.NOT_VIEWABLE)}this.on("change:state",function(b,a){this.log(this+" has changed state:",b,a);if(this.inReadyState()){this.trigger("state:ready",b,a,this.previous("state"))}})},isDeletedOrPurged:function(){return(this.get("deleted")||this.get("purged"))},isVisible:function(b,c){var a=true;if((!b)&&(this.get("deleted")||this.get("purged"))){a=false}if((!c)&&(!this.get("visible"))){a=false}return a},inReadyState:function(){var a=this.get("state");return(this.isDeletedOrPurged()||(a===HistoryDatasetAssociation.STATES.OK)||(a===HistoryDatasetAssociation.STATES.EMPTY)||(a===HistoryDatasetAssociation.STATES.FAILED_METADATA)||(a===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(a===HistoryDatasetAssociation.STATES.DISCARDED)||(a===HistoryDatasetAssociation.STATES.ERROR))},hasData:function(){return(this.get("file_size")>0)},toString:function(){var a=this.get("id")||"";if(this.get("name")){a=this.get("hid")+' :"'+this.get("name")+'",'+a}return"HDA("+a+")"}});HistoryDatasetAssociation.STATES={UPLOAD:"upload",QUEUED:"queued",PAUSED:"paused",RUNNING:"running",SETTING_METADATA:"setting_metadata",NEW:"new",EMPTY:"empty",OK:"ok",FAILED_METADATA:"failed_metadata",NOT_VIEWABLE:"noPermission",DISCARDED:"discarded",ERROR:"error"};var HDACollection=Backbone.Collection.extend(LoggableMixin).extend({model:HistoryDatasetAssociation,initialize:function(){},ids:function(){return this.map(function(a){return a.id})},getByHid:function(a){return _.first(this.filter(function(b){return b.get("hid")===a}))},hidToCollectionIndex:function(a){if(!a){return this.models.length}var d=this.models.length-1;for(var b=d;b>=0;b--){var c=this.at(b).get("hid");if(c==a){return b}if(c<a){return b+1}}return null},getVisible:function(a,b){return this.filter(function(c){return c.isVisible(a,b)})},getStateLists:function(){var a={};_.each(_.values(HistoryDatasetAssociation.STATES),function(b){a[b]=[]});this.each(function(b){a[b.get("state")].push(b.get("id"))});return a},running:function(){var a=[];this.each(function(b){if(!b.inReadyState()){a.push(b.get("id"))}});return a},set:function(a){var b=this;if(!a||!_.isArray(a)){return}a.forEach(function(c){var d=b.get(c.id);if(d){d.set(c)}})},update:function(a){this.log(this+"update:",a);if(!(a&&a.length)){return[]}var c=this,b=null;_.each(a,function(f,d){var e=c.get(f);if(e){e.fetch();b.push(e)}});return b},toString:function(){return("HDACollection()")}});
var HistoryDatasetAssociation=BaseModel.extend(LoggableMixin).extend({defaults:{history_id:null,model_class:"HistoryDatasetAssociation",hid:0,id:null,name:"(unnamed dataset)",state:"new",data_type:null,file_size:0,file_ext:"",meta_files:[],misc_blurb:"",misc_info:"",deleted:false,purged:false,visible:true,accessible:true},urlRoot:"api/histories/",url:function(){return this.urlRoot+this.get("history_id")+"/contents/"+this.get("id")},initialize:function(){this.log(this+".initialize",this.attributes);this.log("\tparent history_id: "+this.get("history_id"));if(!this.get("accessible")){this.set("state",HistoryDatasetAssociation.STATES.NOT_VIEWABLE)}this.on("change:state",function(b,a){this.log(this+" has changed state:",b,a);if(this.inReadyState()){this.trigger("state:ready",b,a,this.previous("state"))}})},isDeletedOrPurged:function(){return(this.get("deleted")||this.get("purged"))},isVisible:function(b,c){var a=true;if((!b)&&(this.get("deleted")||this.get("purged"))){a=false}if((!c)&&(!this.get("visible"))){a=false}return a},inReadyState:function(){var a=this.get("state");return(this.isDeletedOrPurged()||(a===HistoryDatasetAssociation.STATES.OK)||(a===HistoryDatasetAssociation.STATES.EMPTY)||(a===HistoryDatasetAssociation.STATES.FAILED_METADATA)||(a===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(a===HistoryDatasetAssociation.STATES.DISCARDED)||(a===HistoryDatasetAssociation.STATES.ERROR))},hasData:function(){return(this.get("file_size")>0)},toString:function(){var a=this.get("id")||"";if(this.get("name")){a=this.get("hid")+' :"'+this.get("name")+'",'+a}return"HDA("+a+")"}});HistoryDatasetAssociation.STATES={UPLOAD:"upload",QUEUED:"queued",PAUSED:"paused",RUNNING:"running",SETTING_METADATA:"setting_metadata",NEW:"new",EMPTY:"empty",OK:"ok",FAILED_METADATA:"failed_metadata",NOT_VIEWABLE:"noPermission",DISCARDED:"discarded",ERROR:"error"};var HDACollection=Backbone.Collection.extend(LoggableMixin).extend({model:HistoryDatasetAssociation,initialize:function(){},ids:function(){return this.map(function(a){return a.id})},getByHid:function(a){return _.first(this.filter(function(b){return b.get("hid")===a}))},hidToCollectionIndex:function(a){if(!a){return this.models.length}var d=this.models.length-1;for(var b=d;b>=0;b--){var c=this.at(b).get("hid");if(c==a){return b}if(c<a){return b+1}}return null},getVisible:function(a,b){return this.filter(function(c){return c.isVisible(a,b)})},getStateLists:function(){var a={};_.each(_.values(HistoryDatasetAssociation.STATES),function(b){a[b]=[]});this.each(function(b){a[b.get("state")].push(b.get("id"))});return a},running:function(){var a=[];this.each(function(b){if(!b.inReadyState()){a.push(b.get("id"))}});return a},set:function(a){var b=this;if(!a||!_.isArray(a)){return}a.forEach(function(c){var d=b.get(c.id);if(d){d.set(c)}})},update:function(a){this.log(this+"update:",a);if(!(a&&a.length)){return[]}var c=this,b=null;_.each(a,function(f,d){var e=c.get(f);if(e){e.fetch();b.push(e)}});return b},toString:function(){return("HDACollection()")}});
@@ -1 +1 @@
var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:"",name:"",state:"",diskSize:0,deleted:false,annotation:null,message:null},urlRoot:"api/histories/",url:function(){return"api/histories/"+this.get("id")},initialize:function(a,b){this.log(this+".initialize:",a,b);this.hdas=new HDACollection();if(b){if(_.isArray(b)){this.hdas.reset(b);this.checkForUpdates();if(this.hdas.length>0){this.updateDisplayApplications()}}else{if(_.isString(b)){this.log("error in initialHdas: ",b);Galaxy.show_modal(_l("Error loading datasets for history"),b,{Ok:function(){Galaxy.hide_modal()}})}}}this.hdas.bind("state:ready",function(d,f,c){if(d.get("force_history_refresh")){var e=this;setTimeout(function(){e.stateUpdater()},History.UPDATE_DELAY)}},this)},loadFromApi:function(a,c){var b=this;b.attributes.id=a;jQuery.when(jQuery.ajax("api/users/current"),b.fetch()).then(function(e,d){b.attributes.user=e[0];b.trigger("loaded:user",e[0]);b.trigger("loaded",d[0])}).then(function(){jQuery.ajax(b.url()+"/contents?"+jQuery.param({ids:b.hdaIdsFromStateIds().join(",")})).success(function(d){b.hdas.reset(d);b.checkForUpdates();b.trigger("loaded:hdas",d);if(c){callback(b)}})})},hdaIdsFromStateIds:function(){return _.reduce(_.values(this.get("state_ids")),function(b,a){return b.concat(a)})},checkForUpdates:function(a){if(this.hdas.running().length){this.stateUpdater()}else{this.trigger("ready")}return this},stateUpdater:function(){var c=this,a=this.get("state"),b=this.get("state_ids");jQuery.ajax("api/histories/"+this.get("id")).success(function(d){c.set(d);c.log("current history state:",c.get("state"),"(was)",a,"new size:",c.get("nice_size"));var e=[];_.each(_.keys(d.state_ids),function(g){var f=_.difference(d.state_ids[g],b[g]);e=e.concat(f)});if(e.length){c.fetchHdaUpdates(e)}if((c.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(c.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){setTimeout(function(){c.stateUpdater()},History.UPDATE_DELAY)}else{c.trigger("ready")}}).error(function(f,d,e){if(!((f.readyState===0)&&(f.status===0))){alert(_l("Error getting history updates from the server:")+"\n"+e);c.log("stateUpdater error:",e,"responseText:",f.responseText)}})},fetchHdaUpdates:function(b){var a=this;jQuery.ajax({url:this.url()+"/contents?"+jQuery.param({ids:b.join(",")}),error:function(h,c,d){if((h.readyState===0)&&(h.status===0)){return}var f=JSON.parse(h.responseText);if(_.isArray(f)){var e=_.groupBy(f,function(i){if(_.has(i,"error")){return"errored"}return"ok"});a.log("fetched, errored datasets:",e.errored);a.updateHdas(f)}else{var g=_l("ERROR updating hdas from api history contents")+": ";a.log(g,b,h,c,d,f);alert(g+b.join(","))}},success:function(d,c,e){a.log(a+".fetchHdaUpdates, success:",c,e);a.updateHdas(d)}})},updateHdas:function(a){var c=this,b=[];c.log(c+".updateHdas:",a);_.each(a,function(e,f){var d=c.hdas.get(e.id);if(d){c.log("found existing model in list for id "+e.id+", updating...:");d.set(e)}else{c.log("NO existing model for id "+e.id+", creating...:");b.push(e)}});if(b.length){c.addHdas(b)}},addHdas:function(a){var b=this;_.each(a,function(c,d){var e=b.hdas.hidToCollectionIndex(c.hid);c.history_id=b.get("id");b.hdas.add(new HistoryDatasetAssociation(c),{at:e,silent:true})});b.hdas.trigger("add",a)},updateDisplayApplications:function(a){this.log(this+"updateDisplayApplications:",a);var c=this,b=(a&&_.isArray(a))?({hda_ids:a.join(",")}):({});c.log(this+": fetching display application data");jQuery.ajax("history/get_display_application_links",{data:b,success:function(e,d,f){c.hdas.set(e)},error:function(g,d,e){if(!((g.readyState===0)&&(g.status===0))){var f="Error fetching display applications, "+a+":"+(g.responseText||e);Galaxy.show_modal("History panel error",f,{Ok:function(){Galaxy.hide_modal()}});this.log(f)}}})},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});History.UPDATE_DELAY=4000;var HistoryCollection=Backbone.Collection.extend(LoggableMixin).extend({model:History,urlRoot:"api/histories"});
var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:null,name:"Unnamed History",state:"new",diskSize:0,deleted:false},urlRoot:"api/histories/",url:function(){return this.urlRoot+this.get("id")},initialize:function(b,c,a){a=a||null;this.log(this+".initialize:",b,c);this.hdas=new HDACollection();if(c&&_.isArray(c)){this.hdas.reset(c);this.checkForUpdates();if(this.hdas.length>0){this.updateDisplayApplications()}}this.hdas.bind("state:ready",function(e,g,d){if(e.get("force_history_refresh")){var f=this;setTimeout(function(){f.stateUpdater()},History.UPDATE_DELAY)}},this);if(this.logger){this.bind("all",function(d){this.log(this+"",arguments)},this)}},hdaIdsFromStateIds:function(){return _.reduce(_.values(this.get("state_ids")),function(b,a){return b.concat(a)})},checkForUpdates:function(){if(this.hdas.running().length){this.stateUpdater()}else{this.trigger("ready")}return this},stateUpdater:function(){var c=this,a=this.get("state"),b=this.get("state_ids");jQuery.ajax("api/histories/"+this.get("id")).success(function(d){c.set(d);c.log("current history state:",c.get("state"),"(was)",a,"new size:",c.get("nice_size"));var e=[];_.each(_.keys(d.state_ids),function(g){var f=_.difference(d.state_ids[g],b[g]);e=e.concat(f)});if(e.length){c.fetchHdaUpdates(e)}if((c.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(c.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){setTimeout(function(){c.stateUpdater()},History.UPDATE_DELAY)}else{c.trigger("ready")}}).error(function(g,d,e){if(g.status===502){setTimeout(function(){c.log("Bad Gateway error. Retrying...");c.stateUpdater()},History.UPDATE_DELAY)}else{if(!((g.readyState===0)&&(g.status===0))){c.log("stateUpdater error:",e,"responseText:",g.responseText);var f=_l("An error occurred while getting updates from the server.")+" "+_l("Please contact a Galaxy administrator if the problem persists.");c.trigger("error",f,g,d,e)}}})},fetchHdaUpdates:function(b){var a=this;jQuery.ajax({url:this.url()+"/contents?"+jQuery.param({ids:b.join(",")}),error:function(h,c,d){if((h.readyState===0)&&(h.status===0)){return}var f=JSON.parse(h.responseText);if(_.isArray(f)){var e=_.groupBy(f,function(i){if(_.has(i,"error")){return"errored"}return"ok"});a.log("fetched, errored datasets:",e.errored);a.updateHdas(f)}else{a.log("Error updating hdas from api history contents",b,h,c,d,f);var g=_l("An error occurred while getting dataset details from the server.")+" "+_l("Please contact a Galaxy administrator if the problem persists.");a.trigger("error",g,h,c,d)}},success:function(d,c,e){a.log(a+".fetchHdaUpdates, success:",c,e);a.updateHdas(d)}})},updateHdas:function(a){var c=this,b=[];c.log(c+".updateHdas:",a);_.each(a,function(e,f){var d=c.hdas.get(e.id);if(d){c.log("found existing model in list for id "+e.id+", updating...:");d.set(e)}else{c.log("NO existing model for id "+e.id+", creating...:");b.push(e)}});if(b.length){c.addHdas(b)}},addHdas:function(a){var b=this;_.each(a,function(c,d){var e=b.hdas.hidToCollectionIndex(c.hid);c.history_id=b.get("id");b.hdas.add(new HistoryDatasetAssociation(c),{at:e,silent:true})});b.hdas.trigger("add",a)},updateDisplayApplications:function(a){this.log(this+"updateDisplayApplications:",a);var c=this,b=(a&&_.isArray(a))?({hda_ids:a.join(",")}):({});c.log(this+": fetching display application data");jQuery.ajax("history/get_display_application_links",{data:b,success:function(e,d,f){c.hdas.set(e)},error:function(g,d,e){if(!((g.readyState===0)&&(g.status===0))){var f="Error fetching display applications, "+a+":"+(g.responseText||e);Galaxy.show_modal("History panel error",f,{Ok:function(){Galaxy.hide_modal()}});this.log(f)}}})},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});History.UPDATE_DELAY=4000;var HistoryCollection=Backbone.Collection.extend(LoggableMixin).extend({model:History,urlRoot:"api/histories"});
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -184,7 +184,7 @@ function program26(depth0,data) {
function program28(depth0,data) {
var buffer = "", stack1;
buffer += "\n <div id=\"message-container\">\n <div class=\"";
buffer += "\n <div class=\"";
if (stack1 = helpers.status) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
else { stack1 = depth0.status; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
buffer += escapeExpression(stack1)
@@ -192,7 +192,7 @@ function program28(depth0,data) {
if (stack1 = helpers.message) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
else { stack1 = depth0.message; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
buffer += escapeExpression(stack1)
+ "</div>\n </div>\n ";
+ "</div>\n ";
return buffer;
}
@@ -228,10 +228,10 @@ function program32(depth0,data) {
buffer += "\n\n ";
stack2 = helpers['if'].call(depth0, depth0.deleted, {hash:{},inverse:self.noop,fn:self.program(24, program24, data),data:data});
if(stack2 || stack2 === 0) { buffer += stack2; }
buffer += "\n\n ";
buffer += "\n\n <div id=\"message-container\">\n ";
stack2 = helpers['if'].call(depth0, depth0.message, {hash:{},inverse:self.noop,fn:self.program(28, program28, data),data:data});
if(stack2 || stack2 === 0) { buffer += stack2; }
buffer += "\n\n <div id=\"quota-message-container\" style=\"display: none\">\n <div id=\"quota-message\" class=\"errormessage\">\n ";
buffer += "\n </div>\n\n <div id=\"quota-message-container\" style=\"display: none\">\n <div id=\"quota-message\" class=\"errormessage\">\n ";
options = {hash:{},inverse:self.noop,fn:self.program(30, program30, data),data:data};
if (stack2 = helpers.local) { stack2 = stack2.call(depth0, options); }
else { stack2 = depth0.local; stack2 = typeof stack2 === functionType ? stack2.apply(depth0) : stack2; }
@@ -62,11 +62,11 @@ History panel/page - the main container for hdas (gen. on the left hand of the g
{{#warningmessagesmall}}{{#local}}You are currently viewing a deleted history!{{/local}}{{/warningmessagesmall}}
{{/if}}
{{#if message}}
<div id="message-container">
{{#if message}}
<div class="{{status}}message">{{message}}</div>
{{/if}}
</div>
{{/if}}
<div id="quota-message-container" style="display: none">
<div id="quota-message" class="errormessage">
+17 -21
View File
@@ -221,28 +221,25 @@ ${h.js(
)}
<script type="text/javascript">
function modalAsAlert( title, body, buttons ){
alert( title + ':\n' + body );
}
function galaxyPageSetUp(){
// moving global functions, objects into Galaxy namespace
top.Galaxy = top.Galaxy || {};
// bad idea from memleak standpoint?
top.Galaxy.mainWindow = top.Galaxy.mainWindow || top.frames.galaxy_main;
top.Galaxy.toolWindow = top.Galaxy.toolWindow || top.frames.galaxy_tools;
top.Galaxy.historyWindow = top.Galaxy.historyWindow || top.frames.galaxy_history;
top.Galaxy.$masthead = top.Galaxy.$masthead || $( top.document ).find( 'div#masthead' );
top.Galaxy.$messagebox = top.Galaxy.$messagebox || $( top.document ).find( 'div#messagebox' );
top.Galaxy.$leftPanel = top.Galaxy.$leftPanel || $( top.document ).find( 'div#left' );
top.Galaxy.$centerPanel = top.Galaxy.$centerPanel || $( top.document ).find( 'div#center' );
top.Galaxy.$rightPanel = top.Galaxy.$rightPanel || $( top.document ).find( 'div#right' );
if( top != window ){
top.Galaxy.mainWindow = top.Galaxy.mainWindow || top.frames.galaxy_main;
top.Galaxy.toolWindow = top.Galaxy.toolWindow || top.frames.galaxy_tools;
top.Galaxy.historyWindow = top.Galaxy.historyWindow || top.frames.galaxy_history;
//modals
top.Galaxy.show_modal = top.show_modal || modalAsAlert;
top.Galaxy.hide_modal = top.hide_modal || function(){};
top.Galaxy.$masthead = top.Galaxy.$masthead || $( top.document ).find( 'div#masthead' );
top.Galaxy.$messagebox = top.Galaxy.$messagebox || $( top.document ).find( 'div#messagebox' );
top.Galaxy.$leftPanel = top.Galaxy.$leftPanel || $( top.document ).find( 'div#left' );
top.Galaxy.$centerPanel = top.Galaxy.$centerPanel || $( top.document ).find( 'div#center' );
top.Galaxy.$rightPanel = top.Galaxy.$rightPanel || $( top.document ).find( 'div#right' );
//modals
top.Galaxy.show_modal = top.show_modal;
top.Galaxy.hide_modal = top.hide_modal;
}
// other base functions
@@ -297,7 +294,7 @@ $(function(){
historyJson.user = userJson;
// create the history panel
var history = new History( historyJson, hdaJson );
var history = new History( historyJson, hdaJson, ( debugging )?( console ):( null ) );
var historyPanel = new HistoryPanel({
model : history,
urlTemplates : galaxy_paths.attributes,
@@ -445,11 +442,10 @@ $(function(){
.warningmessagesmall {
margin: 8px 0 0 0;
}
#message-container {
margin: 8px 0 0 0;
#message-container div {
}
#message-container [class$="message"] {
margin: 0px;
margin: 8px 0 0 0;
}
/*---- history level */