diff --git a/lib/galaxy/webapps/galaxy/api/history_contents.py b/lib/galaxy/webapps/galaxy/api/history_contents.py
index 2a9ccb5201d..882715d9a48 100644
--- a/lib/galaxy/webapps/galaxy/api/history_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/history_contents.py
@@ -60,8 +60,20 @@ class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociatio
# but we(I?) need an hda collection with full data somewhere
ids = ids.split( ',' )
for hda in history.datasets:
- if trans.security.encode_id( hda.id ) in ids:
- rval.append( get_hda_dict( trans, history, hda, for_editing=True ) )
+ #TODO: curr. ordered by history, change to order from ids list
+ encoded_hda_id = trans.security.encode_id( hda.id )
+ if encoded_hda_id in ids:
+ #TODO: share code with show
+ try:
+ rval.append( get_hda_dict( trans, history, hda, for_editing=True ) )
+
+ 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", history_id, encoded_hda_id, str( exc ) )
+ rval.append( self._exception_as_hda_dict( trans, encoded_hda_id, exc ) )
else:
# if no ids passed, return a _SUMMARY_ of _all_ datasets in the history
@@ -69,12 +81,14 @@ class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociatio
rval.append( self._summary_hda_dict( trans, history_id, hda ) )
except Exception, e:
- rval = "Error in history API at listing contents"
+ # 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 ) ) )
trans.response.status = 500
return rval
+ #TODO: move to model or Mixin
def _summary_hda_dict( self, trans, history_id, hda ):
"""
Returns a dictionary based on the HDA in .. _summary form::
@@ -94,6 +108,26 @@ 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.
+ {
+ 'id' : < the encoded dataset id >,
+ 'type' : < name of the dataset >,
+ 'url' : < api url to retrieve this datasets full data >,
+ }
+ """
+ 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
def show( self, trans, id, history_id, **kwd ):
"""
diff --git a/static/scripts/mvc/dataset/hda-base.js b/static/scripts/mvc/dataset/hda-base.js
index c2b149211e3..2a715a821b2 100644
--- a/static/scripts/mvc/dataset/hda-base.js
+++ b/static/scripts/mvc/dataset/hda-base.js
@@ -445,7 +445,7 @@ var HDABaseView = BaseView.extend( LoggableMixin ).extend(
if( !this.model.get( 'purged' ) ){
parent.append( $( '
' + this.model.get( 'misc_blurb' ) + '
' ) );
}
- parent.append( ( _l( 'An error occurred running this job' ) + ': '
+ parent.append( ( _l( 'An error occurred with this dataset' ) + ': '
+ '' + $.trim( this.model.get( 'misc_info' ) ) + '' ) );
parent.append( this._render_primaryActionButtons(
this.defaultPrimaryActionButtonRenderers.concat([ this._render_downloadButton ])
diff --git a/static/scripts/mvc/history/history-model.js b/static/scripts/mvc/history/history-model.js
index 0b52663738d..612f1971581 100644
--- a/static/scripts/mvc/history/history-model.js
+++ b/static/scripts/mvc/history/history-model.js
@@ -55,9 +55,17 @@ var History = BaseModel.extend( LoggableMixin ).extend(
this.hdas = new HDACollection();
// if we've got hdas passed in the constructor, load them and set up updates if needed
- if( initialHdas && initialHdas.length ){
- this.hdas.reset( initialHdas );
- this.checkForUpdates();
+ if( initialHdas ){
+ if( _.isArray( initialHdas ) ){
+ this.hdas.reset( initialHdas );
+ this.checkForUpdates();
+
+ // 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 ) && ( initialHdas.match( /error/i ) ) ){
+ alert( _l( 'Error loading bootstrapped history' ) + ':\n' + initialHdas );
+ }
}
// events
@@ -163,7 +171,7 @@ var History = BaseModel.extend( LoggableMixin ).extend(
// send the changed ids (if any) to dataset collection to have them fetch their own model changes
if( changedIds.length ){
- history.updateHdas( changedIds );
+ history.fetchHdaUpdates( changedIds );
}
// set up to keep pulling if this history in run/queue state
@@ -193,16 +201,37 @@ var History = BaseModel.extend( LoggableMixin ).extend(
* If it's not in the collection, addHdas will be used to create it.
* @param {String[]} hdaIds an array of the encoded ids of the hdas to get from the server.
*/
- updateHdas : function( hdaIds ){
+ fetchHdaUpdates : function( hdaIds ){
//TODO:?? move to collection? still need proper url
var history = this;
jQuery.ajax({
url : this.url() + '/contents?' + jQuery.param({ ids : hdaIds.join(',') }),
+ /**
+ * @inner
+ */
error : function( xhr, status, error ){
- var msg = 'ERROR updating hdas from api history contents:';
- history.log( msg, hdaIds, xhr, status, error );
- alert( msg + hdaIds.join(',') );
+ if( ( xhr.readyState === 0 ) && ( xhr.status === 0 ) ){ return; }
+
+ var errorJson = JSON.parse( xhr.responseText );
+ if( _.isArray( errorJson ) ){
+
+ // just for debugging/reporting purposes
+ var grouped = _.groupBy( errorJson, function( hdaData ){
+ if( _.has( hdaData, 'error' ) ){ return 'errored'; }
+ return 'ok';
+ });
+ history.log( 'fetched, errored datasets:', grouped.errored );
+
+ // the server already formats the error'd hdas in proper hda-model form
+ // so we're good to send these on
+ 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(',') );
+ }
},
/** when the proper models for the requested ids are returned,
@@ -210,30 +239,41 @@ var History = BaseModel.extend( LoggableMixin ).extend(
* @inner
*/
success : function( hdaDataList, status, xhr ){
- history.log( history + '.updateHdas, success:', hdaDataList, status, xhr );
- //TODO: compile new models to be added in one go
- var hdasToAdd = [];
-
- _.each( hdaDataList, function( hdaData, index ){
- var existingModel = history.hdas.get( hdaData.id );
- // if this model exists already, update it
- if( existingModel ){
- history.log( 'found existing model in list for id ' + hdaData.id + ', updating...:' );
- existingModel.set( hdaData );
-
- // if this model is new and isn't in the hda collection, cache it to be created
- } else {
- history.log( 'NO existing model for id ' + hdaData.id + ', creating...:' );
- hdasToAdd.push( hdaData );
- }
- });
- if( hdasToAdd.length ){
- history.addHdas( hdasToAdd );
- }
+ history.log( history + '.fetchHdaUpdates, success:', status, xhr );
+ history.updateHdas( hdaDataList );
}
});
},
+ /** Update the models in the hdas collection from the data given.
+ * If a model exists in the collection, set will be used with the new data.
+ * If it's not in the collection, addHdas will be used to create it.
+ * @param {Object[]} hdaDataList an array of the model data used to update.
+ */
+ updateHdas : function( hdaDataList ){
+ var history = this,
+ // models/views that need to be created
+ hdasToAdd = [];
+ history.log( history + '.updateHdas:', hdaDataList );
+
+ _.each( hdaDataList, function( hdaData, index ){
+ var existingModel = history.hdas.get( hdaData.id );
+ // if this model exists already, update it
+ if( existingModel ){
+ history.log( 'found existing model in list for id ' + hdaData.id + ', updating...:' );
+ existingModel.set( hdaData );
+
+ // if this model is new and isn't in the hda collection, cache it to be created
+ } else {
+ history.log( 'NO existing model for id ' + hdaData.id + ', creating...:' );
+ hdasToAdd.push( hdaData );
+ }
+ });
+ if( hdasToAdd.length ){
+ history.addHdas( hdasToAdd );
+ }
+ },
+
/** Add multiple hda models to the hdas collection from an array of hda data.
*/
addHdas : function( hdaDataList ){
diff --git a/templates/root/alternate_history.mako b/templates/root/alternate_history.mako
index df9e20b2a5d..48a117d000f 100644
--- a/templates/root/alternate_history.mako
+++ b/templates/root/alternate_history.mako
@@ -191,13 +191,19 @@ ${ unquote_plus( h.to_json_string( url_dict ) ) }
##TODO: api/web controllers should use common code, and this section should call that code
<%def name="get_history( id )">
<%
- return trans.webapp.api_controllers[ 'histories' ].show( trans, trans.security.encode_id( id ) )
+ history_json = trans.webapp.api_controllers[ 'histories' ].show( trans, trans.security.encode_id( id ) )
+ #assert isinstance( history, dict ), (
+ # 'Bootstrapped history was expecting a dictionary: %s' %( str( history ) ) )
+ return history_json
%>
%def>
<%def name="get_current_user()">
<%
- return trans.webapp.api_controllers[ 'users' ].show( trans, 'current' )
+ user_json = trans.webapp.api_controllers[ 'users' ].show( trans, 'current' )
+ #assert isinstance( hdaDataList, list ), (
+ # 'Bootstrapped current user was expecting a dictionary: %s' %( str( user ) ) )
+ return user_json
%>
%def>
@@ -207,10 +213,10 @@ ${ unquote_plus( h.to_json_string( url_dict ) ) }
if not hdas:
return '[]'
# rather just use the history.id (wo the datasets), but...
- return trans.webapp.api_controllers[ 'history_contents' ].index(
- #trans, trans.security.encode_id( history_id ),
+ hda_json = trans.webapp.api_controllers[ 'history_contents' ].index(
trans, trans.security.encode_id( history_id ),
ids=( ','.join([ trans.security.encode_id( hda.id ) for hda in hdas ]) ) )
+ return hda_json
%>
%def>