PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/static/scripts/mvc/history/history-contents.js

https://bitbucket.org/hbc/galaxy-central-hbc/
JavaScript | 301 lines | 196 code | 28 blank | 77 comment | 28 complexity | 747a34ad8a2f535b27ec42212b3a93dd MD5 | raw file
Possible License(s): CC-BY-3.0
  1. define([
  2. "mvc/history/history-content-base",
  3. "mvc/dataset/hda-model",
  4. "mvc/collection/hdca-model",
  5. "mvc/base-mvc",
  6. "utils/localization"
  7. ], function( HISTORY_CONTENT, HDA_MODEL, HDCA_MODEL, BASE_MVC, _l ){
  8. //==============================================================================
  9. /** @class Backbone collection for history content.
  10. * NOTE: history content seems like a dataset collection, but differs in that it is mixed:
  11. * each element can be either an HDA (dataset) or a DatasetCollection and co-exist on
  12. * the same level.
  13. * Dataset collections on the other hand are not mixed and (so far) can only contain either
  14. * HDAs or child dataset collections on one level.
  15. * This is why this does not inherit from any of the DatasetCollections (currently).
  16. *
  17. * @borrows LoggableMixin#logger as #logger
  18. * @borrows LoggableMixin#log as #log
  19. * @constructs
  20. */
  21. var HistoryContents = Backbone.Collection.extend( BASE_MVC.LoggableMixin ).extend(
  22. /** @lends HistoryContents.prototype */{
  23. /** logger used to record this.log messages, commonly set to console */
  24. // comment this out to suppress log output
  25. //logger : console,
  26. /** since history content is a mix, override model fn into a factory, creating based on history_content_type */
  27. model : function( attrs, options ) {
  28. if( attrs.history_content_type === "dataset" ) {
  29. return new HDA_MODEL.HistoryDatasetAssociation( attrs, options );
  30. } else if( attrs.history_content_type === "dataset_collection" ) {
  31. switch( attrs.collection_type ){
  32. case 'list':
  33. return new HDCA_MODEL.HistoryListDatasetCollection( attrs, options );
  34. case 'paired':
  35. return new HDCA_MODEL.HistoryPairDatasetCollection( attrs, options );
  36. case 'list:paired':
  37. return new HDCA_MODEL.HistoryListPairedDatasetCollection( attrs, options );
  38. }
  39. throw new TypeError( 'Unknown collection_type: ' + attrs.collection_type );
  40. }
  41. // TODO: Handle unknown history_content_type...
  42. throw new TypeError( 'Unknown history_content_type: ' + attrs.history_content_type );
  43. },
  44. /** Set up.
  45. * @see Backbone.Collection#initialize
  46. */
  47. initialize : function( models, options ){
  48. options = options || {};
  49. this.historyId = options.historyId;
  50. //this._setUpListeners();
  51. this.on( 'all', function(){
  52. this.debug( this + '.event:', arguments );
  53. });
  54. },
  55. /** root api url */
  56. urlRoot : galaxy_config.root + 'api/histories',
  57. /** complete api url */
  58. url : function(){
  59. return this.urlRoot + '/' + this.historyId + '/contents';
  60. },
  61. // ........................................................................ common queries
  62. /** Get the ids of every hda in this collection
  63. * @returns array of encoded ids
  64. */
  65. ids : function(){
  66. return this.map( function( hda ){ return hda.get('id'); });
  67. },
  68. /** Get hdas that are not ready
  69. * @returns array of HDAs
  70. */
  71. notReady : function(){
  72. return this.filter( function( hda ){
  73. return !hda.inReadyState();
  74. });
  75. },
  76. /** Get the id of every hda in this collection not in a 'ready' state (running).
  77. * @returns an array of hda ids
  78. * @see HistoryDatasetAssociation#inReadyState
  79. */
  80. running : function(){
  81. var idList = [];
  82. this.each( function( item ){
  83. if( !item.inReadyState() ){
  84. idList.push( item.get( 'id' ) );
  85. }
  86. });
  87. return idList;
  88. },
  89. /** Get the hda with the given hid
  90. * @param {Int} hid the hid to search for
  91. * @returns {HistoryDatasetAssociation} the hda with the given hid or undefined if not found
  92. */
  93. getByHid : function( hid ){
  94. return _.first( this.filter( function( hda ){ return hda.get( 'hid' ) === hid; }) );
  95. },
  96. /** Get every 'shown' hda in this collection based on show_deleted/hidden
  97. * @param {Boolean} show_deleted are we showing deleted hdas?
  98. * @param {Boolean} show_hidden are we showing hidden hdas?
  99. * @returns array of hda models
  100. * @see HistoryDatasetAssociation#isVisible
  101. */
  102. getVisible : function( show_deleted, show_hidden, filters ){
  103. filters = filters || [];
  104. //this.debug( 'filters:', filters );
  105. // always filter by show deleted/hidden first
  106. this.debug( 'checking isVisible' );
  107. var filteredHdas = new HistoryContents( this.filter( function( item ){
  108. return item.isVisible( show_deleted, show_hidden );
  109. }));
  110. _.each( filters, function( filter_fn ){
  111. if( !_.isFunction( filter_fn ) ){ return; }
  112. filteredHdas = new HistoryContents( filteredHdas.filter( filter_fn ) );
  113. });
  114. return filteredHdas;
  115. },
  116. /** return true if any hdas don't have details */
  117. haveDetails : function(){
  118. return this.all( function( hda ){ return hda.hasDetails(); });
  119. },
  120. // ........................................................................ ajax
  121. /** fetch detailed model data for all HDAs in this collection */
  122. fetchAllDetails : function( options ){
  123. options = options || {};
  124. var detailsFlag = { details: 'all' };
  125. options.data = ( options.data )?( _.extend( options.data, detailsFlag ) ):( detailsFlag );
  126. return this.fetch( options );
  127. },
  128. /** using a queue, perform hdaAjaxFn on each of the hdas in this collection */
  129. ajaxQueue : function( hdaAjaxFn, options ){
  130. var deferred = jQuery.Deferred(),
  131. startingLength = this.length,
  132. responses = [];
  133. if( !startingLength ){
  134. deferred.resolve([]);
  135. return deferred;
  136. }
  137. // use reverse order (stylistic choice)
  138. var ajaxFns = this.chain().reverse().map( function( hda, i ){
  139. return function(){
  140. var xhr = hdaAjaxFn.call( hda, options );
  141. // if successful, notify using the deferred to allow tracking progress
  142. xhr.done( function( response ){
  143. deferred.notify({ curr: i, total: startingLength, response: response, model: hda });
  144. });
  145. // (regardless of previous error or success) if not last ajax call, shift and call the next
  146. // if last fn, resolve deferred
  147. xhr.always( function( response ){
  148. responses.push( response );
  149. if( ajaxFns.length ){
  150. ajaxFns.shift()();
  151. } else {
  152. deferred.resolve( responses );
  153. }
  154. });
  155. };
  156. }).value();
  157. // start the queue
  158. ajaxFns.shift()();
  159. return deferred;
  160. },
  161. // ........................................................................ sorting/filtering
  162. /** return a new collection of HDAs whose attributes contain the substring matchesWhat */
  163. matches : function( matchesWhat ){
  164. return this.filter( function( hda ){
  165. return hda.matches( matchesWhat );
  166. });
  167. },
  168. // ........................................................................ misc
  169. set : function( models, options ){
  170. // arrrrrrrrrrrrrrrrrg...
  171. // override to get a correct/smarter merge when incoming data is partial (e.g. stupid backbone)
  172. // w/o this partial models from the server will fill in missing data with model defaults
  173. // and overwrite existing data on the client
  174. // see Backbone.Collection.set and _prepareModel
  175. var collection = this;
  176. models = _.map( models, function( model ){
  177. var attrs = model.attributes || model; // Handle raw json or Backbone model.
  178. var typeId = HISTORY_CONTENT.HistoryContent.typeIdStr( attrs.history_content_type, attrs.id );
  179. var existing = collection.get( typeId );
  180. if( !existing ){ return model; }
  181. // merge the models _BEFORE_ calling the superclass version
  182. var merged = existing.toJSON();
  183. _.extend( merged, model );
  184. return merged;
  185. });
  186. // now call superclass when the data is filled
  187. Backbone.Collection.prototype.set.call( this, models, options );
  188. },
  189. /** Convert this ad-hoc collection of HDAs to a formal collection tracked
  190. by the server.
  191. **/
  192. promoteToHistoryDatasetCollection : function _promote( history, collection_type, options ){
  193. options = options || {};
  194. options.url = this.url();
  195. options.type = "POST";
  196. var full_collection_type = collection_type;
  197. var element_identifiers = [],
  198. name = null;
  199. // This mechanism is rough - no error handling, allows invalid selections, no way
  200. // for user to pick/override element identifiers. This is only really meant
  201. if( collection_type === "list" ) {
  202. this.chain().each( function( hda ) {
  203. // TODO: Handle duplicate names.
  204. var name = hda.attributes.name;
  205. var id = hda.get('id');
  206. var content_type = hda.attributes.history_content_type;
  207. if( content_type === "dataset" ) {
  208. if( full_collection_type !== "list" ) {
  209. this.log( "Invalid collection type" );
  210. }
  211. element_identifiers.push( { name: name, src: "hda", id: id } );
  212. } else {
  213. if( full_collection_type === "list" ) {
  214. full_collection_type = "list:" + hda.attributes.collection_type;
  215. } else {
  216. if( full_collection_type !== "list:" + hda.attributes.collection_type ) {
  217. this.log( "Invalid collection type" );
  218. }
  219. }
  220. element_identifiers.push( { name: name, src: "hdca", id: id } );
  221. }
  222. });
  223. name = "New Dataset List";
  224. } else if( collection_type === "paired" ) {
  225. var ids = this.ids();
  226. if( ids.length !== 2 ){
  227. // TODO: Do something...
  228. }
  229. element_identifiers.push( { name: "forward", src: "hda", id: ids[ 0 ] } );
  230. element_identifiers.push( { name: "reverse", src: "hda", id: ids[ 1 ] } );
  231. name = "New Dataset Pair";
  232. }
  233. options.data = {
  234. type: "dataset_collection",
  235. name: name,
  236. collection_type: full_collection_type,
  237. element_identifiers: JSON.stringify( element_identifiers )
  238. };
  239. var xhr = jQuery.ajax( options );
  240. xhr.done( function( message, status, responseObj ){
  241. history.refresh( );
  242. });
  243. xhr.fail( function( xhr, status, message ){
  244. if( xhr.responseJSON && xhr.responseJSON.error ){
  245. error = xhr.responseJSON.error;
  246. } else {
  247. error = xhr.responseJSON;
  248. }
  249. xhr.responseText = error;
  250. // Do something?
  251. });
  252. return xhr;
  253. },
  254. /** debugging */
  255. print : function(){
  256. var contents = this;
  257. contents.each( function( c ){
  258. contents.debug( c );
  259. if( c.elements ){
  260. contents.debug( '\t elements:', c.elements );
  261. }
  262. });
  263. },
  264. /** String representation. */
  265. toString : function(){
  266. return ([ 'HistoryContents(', [ this.historyId, this.length ].join(), ')' ].join( '' ));
  267. }
  268. });
  269. //==============================================================================
  270. return {
  271. HistoryContents : HistoryContents
  272. };
  273. });