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 2012
- 1 participants
- 133 discussions
commit/galaxy-central: clements: Change docstring so it no longer gerneates a warning in Sphinx.
by Bitbucket 06 Nov '12
by Bitbucket 06 Nov '12
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/f2abe519d05e/
changeset: f2abe519d05e
user: clements
date: 2012-11-01 06:50:03
summary: Change docstring so it no longer gerneates a warning in Sphinx.
affected #: 1 file
diff -r 563d279f69bcec68c76a5a3c7c4144b34b614011 -r f2abe519d05e6f4e01fcf9983ab3fb72f5009272 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -340,7 +340,7 @@
def get_database_engine_options( kwargs ):
"""
Allow options for the SQLAlchemy database engine to be passed by using
- the prefix "database_engine_option_".
+ the prefix "database_engine_option".
"""
conversions = {
'convert_unicode': string_as_bool,
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: greg: Add the ability to view the current data tables registry.
by Bitbucket 06 Nov '12
by Bitbucket 06 Nov '12
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/563d279f69bc/
changeset: 563d279f69bc
user: greg
date: 2012-11-06 22:11:13
summary: Add the ability to view the current data tables registry.
affected #: 4 files
diff -r 1911265573315cf71962ac1ae5ca6ba513d051b2 -r 563d279f69bcec68c76a5a3c7c4144b34b614011 lib/galaxy/webapps/galaxy/controllers/admin.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin.py
@@ -763,3 +763,9 @@
message = util.restore_text( kwd.get( 'message', '' ) )
status = util.restore_text( kwd.get( 'status', 'done' ) )
return trans.fill_template( 'admin/view_datatypes_registry.mako', message=message, status=status )
+ @web.expose
+ @web.require_admin
+ def view_tool_data_tables( self, trans, **kwd ):
+ message = util.restore_text( kwd.get( 'message', '' ) )
+ status = util.restore_text( kwd.get( 'status', 'done' ) )
+ return trans.fill_template( 'admin/view_data_tables_registry.mako', message=message, status=status )
diff -r 1911265573315cf71962ac1ae5ca6ba513d051b2 -r 563d279f69bcec68c76a5a3c7c4144b34b614011 templates/admin/view_data_tables_registry.mako
--- /dev/null
+++ b/templates/admin/view_data_tables_registry.mako
@@ -0,0 +1,42 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<%
+ ctr = 0
+ data_tables = trans.app.tool_data_tables
+ sorted_data_table_elem_names = sorted( trans.app.tool_data_tables.data_table_elem_names )
+%>
+
+<div class="toolForm">
+ <div class="toolFormTitle">Current data table registry contains ${len( sorted_data_table_elem_names )} data tables</div>
+ <div class="toolFormBody">
+ <table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
+ <tr>
+ <th bgcolor="#D8D8D8">Name</th>
+ <th bgcolor="#D8D8D8">Tool data path</th>
+ <th bgcolor="#D8D8D8">Missing index file</th>
+ </tr>
+ %for data_table_elem_name in sorted_data_table_elem_names:
+ <% data_table = data_tables[ data_table_elem_name ] %>
+ %if ctr % 2 == 1:
+ <tr class="odd_row">
+ %else:
+ <tr class="tr">
+ %endif
+ <td>${data_table.name}</td>
+ <td>${data_table.tool_data_path}</td>
+ <td>
+ %if data_table.missing_index_file:
+ ${data_table.missing_index_file}
+ %endif
+ </td>
+ </tr>
+ <% ctr += 1 %>
+ %endfor
+ </table>
+ </div>
+</div>
diff -r 1911265573315cf71962ac1ae5ca6ba513d051b2 -r 563d279f69bcec68c76a5a3c7c4144b34b614011 templates/admin/view_datatypes_registry.mako
--- a/templates/admin/view_datatypes_registry.mako
+++ b/templates/admin/view_datatypes_registry.mako
@@ -22,14 +22,14 @@
%><div class="toolForm">
- <div class="toolFormTitle">Current datatypes registry contains ${len( sorted_datatypes )} datatypes</div>
+ <div class="toolFormTitle">Current data types registry contains ${len( sorted_datatypes )} data types</div><div class="toolFormBody"><table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%"><tr>
- <th>Extension</th>
- <th>Type</th>
- <th>Mimetype</th>
- <th>Display in upload</th>
+ <th bgcolor="#D8D8D8">Extension</th>
+ <th bgcolor="#D8D8D8">Type</th>
+ <th bgcolor="#D8D8D8">Mimetype</th>
+ <th bgcolor="#D8D8D8">Display in upload</th></tr>
%for datatype in sorted_datatypes:
%if ctr % 2 == 1:
diff -r 1911265573315cf71962ac1ae5ca6ba513d051b2 -r 563d279f69bcec68c76a5a3c7c4144b34b614011 templates/webapps/galaxy/admin/index.mako
--- a/templates/webapps/galaxy/admin/index.mako
+++ b/templates/webapps/galaxy/admin/index.mako
@@ -65,7 +65,8 @@
<div class="toolSectionTitle">Server</div><div class="toolSectionBody"><div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='view_datatypes_registry' )}" target="galaxy_main">View datatypes registry</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='view_datatypes_registry' )}" target="galaxy_main">View data types registry</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='view_tool_data_tables' )}" target="galaxy_main">View data tables registry</a></div><div class="toolTitle"><a href="${h.url_for( controller='admin', action='tool_versions' )}" target="galaxy_main">View tool lineage</a></div><div class="toolTitle"><a href="${h.url_for( controller='admin', action='reload_tool' )}" target="galaxy_main">Reload a tool's configuration</a></div><div class="toolTitle"><a href="${h.url_for( controller='admin', action='memdump' )}" target="galaxy_main">Profile memory usage</a></div>
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/changeset/fdea658292c9/
changeset: fdea658292c9
user: carlfeberhard
date: 2012-11-06 21:54:47
summary: (alt)history: split files up, rework names, add global Galaxy namespace
affected #: 6 files
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 static/scripts/mvc/dataset/hda-edit.js
--- /dev/null
+++ b/static/scripts/mvc/dataset/hda-edit.js
@@ -0,0 +1,862 @@
+//define([
+// "../mvc/base-mvc"
+//], function(){
+//==============================================================================
+/** View for editing (working with - as opposed to viewing/read-only) an hda
+ *
+ */
+var HDAView = BaseView.extend( LoggableMixin ).extend({
+ //??TODO: add alias in initialize this.hda = this.model?
+ // view for HistoryDatasetAssociation model above
+
+ // uncomment this out see log messages
+ //logger : console,
+
+ tagName : "div",
+ className : "historyItemContainer",
+
+ // ................................................................................ SET UP
+ initialize : function( attributes ){
+ this.log( this + '.initialize:', attributes );
+
+ // render urlTemplates (gen. provided by GalaxyPaths) to urls
+ if( !attributes.urlTemplates ){ throw( 'HDAView needs urlTemplates on initialize' ); }
+ this.urls = this.renderUrls( attributes.urlTemplates, this.model.toJSON() );
+
+ // whether the body of this hda is expanded (shown)
+ this.expanded = attributes.expanded || false;
+
+ // re-render the entire view on any model change
+ this.model.bind( 'change', this.render, this );
+ //this.bind( 'all', function( event ){
+ // this.log( event );
+ //}, this );
+ },
+
+ // urlTemplates is a map (or nested map) of underscore templates (currently, anyhoo)
+ // use the templates to create the apropo urls for each action this ds could use
+ renderUrls : function( urlTemplates, modelJson ){
+ var hdaView = this,
+ urls = {};
+ _.each( urlTemplates, function( urlTemplateOrObj, urlKey ){
+ // object == nested templates: recurse
+ if( _.isObject( urlTemplateOrObj ) ){
+ urls[ urlKey ] = hdaView.renderUrls( urlTemplateOrObj, modelJson );
+
+ // string == template:
+ } else {
+ // meta_down load is a special case (see renderMetaDownloadUrls)
+ //TODO: should be a better (gen.) way to handle this case
+ if( urlKey === 'meta_download' ){
+ urls[ urlKey ] = hdaView.renderMetaDownloadUrls( urlTemplateOrObj, modelJson );
+ } else {
+ urls[ urlKey ] = _.template( urlTemplateOrObj, modelJson );
+ }
+ }
+ });
+ return urls;
+ },
+
+ // there can be more than one meta_file to download, so return a list of url and file_type for each
+ renderMetaDownloadUrls : function( urlTemplate, modelJson ){
+ return _.map( modelJson.meta_files, function( meta_file ){
+ return {
+ url : _.template( urlTemplate, { id: modelJson.id, file_type: meta_file.file_type }),
+ file_type : meta_file.file_type
+ };
+ });
+ },
+
+ // ................................................................................ RENDER MAIN
+ render : function(){
+ var view = this,
+ id = this.model.get( 'id' ),
+ state = this.model.get( 'state' ),
+ itemWrapper = $( '<div/>' ).attr( 'id', 'historyItem-' + id ),
+ initialRender = ( this.$el.children().size() === 0 );
+
+ //console.debug( this + '.render, initial?:', initialRender );
+
+ this._clearReferences();
+ this.$el.attr( 'id', 'historyItemContainer-' + id );
+
+ itemWrapper
+ .addClass( 'historyItemWrapper' ).addClass( 'historyItem' )
+ .addClass( 'historyItem-' + state );
+
+ itemWrapper.append( this._render_warnings() );
+ itemWrapper.append( this._render_titleBar() );
+ this.body = $( this._render_body() );
+ itemWrapper.append( this.body );
+
+ //TODO: move to own function: setUpBehaviours
+ // we can potentially skip this step and call popupmenu directly on the download button
+ make_popup_menus( itemWrapper );
+
+ // set up canned behavior on children (bootstrap, popupmenus, editable_text, etc.)
+ itemWrapper.find( '.tooltip' ).tooltip({ placement : 'bottom' });
+
+ // transition...
+ this.$el.fadeOut( 'fast', function(){
+ view.$el.children().remove();
+ view.$el.append( itemWrapper ).fadeIn( 'fast', function(){
+ view.log( view + ' rendered:', view.$el );
+ var renderedEventName = 'rendered';
+
+ if( initialRender ){
+ renderedEventName += ':initial';
+ } else if( view.model.inReadyState() ){
+ renderedEventName += ':ready';
+ }
+ view.trigger( renderedEventName );
+ });
+ });
+ return this;
+ },
+
+ //NOTE: button renderers have the side effect of caching their IconButtonViews to this view
+ // clear out cached sub-views, dom refs, etc. from prev. render
+ _clearReferences : function(){
+ //??TODO: we should reset these in the button logic checks (i.e. if not needed this.button = null; return null)
+ //?? do we really need these - not so far
+ //TODO: at least move these to a map
+ this.displayButton = null;
+ this.editButton = null;
+ this.deleteButton = null;
+ this.errButton = null;
+ this.showParamsButton = null;
+ this.rerunButton = null;
+ this.visualizationsButton = null;
+ this.tagButton = null;
+ this.annotateButton = null;
+ },
+
+ // ................................................................................ RENDER WARNINGS
+ // hda warnings including: is deleted, is purged, is hidden (including links to further actions (undelete, etc.))
+ _render_warnings : function(){
+ // jQ errs on building dom with whitespace - if there are no messages, trim -> ''
+ return $( jQuery.trim( HDAView.templates.messages(
+ _.extend( this.model.toJSON(), { urls: this.urls } )
+ )));
+ },
+
+ // ................................................................................ RENDER TITLEBAR
+ // the part of an hda always shown (whether the body is expanded or not): title link, title buttons
+ _render_titleBar : function(){
+ var titleBar = $( '<div class="historyItemTitleBar" style="overflow: hidden"></div>' );
+ titleBar.append( this._render_titleButtons() );
+ titleBar.append( '<span class="state-icon"></span>' );
+ titleBar.append( this._render_titleLink() );
+ return titleBar;
+ },
+
+ // ................................................................................ display, edit attr, delete
+ // icon-button group for the common, most easily accessed actions
+ //NOTE: these are generally displayed for almost every hda state (tho poss. disabled)
+ _render_titleButtons : function(){
+ // render the display, edit attr and delete icon-buttons
+ var buttonDiv = $( '<div class="historyItemButtons"></div>' );
+ buttonDiv.append( this._render_displayButton() );
+ buttonDiv.append( this._render_editButton() );
+ buttonDiv.append( this._render_deleteButton() );
+ return buttonDiv;
+ },
+
+ // icon-button to display this hda in the galaxy main iframe
+ _render_displayButton : function(){
+ // don't show display if not in ready state, error'd, or not accessible
+ if( ( !this.model.inReadyState() )
+ || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.ERROR )
+ || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
+ || ( !this.model.get( 'accessible' ) ) ){
+ return null;
+ }
+
+ var displayBtnData = {
+ icon_class : 'display'
+ };
+
+ // show a disabled display if the data's been purged
+ if( this.model.get( 'purged' ) ){
+ displayBtnData.enabled = false;
+ displayBtnData.title = 'Cannot display datasets removed from disk';
+
+ } else {
+ displayBtnData.title = 'Display data in browser';
+ displayBtnData.href = this.urls.display;
+ }
+
+ if( this.model.get( 'for_editing' ) ){
+ displayBtnData.target = 'galaxy_main';
+ }
+
+ this.displayButton = new IconButtonView({ model : new IconButton( displayBtnData ) });
+ return this.displayButton.render().$el;
+ },
+
+ // icon-button to edit the attributes (format, permissions, etc.) this hda
+ _render_editButton : function(){
+ // don't show edit while uploading, or if editable
+ if( ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.UPLOAD )
+ || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.ERROR )
+ || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
+ || ( !this.model.get( 'accessible' ) )
+ || ( !this.model.get( 'for_editing' ) ) ){
+ return null;
+ }
+
+ var purged = this.model.get( 'purged' ),
+ deleted = this.model.get( 'deleted' ),
+ editBtnData = {
+ title : 'Edit attributes',
+ href : this.urls.edit,
+ target : 'galaxy_main',
+ icon_class : 'edit'
+ };
+
+ // disable if purged or deleted and explain why in the tooltip
+ //TODO: if for_editing
+ if( deleted || purged ){
+ editBtnData.enabled = false;
+ if( purged ){
+ editBtnData.title = 'Cannot edit attributes of datasets removed from disk';
+ } else if( deleted ){
+ editBtnData.title = 'Undelete dataset to edit attributes';
+ }
+ }
+
+ this.editButton = new IconButtonView({ model : new IconButton( editBtnData ) });
+ return this.editButton.render().$el;
+ },
+
+ // icon-button to delete this hda
+ _render_deleteButton : function(){
+ // don't show delete if...
+ if( ( !this.model.get( 'for_editing' ) )
+ || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
+ || ( !this.model.get( 'accessible' ) ) ){
+ return null;
+ }
+
+ var deleteBtnData = {
+ title : 'Delete',
+ href : this.urls[ 'delete' ],
+ id : 'historyItemDeleter-' + this.model.get( 'id' ),
+ icon_class : 'delete'
+ };
+ if( this.model.get( 'deleted' ) || this.model.get( 'purged' ) ){
+ deleteBtnData = {
+ title : 'Dataset is already deleted',
+ icon_class : 'delete',
+ enabled : false
+ };
+ }
+ this.deleteButton = new IconButtonView({ model : new IconButton( deleteBtnData ) });
+ return this.deleteButton.render().$el;
+ },
+
+ // ................................................................................ titleLink
+ // render the hid and hda.name as a link (that will expand the body)
+ _render_titleLink : function(){
+ return $( jQuery.trim( HDAView.templates.titleLink(
+ _.extend( this.model.toJSON(), { urls: this.urls } )
+ )));
+ },
+
+ // ................................................................................ RENDER BODY
+ // render the data/metadata summary (format, size, misc info, etc.)
+ _render_hdaSummary : function(){
+ var modelData = _.extend( this.model.toJSON(), { urls: this.urls } );
+ // if there's no dbkey and it's editable : pass a flag to the template to render a link to editing in the '?'
+ if( this.model.get( 'metadata_dbkey' ) === '?'
+ && !this.model.isDeletedOrPurged() ){
+ _.extend( modelData, { dbkey_unknown_and_editable : true });
+ }
+ return HDAView.templates.hdaSummary( modelData );
+ },
+
+ // ................................................................................ primary actions
+ // render the icon-buttons gen. placed underneath the hda summary
+ _render_primaryActionButtons : function( buttonRenderingFuncs ){
+ var primaryActionButtons = $( '<div/>' ).attr( 'id', 'primary-actions-' + this.model.get( 'id' ) ),
+ view = this;
+ _.each( buttonRenderingFuncs, function( fn ){
+ primaryActionButtons.append( fn.call( view ) );
+ });
+ return primaryActionButtons;
+ },
+
+ // icon-button/popupmenu to down the data (and/or the associated meta files (bai, etc.)) for this hda
+ _render_downloadButton : function(){
+ // don't show anything if the data's been purged
+ if( this.model.get( 'purged' ) ){ return null; }
+
+ // return either: a single download icon-button (if there are no meta files)
+ // or a popupmenu with links to download assoc. meta files (if there are meta files)
+ var downloadLinkHTML = HDAView.templates.downloadLinks(
+ _.extend( this.model.toJSON(), { urls: this.urls } )
+ );
+ //this.log( this + '_render_downloadButton, downloadLinkHTML:', downloadLinkHTML );
+ return $( downloadLinkHTML );
+ },
+
+ // icon-button to show the input and output (stdout/err) for the job that created this hda
+ _render_errButton : function(){
+ if( ( this.model.get( 'state' ) !== HistoryDatasetAssociation.STATES.ERROR )
+ || ( !this.model.get( 'for_editing' ) ) ){ return null; }
+
+ this.errButton = new IconButtonView({ model : new IconButton({
+ title : 'View or report this error',
+ href : this.urls.report_error,
+ target : 'galaxy_main',
+ icon_class : 'bug'
+ })});
+ return this.errButton.render().$el;
+ },
+
+ // icon-button to show the input and output (stdout/err) for the job that created this hda
+ _render_showParamsButton : function(){
+ // gen. safe to show in all cases
+ this.showParamsButton = new IconButtonView({ model : new IconButton({
+ title : 'View details',
+ href : this.urls.show_params,
+ target : 'galaxy_main',
+ icon_class : 'information'
+ }) });
+ return this.showParamsButton.render().$el;
+ },
+
+ // icon-button to re run the job that created this hda
+ _render_rerunButton : function(){
+ if( !this.model.get( 'for_editing' ) ){ return null; }
+ this.rerunButton = new IconButtonView({ model : new IconButton({
+ title : 'Run this job again',
+ href : this.urls.rerun,
+ target : 'galaxy_main',
+ icon_class : 'arrow-circle'
+ }) });
+ return this.rerunButton.render().$el;
+ },
+
+ // build an icon-button or popupmenu based on the number of applicable visualizations
+ // also map button/popup clicks to viz setup functions
+ _render_visualizationsButton : function(){
+ var dbkey = this.model.get( 'dbkey' ),
+ visualizations = this.model.get( 'visualizations' ),
+ visualization_url = this.urls.visualization,
+ popup_menu_dict = {},
+ params = {
+ dataset_id: this.model.get( 'id' ),
+ hda_ldda: 'hda'
+ };
+
+ if( !( this.model.hasData() )
+ || !( this.model.get( 'for_editing' ) )
+ || !( visualizations && visualizations.length )
+ || !( visualization_url ) ){
+ //console.warn( 'NOT rendering visualization icon' )
+ return null;
+ }
+
+ // render the icon from template
+ this.visualizationsButton = new IconButtonView({ model : new IconButton({
+ title : 'Visualize',
+ href : visualization_url,
+ icon_class : 'chart_curve'
+ })});
+ var $icon = this.visualizationsButton.render().$el;
+ $icon.addClass( 'visualize-icon' ); // needed?
+
+ //TODO: make this more concise
+ // map a function to each visualization in the icon's attributes
+ // create a popupmenu from that map
+ // Add dbkey to params if it exists.
+ if( dbkey ){ params.dbkey = dbkey; }
+
+ function create_viz_action( visualization ) {
+ switch( visualization ){
+ case 'trackster':
+ return create_trackster_action_fn( visualization_url, params, dbkey );
+ case 'scatterplot':
+ return create_scatterplot_action_fn( visualization_url, params );
+ default:
+ return function(){
+ window.parent.location = visualization_url + '/' + visualization + '?' + $.param( params ); };
+ }
+ }
+
+ // No need for popup menu because there's a single visualization.
+ if ( visualizations.length === 1 ) {
+ $icon.attr( 'title', visualizations[0] );
+ $icon.click( create_viz_action( visualizations[0] ) );
+
+ // >1: Populate menu dict with visualization fns, make the popupmenu
+ } else {
+ _.each( visualizations, function( visualization ) {
+ //TODO: move to utils
+ var titleCaseVisualization = visualization.charAt( 0 ).toUpperCase() + visualization.slice( 1 );
+ popup_menu_dict[ titleCaseVisualization ] = create_viz_action( visualization );
+ });
+ make_popupmenu( $icon, popup_menu_dict );
+ }
+ return $icon;
+ },
+
+ // ................................................................................ secondary actions
+ // secondary actions: currently tagging and annotation (if user is allowed)
+ _render_secondaryActionButtons : function( buttonRenderingFuncs ){
+ // move to the right (same level as primary)
+ var secondaryActionButtons = $( '<div/>' ),
+ view = this;
+ secondaryActionButtons
+ .attr( 'style', 'float: right;' )
+ .attr( 'id', 'secondary-actions-' + this.model.get( 'id' ) );
+
+ _.each( buttonRenderingFuncs, function( fn ){
+ secondaryActionButtons.append( fn.call( view ) );
+ });
+ return secondaryActionButtons;
+ },
+
+ // icon-button to load and display tagging html
+ //TODO: these should be a sub-MV
+ _render_tagButton : function(){
+ if( !( this.model.hasData() )
+ || !( this.model.get( 'for_editing' ) )
+ || ( !this.urls.tags.get ) ){ return null; }
+
+ this.tagButton = new IconButtonView({ model : new IconButton({
+ title : 'Edit dataset tags',
+ target : 'galaxy_main',
+ href : this.urls.tags.get,
+ icon_class : 'tags'
+ })});
+ return this.tagButton.render().$el;
+ },
+
+ // icon-button to load and display annotation html
+ //TODO: these should be a sub-MV
+ _render_annotateButton : function(){
+ if( !( this.model.hasData() )
+ || !( this.model.get( 'for_editing' ) )
+ || ( !this.urls.annotation.get ) ){ return null; }
+
+ this.annotateButton = new IconButtonView({ model : new IconButton({
+ title : 'Edit dataset annotation',
+ target : 'galaxy_main',
+ icon_class : 'annotate'
+ })});
+ return this.annotateButton.render().$el;
+ },
+
+ // ................................................................................ other elements
+ // render links to external genome display applications (igb, gbrowse, etc.)
+ //TODO: not a fan of the style on these
+ _render_displayApps : function(){
+ if( !this.model.hasData() ){ return null; }
+
+ var displayAppsDiv = $( '<div/>' ).addClass( 'display-apps' );
+ if( !_.isEmpty( this.model.get( 'display_types' ) ) ){
+ //this.log( this + 'display_types:', this.model.get( 'urls' ).display_types );
+ //TODO:?? does this ever get used?
+ displayAppsDiv.append(
+ HDAView.templates.displayApps({ displayApps : this.model.get( 'display_types' ) })
+ );
+ }
+ if( !_.isEmpty( this.model.get( 'display_apps' ) ) ){
+ //this.log( this + 'display_apps:', this.model.get( 'urls' ).display_apps );
+ displayAppsDiv.append(
+ HDAView.templates.displayApps({ displayApps : this.model.get( 'display_apps' ) })
+ );
+ }
+ return displayAppsDiv;
+ },
+
+ //TODO: into sub-MV
+ // render the area used to load tag display
+ _render_tagArea : function(){
+ if( !this.urls.tags.set ){ return null; }
+ //TODO: move to mvc/tags.js
+ return $( HDAView.templates.tagArea(
+ _.extend( this.model.toJSON(), { urls: this.urls } )
+ ));
+ },
+
+ //TODO: into sub-MV
+ // render the area used to load annotation display
+ _render_annotationArea : function(){
+ if( !this.urls.annotation.get ){ return null; }
+ //TODO: move to mvc/annotations.js
+ return $( HDAView.templates.annotationArea(
+ _.extend( this.model.toJSON(), { urls: this.urls } )
+ ));
+ },
+
+ // render the data peek
+ //TODO: curr. pre-formatted into table on the server side - may not be ideal/flexible
+ _render_peek : function(){
+ if( !this.model.get( 'peek' ) ){ return null; }
+ return $( '<div/>' ).append(
+ $( '<pre/>' )
+ .attr( 'id', 'peek' + this.model.get( 'id' ) )
+ .addClass( 'peek' )
+ .append( this.model.get( 'peek' ) )
+ );
+ },
+
+ // ................................................................................ state body renderers
+ // _render_body fns for the various states
+ //TODO: only render these on expansion (or already expanded)
+ _render_body_not_viewable : function( parent ){
+ //TODO: revisit - still showing display, edit, delete (as common) - that CAN'T be right
+ parent.append( $( '<div>You do not have permission to view dataset.</div>' ) );
+ },
+
+ _render_body_uploading : function( parent ){
+ parent.append( $( '<div>Dataset is uploading</div>' ) );
+ },
+
+ _render_body_queued : function( parent ){
+ parent.append( $( '<div>Job is waiting to run.</div>' ) );
+ parent.append( this._render_primaryActionButtons([
+ this._render_showParamsButton,
+ this._render_rerunButton
+ ]));
+ },
+
+ _render_body_running : function( parent ){
+ parent.append( '<div>Job is currently running.</div>' );
+ parent.append( this._render_primaryActionButtons([
+ this._render_showParamsButton,
+ this._render_rerunButton
+ ]));
+ },
+
+ _render_body_error : function( parent ){
+ if( !this.model.get( 'purged' ) ){
+ parent.append( $( '<div>' + this.model.get( 'misc_blurb' ) + '</div>' ) );
+ }
+ parent.append( ( 'An error occurred running this job: '
+ + '<i>' + $.trim( this.model.get( 'misc_info' ) ) + '</i>' ) );
+ parent.append( this._render_primaryActionButtons([
+ this._render_downloadButton,
+ this._render_errButton,
+ this._render_showParamsButton,
+ this._render_rerunButton
+ ]));
+ },
+
+ _render_body_discarded : function( parent ){
+ parent.append( '<div>The job creating this dataset was cancelled before completion.</div>' );
+ parent.append( this._render_primaryActionButtons([
+ this._render_showParamsButton,
+ this._render_rerunButton
+ ]));
+ },
+
+ _render_body_setting_metadata : function( parent ){
+ parent.append( $( '<div>Metadata is being auto-detected.</div>' ) );
+ },
+
+ _render_body_empty : function( parent ){
+ //TODO: replace i with dataset-misc-info class
+ //?? why are we showing the file size when we know it's zero??
+ parent.append( $( '<div>No data: <i>' + this.model.get( 'misc_blurb' ) + '</i></div>' ) );
+ parent.append( this._render_primaryActionButtons([
+ this._render_showParamsButton,
+ this._render_rerunButton
+ ]));
+ },
+
+ _render_body_failed_metadata : function( parent ){
+ //TODO: the css for this box is broken (unlike the others)
+ // add a message box about the failure at the top of the body...
+ parent.append( $( HDAView.templates.failedMetadata( this.model.toJSON() ) ) );
+ //...then render the remaining body as STATES.OK (only diff between these states is the box above)
+ this._render_body_ok( parent );
+ },
+
+ _render_body_ok : function( parent ){
+ // most common state renderer and the most complicated
+ parent.append( this._render_hdaSummary() );
+
+ // return shortened form if del'd
+ //TODO: is this correct? maybe only on purged
+ if( this.model.isDeletedOrPurged() ){
+ parent.append( this._render_primaryActionButtons([
+ this._render_downloadButton,
+ this._render_showParamsButton,
+ this._render_rerunButton
+ ]));
+ return;
+ }
+
+ //NOTE: change the order here
+ parent.append( this._render_primaryActionButtons([
+ this._render_downloadButton,
+ this._render_errButton,
+ this._render_showParamsButton,
+ this._render_rerunButton,
+ this._render_visualizationsButton
+ ]));
+ parent.append( this._render_secondaryActionButtons([
+ this._render_tagButton,
+ this._render_annotateButton
+ ]));
+ parent.append( '<div class="clear"/>' );
+
+ parent.append( this._render_tagArea() );
+ parent.append( this._render_annotationArea() );
+
+ parent.append( this._render_displayApps() );
+ parent.append( this._render_peek() );
+
+ //TODO??: still needed?
+ //// If Mozilla, hide scrollbars in hidden items since they cause animation bugs
+ //if ( $.browser.mozilla ) {
+ // $( "div.historyItemBody" ).each( function() {
+ // if ( !$(this).is(":visible") ) { $(this).find( "pre.peek" ).css( "overflow", "hidden" ); }
+ // });
+ //}
+ },
+
+ _render_body : function(){
+ //this.log( this + '_render_body' );
+ //this.log( 'state:', state, 'for_editing', for_editing );
+
+ //TODO: incorrect id (encoded - use hid?)
+ var body = $( '<div/>' )
+ .attr( 'id', 'info-' + this.model.get( 'id' ) )
+ .addClass( 'historyItemBody' )
+ .attr( 'style', 'display: block' );
+
+ //TODO: not a fan of this dispatch
+ switch( this.model.get( 'state' ) ){
+ case HistoryDatasetAssociation.STATES.NOT_VIEWABLE :
+ this._render_body_not_viewable( body );
+ break;
+ case HistoryDatasetAssociation.STATES.UPLOAD :
+ this._render_body_uploading( body );
+ break;
+ case HistoryDatasetAssociation.STATES.QUEUED :
+ this._render_body_queued( body );
+ break;
+ case HistoryDatasetAssociation.STATES.RUNNING :
+ this._render_body_running( body );
+ break;
+ case HistoryDatasetAssociation.STATES.ERROR :
+ this._render_body_error( body );
+ break;
+ case HistoryDatasetAssociation.STATES.DISCARDED :
+ this._render_body_discarded( body );
+ break;
+ case HistoryDatasetAssociation.STATES.SETTING_METADATA :
+ this._render_body_setting_metadata( body );
+ break;
+ case HistoryDatasetAssociation.STATES.EMPTY :
+ this._render_body_empty( body );
+ break;
+ case HistoryDatasetAssociation.STATES.FAILED_METADATA :
+ this._render_body_failed_metadata( body );
+ break;
+ case HistoryDatasetAssociation.STATES.OK :
+ this._render_body_ok( body );
+ break;
+ default:
+ //??: no body?
+ body.append( $( '<div>Error: unknown dataset state "' + state + '".</div>' ) );
+ }
+ body.append( '<div style="clear: both"></div>' );
+
+ if( this.expanded ){
+ body.show();
+ } else {
+ body.hide();
+ }
+ return body;
+ },
+
+ // ................................................................................ EVENTS
+ events : {
+ 'click .historyItemTitle' : 'toggleBodyVisibility',
+ 'click a.icon-button.tags' : 'loadAndDisplayTags',
+ 'click a.icon-button.annotate' : 'loadAndDisplayAnnotation'
+ },
+
+ // ................................................................................ STATE CHANGES / MANIPULATION
+ // find the tag area and, if initial: (via ajax) load the html for displaying them; otherwise, unhide/hide
+ //TODO: into sub-MV
+ loadAndDisplayTags : function( event ){
+ //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' ),
+ tagElt = tagArea.find( '.tag-elt' );
+
+ // 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.
+ $.ajax({
+ //TODO: the html from this breaks a couple of times
+ url: this.urls.tags.get,
+ error: function() { alert( "Tagging failed" ); },
+ success: function(tag_elt_html) {
+ tagElt.html(tag_elt_html);
+ tagElt.find(".tooltip").tooltip();
+ tagArea.slideDown("fast");
+ }
+ });
+ } else {
+ // Tag element is filled; show.
+ tagArea.slideDown("fast");
+ }
+ } else {
+ // Hide.
+ tagArea.slideUp("fast");
+ }
+ return false;
+ },
+
+ // find the annotation area and, if initial: (via ajax) load the html for displaying it; otherwise, unhide/hide
+ //TODO: into sub-MV
+ loadAndDisplayAnnotation : function( event ){
+ //TODO: this is a drop in from history.mako - should use MV as well
+ this.log( this + '.loadAndDisplayAnnotation', event );
+ var annotationArea = this.$el.find( '.annotation-area' ),
+ annotationElem = annotationArea.find( '.annotation-elt' ),
+ setAnnotationUrl = this.urls.annotation.set;
+
+ // Show or hide annotation area; if showing annotation area and it's empty, fill it.
+ if ( annotationArea.is( ":hidden" ) ){
+ if( !jQuery.trim( annotationElem.html() ) ){
+ // Need to fill annotation element.
+ $.ajax({
+ url: this.urls.annotation.get,
+ error: function(){ alert( "Annotations failed" ); },
+ success: function( htmlFromAjax ){
+ if( htmlFromAjax === "" ){
+ htmlFromAjax = "<em>Describe or add notes to dataset</em>";
+ }
+ annotationElem.html( htmlFromAjax );
+ annotationArea.find(".tooltip").tooltip();
+
+ async_save_text(
+ annotationElem.attr("id"), annotationElem.attr("id"),
+ setAnnotationUrl,
+ "new_annotation", 18, true, 4
+ );
+ annotationArea.slideDown("fast");
+ }
+ });
+ } else {
+ annotationArea.slideDown("fast");
+ }
+
+ } else {
+ // Hide.
+ annotationArea.slideUp("fast");
+ }
+ return false;
+ },
+
+ // expand/collapse body
+ //side effect: trigger event
+ toggleBodyVisibility : function( event, expanded ){
+ var $body = this.$el.find( '.historyItemBody' );
+ expanded = ( expanded === undefined )?( !$body.is( ':visible' ) ):( expanded );
+ //this.log( 'toggleBodyVisibility, expanded:', expanded, '$body:', $body );
+
+ if( expanded ){
+ $body.slideDown( 'fast' );
+ } else {
+ $body.slideUp( 'fast' );
+ }
+ this.trigger( 'toggleBodyVisibility', this.model.get( 'id' ), expanded );
+ },
+
+ // ................................................................................ UTILTIY
+ toString : function(){
+ var modelString = ( this.model )?( this.model + '' ):( '(no model)' );
+ return 'HDAView(' + modelString + ')';
+ }
+});
+
+//------------------------------------------------------------------------------
+HDAView.templates = {
+ warningMsg : Handlebars.templates[ 'template-warningmessagesmall' ],
+
+ messages : Handlebars.templates[ 'template-history-warning-messages' ],
+ titleLink : Handlebars.templates[ 'template-history-titleLink' ],
+ hdaSummary : Handlebars.templates[ 'template-history-hdaSummary' ],
+ downloadLinks : Handlebars.templates[ 'template-history-downloadLinks' ],
+ failedMetadata : Handlebars.templates[ 'template-history-failedMetaData' ],
+ tagArea : Handlebars.templates[ 'template-history-tagArea' ],
+ annotationArea : Handlebars.templates[ 'template-history-annotationArea' ],
+ displayApps : Handlebars.templates[ 'template-history-displayApps' ]
+};
+
+//==============================================================================
+//TODO: these belong somewhere else
+
+//TODO: should be imported from scatterplot.js
+//TODO: OR abstracted to 'load this in the galaxy_main frame'
+function create_scatterplot_action_fn( url, params ){
+ action = function() {
+ var galaxy_main = $( window.parent.document ).find( 'iframe#galaxy_main' ),
+ final_url = url + '/scatterplot?' + $.param(params);
+ galaxy_main.attr( 'src', final_url );
+ //TODO: this needs to go away
+ $( 'div.popmenu-wrapper' ).remove();
+ return false;
+ };
+ return action;
+}
+
+// -----------------------------------------------------------------------------
+// Create trackster action function.
+//TODO: should be imported from trackster.js
+function create_trackster_action_fn(vis_url, dataset_params, dbkey) {
+ return function() {
+ var params = {};
+ if (dbkey) { params.dbkey = dbkey; }
+ $.ajax({
+ url: vis_url + '/list_tracks?f-' + $.param(params),
+ dataType: "html",
+ error: function() { alert( "Could not add this dataset to browser." ); },
+ success: function(table_html) {
+ var parent = window.parent;
+
+ parent.show_modal("View Data in a New or Saved Visualization", "", {
+ "Cancel": function() {
+ parent.hide_modal();
+ },
+ "View in saved visualization": function() {
+ // Show new modal with saved visualizations.
+ parent.show_modal("Add Data to Saved Visualization", table_html, {
+ "Cancel": function() {
+ parent.hide_modal();
+ },
+ "Add to visualization": function() {
+ $(parent.document).find('input[name=id]:checked').each(function() {
+ var vis_id = $(this).val();
+ dataset_params.id = vis_id;
+ parent.location = vis_url + "/trackster?" + $.param(dataset_params);
+ });
+ }
+ });
+ },
+ "View in new visualization": function() {
+ parent.location = vis_url + "/trackster?" + $.param(dataset_params);
+ }
+ });
+ }
+ });
+ return false;
+ };
+}
+
+//==============================================================================
+//return {
+// HDAView : HDAView,
+//};});
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 static/scripts/mvc/dataset/hda-model.js
--- /dev/null
+++ b/static/scripts/mvc/dataset/hda-model.js
@@ -0,0 +1,223 @@
+//define([
+// "../mvc/base-mvc"
+//], function(){
+//==============================================================================
+/**
+ *
+ */
+var HistoryDatasetAssociation = BaseModel.extend( LoggableMixin ).extend({
+ // a single HDA model
+
+ // uncomment this out see log messages
+ //logger : console,
+
+ defaults : {
+ // ---these are part of an HDA proper:
+
+ // parent (containing) history
+ history_id : null,
+ // often used with tagging
+ model_class : 'HistoryDatasetAssociation',
+ // index within history (??)
+ hid : 0,
+
+ // ---whereas these are Dataset related/inherited
+
+ id : null,
+ name : '',
+ // one of HistoryDatasetAssociation.STATES
+ state : '',
+ // sniffed datatype (sam, tabular, bed, etc.)
+ data_type : null,
+ // size in bytes
+ file_size : 0,
+
+ // array of associated file types (eg. [ 'bam_index', ... ])
+ meta_files : [],
+
+ misc_blurb : '',
+ misc_info : '',
+
+ deleted : false,
+ purged : false,
+ // aka. !hidden
+ visible : false,
+ // based on trans.user (is_admin or security_agent.can_access_dataset( <user_roles>, hda.dataset ))
+ accessible : false,
+
+ //TODO: this needs to be removed (it is a function of the view type (e.g. HDAForEditingView))
+ for_editing : true
+ },
+
+ // fetch location of this history in the api
+ url : function(){
+ //TODO: get this via url router
+ return 'api/histories/' + this.get( 'history_id' ) + '/contents/' + this.get( 'id' );
+ },
+
+ // (curr) only handles changing state of non-accessible hdas to STATES.NOT_VIEWABLE
+ //TODO:? use initialize (or validate) to check purged AND deleted -> purged XOR deleted
+ initialize : function(){
+ this.log( this + '.initialize', this.attributes );
+ this.log( '\tparent history_id: ' + this.get( 'history_id' ) );
+
+ //!! this state is not in trans.app.model.Dataset.states - set it here -
+ //TODO: change to server side.
+ if( !this.get( 'accessible' ) ){
+ this.set( 'state', HistoryDatasetAssociation.STATES.NOT_VIEWABLE );
+ }
+
+ // if the state has changed and the new state is a ready state, fire an event
+ this.on( 'change:state', function( currModel, newState ){
+ this.log( this + ' has changed state:', currModel, newState );
+ if( this.inReadyState() ){
+ this.trigger( 'state:ready', this.get( 'id' ), newState, this.previous( 'state' ), currModel );
+ }
+ });
+
+ // debug on change events
+ //this.on( 'change', function( currModel, changedList ){
+ // this.log( this + ' has changed:', currModel, changedList );
+ //});
+ //this.bind( 'all', function( event ){
+ // this.log( this + '', arguments );
+ //});
+ },
+
+ isDeletedOrPurged : function(){
+ return ( this.get( 'deleted' ) || this.get( 'purged' ) );
+ },
+
+ // based on show_deleted, show_hidden (gen. from the container control), would this ds show in the list of ds's?
+ //TODO: too many visibles
+ isVisible : function( show_deleted, show_hidden ){
+ var isVisible = true;
+ if( ( !show_deleted )
+ && ( this.get( 'deleted' ) || this.get( 'purged' ) ) ){
+ isVisible = false;
+ }
+ if( ( !show_hidden )
+ && ( !this.get( 'visible' ) ) ){
+ isVisible = false;
+ }
+ return isVisible;
+ },
+
+ // 'ready' states are states where no processing (for the ds) is left to do on the server
+ inReadyState : function(){
+ var state = this.get( 'state' );
+ return (
+ ( state === HistoryDatasetAssociation.STATES.NEW )
+ || ( state === HistoryDatasetAssociation.STATES.OK )
+ || ( state === HistoryDatasetAssociation.STATES.EMPTY )
+ || ( state === HistoryDatasetAssociation.STATES.FAILED_METADATA )
+ || ( state === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
+ || ( state === HistoryDatasetAssociation.STATES.DISCARDED )
+ || ( state === HistoryDatasetAssociation.STATES.ERROR )
+ );
+ },
+
+ // convenience fn to match hda.has_data
+ hasData : function(){
+ //TODO:?? is this equivalent to all possible hda.has_data calls?
+ return ( this.get( 'file_size' ) > 0 );
+ },
+
+ toString : function(){
+ var nameAndId = this.get( 'id' ) || '';
+ if( this.get( 'name' ) ){
+ nameAndId += ':"' + this.get( 'name' ) + '"';
+ }
+ return 'HistoryDatasetAssociation(' + nameAndId + ')';
+ }
+});
+
+//------------------------------------------------------------------------------
+HistoryDatasetAssociation.STATES = {
+ UPLOAD : 'upload',
+ QUEUED : 'queued',
+ RUNNING : 'running',
+ SETTING_METADATA : 'setting_metadata',
+
+ NEW : 'new',
+ OK : 'ok',
+ EMPTY : 'empty',
+
+ FAILED_METADATA : 'failed_metadata',
+ NOT_VIEWABLE : 'noPermission', // not in trans.app.model.Dataset.states
+ DISCARDED : 'discarded',
+ ERROR : 'error'
+};
+
+//==============================================================================
+/**
+ *
+ */
+var HDACollection = Backbone.Collection.extend( LoggableMixin ).extend({
+ model : HistoryDatasetAssociation,
+
+ //logger : console,
+
+ initialize : function(){
+ //this.bind( 'all', function( event ){
+ // this.log( this + '', arguments );
+ //});
+ },
+
+ // return the ids of every hda in this collection
+ ids : function(){
+ return this.map( function( item ){ return item.id; });
+ },
+
+ // return an HDA collection containing every 'shown' hda based on show_deleted/hidden
+ getVisible : function( show_deleted, show_hidden ){
+ return this.filter( function( item ){ return item.isVisible( show_deleted, show_hidden ); });
+ },
+
+ // get a map where <possible hda state> : [ <list of hda ids in that state> ]
+ getStateLists : function(){
+ var stateLists = {};
+ _.each( _.values( HistoryDatasetAssociation.STATES ), function( state ){
+ stateLists[ state ] = [];
+ });
+ //NOTE: will err on unknown state
+ this.each( function( item ){
+ stateLists[ item.get( 'state' ) ].push( item.get( 'id' ) );
+ });
+ return stateLists;
+ },
+
+ // returns the id of every hda still running (not in a ready state)
+ running : function(){
+ var idList = [];
+ this.each( function( item ){
+ if( !item.inReadyState() ){
+ idList.push( item.get( 'id' ) );
+ }
+ });
+ return idList;
+ },
+
+ // update (fetch -> render) the hdas with the ids given
+ update : function( ids ){
+ this.log( this + 'update:', ids );
+
+ if( !( ids && ids.length ) ){ return; }
+
+ var collection = this;
+ _.each( ids, function( id, index ){
+ var historyItem = collection.get( id );
+ historyItem.fetch();
+ });
+ },
+
+ toString : function(){
+ return ( 'HDACollection(' + this.ids().join(',') + ')' );
+ }
+});
+
+//==============================================================================
+//return {
+// HistoryDatasetAssociation : HistoryDatasetAssociation,
+// HDACollection : HDACollection,
+//};});
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 static/scripts/mvc/history.js
--- a/static/scripts/mvc/history.js
+++ /dev/null
@@ -1,1611 +0,0 @@
-//define([
-// "../mvc/base-mvc"
-//
-//], function(){
-/* =============================================================================
-Backbone.js implementation of history panel
-
-TODO:
- bug:
- anon, mako:
- tooltips not rendered
- anno, tags rendered
- title editable
- bug:
- when over quota history is re-rendered, over quota msg is not displayed
- bc the quota:over event isn't fired
- bc the user state hasn't changed
-
- anon user, mako template init:
- bug: rename url seems to be wrong url
-
- currently, adding a dataset (via tool execute, etc.) creates a new dataset and refreshes the page
- logged in, mako template:
- BUG: am able to start upload even if over quota - 'runs' forever
-
- BUG: from above sit, delete uploading hda - now in state 'discarded'! ...new state to handle
- bug: quotaMeter bar rendering square in chrome
- BUG: quotaMsg not showing when 100% (on load)
- BUG: upload, history size, doesn't change
- TODO: on hdas state:ready, update ONLY the size...from what? histories.py? in js?
- BUG: imported, shared history with unaccessible dataset errs in historycontents when getting history
- (entire history is inaccessible)
- ??: still happening?
-
- from loadFromApi:
- BUG: not showing previous annotations
-
- fixed:
- BUG: historyItem, error'd ds show display, download?
- FIXED: removed
- bug: loading hdas (alt_hist)
- FIXED: added anon user api request ( trans.user == None and trans.history.id == requested id )
- bug: quota meter not updating on upload/tool run
- FIXED: quotaMeter now listens for 'state:ready' from glx_history in alternate_history.mako
- bug: use of new HDACollection with event listener in init doesn't die...keeps reporting
- FIXED: change getVisible to return an array
- BUG: history, broken intial hist state (running, updater, etc.)
- ??: doesn't seem to happen anymore
- BUG: collapse all should remove all expanded from storage
- FIXED: hideAllItemBodies now resets storage.expandedItems
- BUG: historyItem, shouldn't allow tag, annotate, peek on purged datasets
- FIXED: ok state now shows only: info, rerun
- BUG: history?, some ids aren't returning encoded...
- FIXED:???
- BUG: history, showing deleted ds
- FIXED
- UGH: historyItems have to be decorated with history_ids (api/histories/:history_id/contents/:id)
- FIXED by adding history_id to history_contents.show
- BUG: history, if hist has err'd ds, hist has perm state 'error', updater on following ds's doesn't run
- FIXED by reordering history state from ds' states here and histories
- BUG: history, broken annotation on reload (can't get thru api (sets fine, tho))
- FIXED: get thru api for now
-
- to relational model?
- HDACollection, meta_files, display_apps, etc.
-
-
- quota mgr
- show_deleted/hidden:
- use storage
- on/off ui
- move histview fadein/out in render to app?
- don't draw body until it's first expand event
- localize all
- break this file up
- ?: render url templates on init or render?
- ?: history, annotation won't accept unicode
-
- hierarchy:
- dataset -> hda
- history -> historyForEditing, historyForViewing
- display_structured?
-
- btw: get an error'd ds by running fastqc on fastq (when you don't have it installed)
-
- meta:
- css/html class/id 'item' -> hda
- add classes, ids on empty divs
- events (local/ui and otherwise)
- list in docs as well
- require.js
- convert function comments to jsDoc style, complete comments
- move inline styles into base.less
- watch the magic strings
- watch your globals
-
- features:
- lineage
- hide button
- show permissions in info
- show shared/sharing status on ds, history
- maintain scroll position on refresh (storage?)
- selection, multi-select (and actions common to selected (ugh))
- searching
- sorting, re-shuffling
-
-============================================================================= */
-var HistoryDatasetAssociation = BaseModel.extend( LoggableMixin ).extend({
- // a single HDA model
-
- // uncomment this out see log messages
- //logger : console,
-
- defaults : {
- // ---these are part of an HDA proper:
-
- // parent (containing) history
- history_id : null,
- // often used with tagging
- model_class : 'HistoryDatasetAssociation',
- // index within history (??)
- hid : 0,
-
- // ---whereas these are Dataset related/inherited
-
- id : null,
- name : '',
- // one of HistoryDatasetAssociation.STATES
- state : '',
- // sniffed datatype (sam, tabular, bed, etc.)
- data_type : null,
- // size in bytes
- file_size : 0,
-
- // array of associated file types (eg. [ 'bam_index', ... ])
- meta_files : [],
-
- misc_blurb : '',
- misc_info : '',
-
- deleted : false,
- purged : false,
- // aka. !hidden
- visible : false,
- // based on trans.user (is_admin or security_agent.can_access_dataset( <user_roles>, hda.dataset ))
- accessible : false,
-
- //TODO: this needs to be removed (it is a function of the view type (e.g. HDAForEditingView))
- for_editing : true
- },
-
- // fetch location of this history in the api
- url : function(){
- //TODO: get this via url router
- return 'api/histories/' + this.get( 'history_id' ) + '/contents/' + this.get( 'id' );
- },
-
- // (curr) only handles changing state of non-accessible hdas to STATES.NOT_VIEWABLE
- //TODO:? use initialize (or validate) to check purged AND deleted -> purged XOR deleted
- initialize : function(){
- this.log( this + '.initialize', this.attributes );
- this.log( '\tparent history_id: ' + this.get( 'history_id' ) );
-
- //!! this state is not in trans.app.model.Dataset.states - set it here -
- //TODO: change to server side.
- if( !this.get( 'accessible' ) ){
- this.set( 'state', HistoryDatasetAssociation.STATES.NOT_VIEWABLE );
- }
-
- // if the state has changed and the new state is a ready state, fire an event
- this.on( 'change:state', function( currModel, newState ){
- this.log( this + ' has changed state:', currModel, newState );
- if( this.inReadyState() ){
- this.trigger( 'state:ready', this.get( 'id' ), newState, this.previous( 'state' ), currModel );
- }
- });
-
- // debug on change events
- //this.on( 'change', function( currModel, changedList ){
- // this.log( this + ' has changed:', currModel, changedList );
- //});
- //this.bind( 'all', function( event ){
- // this.log( this + '', arguments );
- //});
- },
-
- isDeletedOrPurged : function(){
- return ( this.get( 'deleted' ) || this.get( 'purged' ) );
- },
-
- // based on show_deleted, show_hidden (gen. from the container control), would this ds show in the list of ds's?
- //TODO: too many visibles
- isVisible : function( show_deleted, show_hidden ){
- var isVisible = true;
- if( ( !show_deleted )
- && ( this.get( 'deleted' ) || this.get( 'purged' ) ) ){
- isVisible = false;
- }
- if( ( !show_hidden )
- && ( !this.get( 'visible' ) ) ){
- isVisible = false;
- }
- return isVisible;
- },
-
- // 'ready' states are states where no processing (for the ds) is left to do on the server
- inReadyState : function(){
- var state = this.get( 'state' );
- return (
- ( state === HistoryDatasetAssociation.STATES.NEW )
- || ( state === HistoryDatasetAssociation.STATES.OK )
- || ( state === HistoryDatasetAssociation.STATES.EMPTY )
- || ( state === HistoryDatasetAssociation.STATES.FAILED_METADATA )
- || ( state === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
- || ( state === HistoryDatasetAssociation.STATES.DISCARDED )
- || ( state === HistoryDatasetAssociation.STATES.ERROR )
- );
- },
-
- // convenience fn to match hda.has_data
- hasData : function(){
- //TODO:?? is this equivalent to all possible hda.has_data calls?
- return ( this.get( 'file_size' ) > 0 );
- },
-
- toString : function(){
- var nameAndId = this.get( 'id' ) || '';
- if( this.get( 'name' ) ){
- nameAndId += ':"' + this.get( 'name' ) + '"';
- }
- return 'HistoryDatasetAssociation(' + nameAndId + ')';
- }
-});
-
-//------------------------------------------------------------------------------
-HistoryDatasetAssociation.STATES = {
- UPLOAD : 'upload',
- QUEUED : 'queued',
- RUNNING : 'running',
- SETTING_METADATA : 'setting_metadata',
-
- NEW : 'new',
- OK : 'ok',
- EMPTY : 'empty',
-
- FAILED_METADATA : 'failed_metadata',
- NOT_VIEWABLE : 'noPermission', // not in trans.app.model.Dataset.states
- DISCARDED : 'discarded',
- ERROR : 'error'
-};
-
-//==============================================================================
-var HDACollection = Backbone.Collection.extend( LoggableMixin ).extend({
- model : HistoryDatasetAssociation,
-
- //logger : console,
-
- initialize : function(){
- //this.bind( 'all', function( event ){
- // this.log( this + '', arguments );
- //});
- },
-
- // return the ids of every hda in this collection
- ids : function(){
- return this.map( function( item ){ return item.id; });
- },
-
- // return an HDA collection containing every 'shown' hda based on show_deleted/hidden
- getVisible : function( show_deleted, show_hidden ){
- return this.filter( function( item ){ return item.isVisible( show_deleted, show_hidden ); });
- },
-
- // get a map where <possible hda state> : [ <list of hda ids in that state> ]
- getStateLists : function(){
- var stateLists = {};
- _.each( _.values( HistoryDatasetAssociation.STATES ), function( state ){
- stateLists[ state ] = [];
- });
- //NOTE: will err on unknown state
- this.each( function( item ){
- stateLists[ item.get( 'state' ) ].push( item.get( 'id' ) );
- });
- return stateLists;
- },
-
- // returns the id of every hda still running (not in a ready state)
- running : function(){
- var idList = [];
- this.each( function( item ){
- if( !item.inReadyState() ){
- idList.push( item.get( 'id' ) );
- }
- });
- return idList;
- },
-
- // update (fetch -> render) the hdas with the ids given
- update : function( ids ){
- this.log( this + 'update:', ids );
-
- if( !( ids && ids.length ) ){ return; }
-
- var collection = this;
- _.each( ids, function( id, index ){
- var historyItem = collection.get( id );
- historyItem.fetch();
- });
- },
-
- toString : function(){
- return ( 'HDACollection(' + this.ids().join(',') + ')' );
- }
-});
-
-
-//==============================================================================
-var History = BaseModel.extend( LoggableMixin ).extend({
- //TODO: bind change events from items and collection to this (itemLengths, states)
-
- // uncomment this out see log messages
- //logger : console,
-
- // values from api (may need more)
- defaults : {
- id : '',
- name : '',
- state : '',
-
- //TODO: wire these to items (or this)
- show_deleted : false,
- show_hidden : false,
-
- diskSize : 0,
- deleted : false,
-
- tags : [],
- annotation : null,
-
- //TODO: quota msg and message? how to get those over the api?
- message : null,
- quotaMsg : false
- },
-
- url : function(){
- // api location of history resource
- //TODO: hardcoded
- return 'api/histories/' + this.get( 'id' );
- },
-
- initialize : function( initialSettings, initialHdas ){
- this.log( this + ".initialize:", initialSettings, initialHdas );
-
- 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();
- }
-
- //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)
- loadFromApi : function( historyId, callback ){
- 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 ){
- //console.warn( 'fetched user, history: ', userResponse, historyResponse );
- history.attributes.user = userResponse[0]; //? meh.
- history.log( history );
-
- }).then( function(){
- // ...then the hdas (using contents?ids=...)
- jQuery.ajax( history.url() + '/contents?' + jQuery.param({
- ids : history.itemIdsFromStateIds().join( ',' )
-
- // reset the collection to the hdas returned
- })).success( function( hdas ){
- //console.warn( 'fetched hdas' );
- history.hdas.reset( hdas );
- history.checkForUpdates();
- callback();
- });
- });
- },
-
- // reduce the state_ids map of hda id lists -> a single list of ids
- //...ugh - seems roundabout; necessary because the history doesn't have a straightforward list of ids
- // (and history_contents/index curr returns a summary only)
- hdaIdsFromStateIds : function(){
- return _.reduce( _.values( this.get( 'state_ids' ) ), function( reduction, currIdList ){
- return reduction.concat( currIdList );
- });
- },
-
- // get the history's state from it's cummulative ds states, delay + update if needed
- checkForUpdates : function( datasets ){
- // 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 ){
- this.stateUpdater();
- }
- return this;
- },
-
- // update this history, find any hda's running/queued, update ONLY those that have changed states,
- // set up to run this again in some interval of time
- stateUpdater : function(){
- var history = this,
- oldState = this.get( 'state' ),
- // state ids is a map of every possible hda state, each containing a list of ids for hdas in that state
- oldStateIds = this.get( 'state_ids' );
-
- // pull from the history api
- //TODO: fetch?
- jQuery.ajax( 'api/histories/' + this.get( 'id' )
-
- ).success( function( response ){
- //this.log( 'historyApiRequest, response:', response );
- history.set( response );
- history.log( 'current history state:', history.get( 'state' ),
- '(was)', oldState,
- 'new size:', history.get( 'nice_size' ) );
-
- //TODO: revisit this - seems too elaborate, need something straightforward
- // for each state, check for the difference between old dataset states and new
- // the goal here is to check ONLY those datasets that have changed states (not all datasets)
- var changedIds = [];
- _.each( _.keys( response.state_ids ), function( state ){
- var diffIds = _.difference( response.state_ids[ state ], oldStateIds[ state ] );
- // aggregate those changed ids
- changedIds = changedIds.concat( diffIds );
- });
-
- // send the changed ids (if any) to dataset collection to have them fetch their own model changes
- if( changedIds.length ){
- history.hdas.update( changedIds );
- }
-
- // 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(){
- history.stateUpdater();
- }, 4000 );
- }
-
- }).error( function( xhr, status, error ){
- if( console && console.warn ){
- console.warn( 'Error getting history updates from the server:', xhr, status, error );
- }
- alert( 'Error getting history updates from the server.\n' + error );
- });
- },
-
- toString : function(){
- var nameString = ( this.get( 'name' ) )?
- ( ',' + this.get( 'name' ) ) : ( '' );
- return 'History(' + this.get( 'id' ) + nameString + ')';
- }
-});
-
-//==============================================================================
-var HDAView = BaseView.extend( LoggableMixin ).extend({
- //??TODO: add alias in initialize this.hda = this.model?
- // view for HistoryDatasetAssociation model above
-
- // uncomment this out see log messages
- //logger : console,
-
- tagName : "div",
- className : "historyItemContainer",
-
- // ................................................................................ SET UP
- initialize : function( attributes ){
- this.log( this + '.initialize:', attributes );
-
- // render urlTemplates (gen. provided by GalaxyPaths) to urls
- if( !attributes.urlTemplates ){ throw( 'HDAView needs urlTemplates on initialize' ); }
- this.urls = this.renderUrls( attributes.urlTemplates, this.model.toJSON() );
-
- // whether the body of this hda is expanded (shown)
- this.expanded = attributes.expanded || false;
-
- // re-render the entire view on any model change
- this.model.bind( 'change', this.render, this );
- //this.bind( 'all', function( event ){
- // this.log( event );
- //}, this );
- },
-
- // urlTemplates is a map (or nested map) of underscore templates (currently, anyhoo)
- // use the templates to create the apropo urls for each action this ds could use
- renderUrls : function( urlTemplates, modelJson ){
- var hdaView = this,
- urls = {};
- _.each( urlTemplates, function( urlTemplateOrObj, urlKey ){
- // object == nested templates: recurse
- if( _.isObject( urlTemplateOrObj ) ){
- urls[ urlKey ] = hdaView.renderUrls( urlTemplateOrObj, modelJson );
-
- // string == template:
- } else {
- // meta_down load is a special case (see renderMetaDownloadUrls)
- //TODO: should be a better (gen.) way to handle this case
- if( urlKey === 'meta_download' ){
- urls[ urlKey ] = hdaView.renderMetaDownloadUrls( urlTemplateOrObj, modelJson );
- } else {
- urls[ urlKey ] = _.template( urlTemplateOrObj, modelJson );
- }
- }
- });
- return urls;
- },
-
- // there can be more than one meta_file to download, so return a list of url and file_type for each
- renderMetaDownloadUrls : function( urlTemplate, modelJson ){
- return _.map( modelJson.meta_files, function( meta_file ){
- return {
- url : _.template( urlTemplate, { id: modelJson.id, file_type: meta_file.file_type }),
- file_type : meta_file.file_type
- };
- });
- },
-
- // ................................................................................ RENDER MAIN
- render : function(){
- var view = this,
- id = this.model.get( 'id' ),
- state = this.model.get( 'state' ),
- itemWrapper = $( '<div/>' ).attr( 'id', 'historyItem-' + id ),
- initialRender = ( this.$el.children().size() === 0 );
-
- //console.debug( this + '.render, initial?:', initialRender );
-
- this._clearReferences();
- this.$el.attr( 'id', 'historyItemContainer-' + id );
-
- itemWrapper
- .addClass( 'historyItemWrapper' ).addClass( 'historyItem' )
- .addClass( 'historyItem-' + state );
-
- itemWrapper.append( this._render_warnings() );
- itemWrapper.append( this._render_titleBar() );
- this.body = $( this._render_body() );
- itemWrapper.append( this.body );
-
- //TODO: move to own function: setUpBehaviours
- // we can potentially skip this step and call popupmenu directly on the download button
- make_popup_menus( itemWrapper );
-
- // set up canned behavior on children (bootstrap, popupmenus, editable_text, etc.)
- itemWrapper.find( '.tooltip' ).tooltip({ placement : 'bottom' });
-
- // transition...
- this.$el.fadeOut( 'fast', function(){
- view.$el.children().remove();
- view.$el.append( itemWrapper ).fadeIn( 'fast', function(){
- view.log( view + ' rendered:', view.$el );
- var renderedEventName = 'rendered';
-
- if( initialRender ){
- renderedEventName += ':initial';
- } else if( view.model.inReadyState() ){
- renderedEventName += ':ready';
- }
- view.trigger( renderedEventName );
- });
- });
- return this;
- },
-
- //NOTE: button renderers have the side effect of caching their IconButtonViews to this view
- // clear out cached sub-views, dom refs, etc. from prev. render
- _clearReferences : function(){
- //??TODO: we should reset these in the button logic checks (i.e. if not needed this.button = null; return null)
- //?? do we really need these - not so far
- //TODO: at least move these to a map
- this.displayButton = null;
- this.editButton = null;
- this.deleteButton = null;
- this.errButton = null;
- this.showParamsButton = null;
- this.rerunButton = null;
- this.visualizationsButton = null;
- this.tagButton = null;
- this.annotateButton = null;
- },
-
- // ................................................................................ RENDER WARNINGS
- // hda warnings including: is deleted, is purged, is hidden (including links to further actions (undelete, etc.))
- _render_warnings : function(){
- // jQ errs on building dom with whitespace - if there are no messages, trim -> ''
- return $( jQuery.trim( HDAView.templates.messages(
- _.extend( this.model.toJSON(), { urls: this.urls } )
- )));
- },
-
- // ................................................................................ RENDER TITLEBAR
- // the part of an hda always shown (whether the body is expanded or not): title link, title buttons
- _render_titleBar : function(){
- var titleBar = $( '<div class="historyItemTitleBar" style="overflow: hidden"></div>' );
- titleBar.append( this._render_titleButtons() );
- titleBar.append( '<span class="state-icon"></span>' );
- titleBar.append( this._render_titleLink() );
- return titleBar;
- },
-
- // ................................................................................ display, edit attr, delete
- // icon-button group for the common, most easily accessed actions
- //NOTE: these are generally displayed for almost every hda state (tho poss. disabled)
- _render_titleButtons : function(){
- // render the display, edit attr and delete icon-buttons
- var buttonDiv = $( '<div class="historyItemButtons"></div>' );
- buttonDiv.append( this._render_displayButton() );
- buttonDiv.append( this._render_editButton() );
- buttonDiv.append( this._render_deleteButton() );
- return buttonDiv;
- },
-
- // icon-button to display this hda in the galaxy main iframe
- _render_displayButton : function(){
- // don't show display if not in ready state, error'd, or not accessible
- if( ( !this.model.inReadyState() )
- || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.ERROR )
- || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
- || ( !this.model.get( 'accessible' ) ) ){
- return null;
- }
-
- var displayBtnData = {
- icon_class : 'display'
- };
-
- // show a disabled display if the data's been purged
- if( this.model.get( 'purged' ) ){
- displayBtnData.enabled = false;
- displayBtnData.title = 'Cannot display datasets removed from disk';
-
- } else {
- displayBtnData.title = 'Display data in browser';
- displayBtnData.href = this.urls.display;
- }
-
- if( this.model.get( 'for_editing' ) ){
- displayBtnData.target = 'galaxy_main';
- }
-
- this.displayButton = new IconButtonView({ model : new IconButton( displayBtnData ) });
- return this.displayButton.render().$el;
- },
-
- // icon-button to edit the attributes (format, permissions, etc.) this hda
- _render_editButton : function(){
- // don't show edit while uploading, or if editable
- if( ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.UPLOAD )
- || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.ERROR )
- || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
- || ( !this.model.get( 'accessible' ) )
- || ( !this.model.get( 'for_editing' ) ) ){
- return null;
- }
-
- var purged = this.model.get( 'purged' ),
- deleted = this.model.get( 'deleted' ),
- editBtnData = {
- title : 'Edit attributes',
- href : this.urls.edit,
- target : 'galaxy_main',
- icon_class : 'edit'
- };
-
- // disable if purged or deleted and explain why in the tooltip
- //TODO: if for_editing
- if( deleted || purged ){
- editBtnData.enabled = false;
- if( purged ){
- editBtnData.title = 'Cannot edit attributes of datasets removed from disk';
- } else if( deleted ){
- editBtnData.title = 'Undelete dataset to edit attributes';
- }
- }
-
- this.editButton = new IconButtonView({ model : new IconButton( editBtnData ) });
- return this.editButton.render().$el;
- },
-
- // icon-button to delete this hda
- _render_deleteButton : function(){
- // don't show delete if...
- if( ( !this.model.get( 'for_editing' ) )
- || ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
- || ( !this.model.get( 'accessible' ) ) ){
- return null;
- }
-
- var deleteBtnData = {
- title : 'Delete',
- href : this.urls[ 'delete' ],
- id : 'historyItemDeleter-' + this.model.get( 'id' ),
- icon_class : 'delete'
- };
- if( this.model.get( 'deleted' ) || this.model.get( 'purged' ) ){
- deleteBtnData = {
- title : 'Dataset is already deleted',
- icon_class : 'delete',
- enabled : false
- };
- }
- this.deleteButton = new IconButtonView({ model : new IconButton( deleteBtnData ) });
- return this.deleteButton.render().$el;
- },
-
- // ................................................................................ titleLink
- // render the hid and hda.name as a link (that will expand the body)
- _render_titleLink : function(){
- return $( jQuery.trim( HDAView.templates.titleLink(
- _.extend( this.model.toJSON(), { urls: this.urls } )
- )));
- },
-
- // ................................................................................ RENDER BODY
- // render the data/metadata summary (format, size, misc info, etc.)
- _render_hdaSummary : function(){
- var modelData = _.extend( this.model.toJSON(), { urls: this.urls } );
- // if there's no dbkey and it's editable : pass a flag to the template to render a link to editing in the '?'
- if( this.model.get( 'metadata_dbkey' ) === '?'
- && !this.model.isDeletedOrPurged() ){
- _.extend( modelData, { dbkey_unknown_and_editable : true });
- }
- return HDAView.templates.hdaSummary( modelData );
- },
-
- // ................................................................................ primary actions
- // render the icon-buttons gen. placed underneath the hda summary
- _render_primaryActionButtons : function( buttonRenderingFuncs ){
- var primaryActionButtons = $( '<div/>' ).attr( 'id', 'primary-actions-' + this.model.get( 'id' ) ),
- view = this;
- _.each( buttonRenderingFuncs, function( fn ){
- primaryActionButtons.append( fn.call( view ) );
- });
- return primaryActionButtons;
- },
-
- // icon-button/popupmenu to down the data (and/or the associated meta files (bai, etc.)) for this hda
- _render_downloadButton : function(){
- // don't show anything if the data's been purged
- if( this.model.get( 'purged' ) ){ return null; }
-
- // return either: a single download icon-button (if there are no meta files)
- // or a popupmenu with links to download assoc. meta files (if there are meta files)
- var downloadLinkHTML = HDAView.templates.downloadLinks(
- _.extend( this.model.toJSON(), { urls: this.urls } )
- );
- //this.log( this + '_render_downloadButton, downloadLinkHTML:', downloadLinkHTML );
- return $( downloadLinkHTML );
- },
-
- // icon-button to show the input and output (stdout/err) for the job that created this hda
- _render_errButton : function(){
- if( ( this.model.get( 'state' ) !== HistoryDatasetAssociation.STATES.ERROR )
- || ( !this.model.get( 'for_editing' ) ) ){ return null; }
-
- this.errButton = new IconButtonView({ model : new IconButton({
- title : 'View or report this error',
- href : this.urls.report_error,
- target : 'galaxy_main',
- icon_class : 'bug'
- })});
- return this.errButton.render().$el;
- },
-
- // icon-button to show the input and output (stdout/err) for the job that created this hda
- _render_showParamsButton : function(){
- // gen. safe to show in all cases
- this.showParamsButton = new IconButtonView({ model : new IconButton({
- title : 'View details',
- href : this.urls.show_params,
- target : 'galaxy_main',
- icon_class : 'information'
- }) });
- return this.showParamsButton.render().$el;
- },
-
- // icon-button to re run the job that created this hda
- _render_rerunButton : function(){
- if( !this.model.get( 'for_editing' ) ){ return null; }
- this.rerunButton = new IconButtonView({ model : new IconButton({
- title : 'Run this job again',
- href : this.urls.rerun,
- target : 'galaxy_main',
- icon_class : 'arrow-circle'
- }) });
- return this.rerunButton.render().$el;
- },
-
- // build an icon-button or popupmenu based on the number of applicable visualizations
- // also map button/popup clicks to viz setup functions
- _render_visualizationsButton : function(){
- var dbkey = this.model.get( 'dbkey' ),
- visualizations = this.model.get( 'visualizations' ),
- visualization_url = this.urls.visualization,
- popup_menu_dict = {},
- params = {
- dataset_id: this.model.get( 'id' ),
- hda_ldda: 'hda'
- };
-
- if( !( this.model.hasData() )
- || !( this.model.get( 'for_editing' ) )
- || !( visualizations && visualizations.length )
- || !( visualization_url ) ){
- //console.warn( 'NOT rendering visualization icon' )
- return null;
- }
-
- // render the icon from template
- this.visualizationsButton = new IconButtonView({ model : new IconButton({
- title : 'Visualize',
- href : visualization_url,
- icon_class : 'chart_curve'
- })});
- var $icon = this.visualizationsButton.render().$el;
- $icon.addClass( 'visualize-icon' ); // needed?
-
- //TODO: make this more concise
- // map a function to each visualization in the icon's attributes
- // create a popupmenu from that map
- // Add dbkey to params if it exists.
- if( dbkey ){ params.dbkey = dbkey; }
-
- function create_viz_action( visualization ) {
- switch( visualization ){
- case 'trackster':
- return create_trackster_action_fn( visualization_url, params, dbkey );
- case 'scatterplot':
- return create_scatterplot_action_fn( visualization_url, params );
- default:
- return function(){
- window.parent.location = visualization_url + '/' + visualization + '?' + $.param( params ); };
- }
- }
-
- // No need for popup menu because there's a single visualization.
- if ( visualizations.length === 1 ) {
- $icon.attr( 'title', visualizations[0] );
- $icon.click( create_viz_action( visualizations[0] ) );
-
- // >1: Populate menu dict with visualization fns, make the popupmenu
- } else {
- _.each( visualizations, function( visualization ) {
- //TODO: move to utils
- var titleCaseVisualization = visualization.charAt( 0 ).toUpperCase() + visualization.slice( 1 );
- popup_menu_dict[ titleCaseVisualization ] = create_viz_action( visualization );
- });
- make_popupmenu( $icon, popup_menu_dict );
- }
- return $icon;
- },
-
- // ................................................................................ secondary actions
- // secondary actions: currently tagging and annotation (if user is allowed)
- _render_secondaryActionButtons : function( buttonRenderingFuncs ){
- // move to the right (same level as primary)
- var secondaryActionButtons = $( '<div/>' ),
- view = this;
- secondaryActionButtons
- .attr( 'style', 'float: right;' )
- .attr( 'id', 'secondary-actions-' + this.model.get( 'id' ) );
-
- _.each( buttonRenderingFuncs, function( fn ){
- secondaryActionButtons.append( fn.call( view ) );
- });
- return secondaryActionButtons;
- },
-
- // icon-button to load and display tagging html
- //TODO: these should be a sub-MV
- _render_tagButton : function(){
- if( !( this.model.hasData() )
- || !( this.model.get( 'for_editing' ) )
- || ( !this.urls.tags.get ) ){ return null; }
-
- this.tagButton = new IconButtonView({ model : new IconButton({
- title : 'Edit dataset tags',
- target : 'galaxy_main',
- href : this.urls.tags.get,
- icon_class : 'tags'
- })});
- return this.tagButton.render().$el;
- },
-
- // icon-button to load and display annotation html
- //TODO: these should be a sub-MV
- _render_annotateButton : function(){
- if( !( this.model.hasData() )
- || !( this.model.get( 'for_editing' ) )
- || ( !this.urls.annotation.get ) ){ return null; }
-
- this.annotateButton = new IconButtonView({ model : new IconButton({
- title : 'Edit dataset annotation',
- target : 'galaxy_main',
- icon_class : 'annotate'
- })});
- return this.annotateButton.render().$el;
- },
-
- // ................................................................................ other elements
- // render links to external genome display applications (igb, gbrowse, etc.)
- //TODO: not a fan of the style on these
- _render_displayApps : function(){
- if( !this.model.hasData() ){ return null; }
-
- var displayAppsDiv = $( '<div/>' ).addClass( 'display-apps' );
- if( !_.isEmpty( this.model.get( 'display_types' ) ) ){
- //this.log( this + 'display_types:', this.model.get( 'urls' ).display_types );
- //TODO:?? does this ever get used?
- displayAppsDiv.append(
- HDAView.templates.displayApps({ displayApps : this.model.get( 'display_types' ) })
- );
- }
- if( !_.isEmpty( this.model.get( 'display_apps' ) ) ){
- //this.log( this + 'display_apps:', this.model.get( 'urls' ).display_apps );
- displayAppsDiv.append(
- HDAView.templates.displayApps({ displayApps : this.model.get( 'display_apps' ) })
- );
- }
- return displayAppsDiv;
- },
-
- //TODO: into sub-MV
- // render the area used to load tag display
- _render_tagArea : function(){
- if( !this.urls.tags.set ){ return null; }
- //TODO: move to mvc/tags.js
- return $( HDAView.templates.tagArea(
- _.extend( this.model.toJSON(), { urls: this.urls } )
- ));
- },
-
- //TODO: into sub-MV
- // render the area used to load annotation display
- _render_annotationArea : function(){
- if( !this.urls.annotation.get ){ return null; }
- //TODO: move to mvc/annotations.js
- return $( HDAView.templates.annotationArea(
- _.extend( this.model.toJSON(), { urls: this.urls } )
- ));
- },
-
- // render the data peek
- //TODO: curr. pre-formatted into table on the server side - may not be ideal/flexible
- _render_peek : function(){
- if( !this.model.get( 'peek' ) ){ return null; }
- return $( '<div/>' ).append(
- $( '<pre/>' )
- .attr( 'id', 'peek' + this.model.get( 'id' ) )
- .addClass( 'peek' )
- .append( this.model.get( 'peek' ) )
- );
- },
-
- // ................................................................................ state body renderers
- // _render_body fns for the various states
- //TODO: only render these on expansion (or already expanded)
- _render_body_not_viewable : function( parent ){
- //TODO: revisit - still showing display, edit, delete (as common) - that CAN'T be right
- parent.append( $( '<div>You do not have permission to view dataset.</div>' ) );
- },
-
- _render_body_uploading : function( parent ){
- parent.append( $( '<div>Dataset is uploading</div>' ) );
- },
-
- _render_body_queued : function( parent ){
- parent.append( $( '<div>Job is waiting to run.</div>' ) );
- parent.append( this._render_primaryActionButtons([
- this._render_showParamsButton,
- this._render_rerunButton
- ]));
- },
-
- _render_body_running : function( parent ){
- parent.append( '<div>Job is currently running.</div>' );
- parent.append( this._render_primaryActionButtons([
- this._render_showParamsButton,
- this._render_rerunButton
- ]));
- },
-
- _render_body_error : function( parent ){
- if( !this.model.get( 'purged' ) ){
- parent.append( $( '<div>' + this.model.get( 'misc_blurb' ) + '</div>' ) );
- }
- parent.append( ( 'An error occurred running this job: '
- + '<i>' + $.trim( this.model.get( 'misc_info' ) ) + '</i>' ) );
- parent.append( this._render_primaryActionButtons([
- this._render_downloadButton,
- this._render_errButton,
- this._render_showParamsButton,
- this._render_rerunButton
- ]));
- },
-
- _render_body_discarded : function( parent ){
- parent.append( '<div>The job creating this dataset was cancelled before completion.</div>' );
- parent.append( this._render_primaryActionButtons([
- this._render_showParamsButton,
- this._render_rerunButton
- ]));
- },
-
- _render_body_setting_metadata : function( parent ){
- parent.append( $( '<div>Metadata is being auto-detected.</div>' ) );
- },
-
- _render_body_empty : function( parent ){
- //TODO: replace i with dataset-misc-info class
- //?? why are we showing the file size when we know it's zero??
- parent.append( $( '<div>No data: <i>' + this.model.get( 'misc_blurb' ) + '</i></div>' ) );
- parent.append( this._render_primaryActionButtons([
- this._render_showParamsButton,
- this._render_rerunButton
- ]));
- },
-
- _render_body_failed_metadata : function( parent ){
- //TODO: the css for this box is broken (unlike the others)
- // add a message box about the failure at the top of the body...
- parent.append( $( HDAView.templates.failedMetadata( this.model.toJSON() ) ) );
- //...then render the remaining body as STATES.OK (only diff between these states is the box above)
- this._render_body_ok( parent );
- },
-
- _render_body_ok : function( parent ){
- // most common state renderer and the most complicated
- parent.append( this._render_hdaSummary() );
-
- // return shortened form if del'd
- //TODO: is this correct? maybe only on purged
- if( this.model.isDeletedOrPurged() ){
- parent.append( this._render_primaryActionButtons([
- this._render_downloadButton,
- this._render_showParamsButton,
- this._render_rerunButton
- ]));
- return;
- }
-
- //NOTE: change the order here
- parent.append( this._render_primaryActionButtons([
- this._render_downloadButton,
- this._render_errButton,
- this._render_showParamsButton,
- this._render_rerunButton,
- this._render_visualizationsButton
- ]));
- parent.append( this._render_secondaryActionButtons([
- this._render_tagButton,
- this._render_annotateButton
- ]));
- parent.append( '<div class="clear"/>' );
-
- parent.append( this._render_tagArea() );
- parent.append( this._render_annotationArea() );
-
- parent.append( this._render_displayApps() );
- parent.append( this._render_peek() );
-
- //TODO??: still needed?
- //// If Mozilla, hide scrollbars in hidden items since they cause animation bugs
- //if ( $.browser.mozilla ) {
- // $( "div.historyItemBody" ).each( function() {
- // if ( !$(this).is(":visible") ) { $(this).find( "pre.peek" ).css( "overflow", "hidden" ); }
- // });
- //}
- },
-
- _render_body : function(){
- //this.log( this + '_render_body' );
- //this.log( 'state:', state, 'for_editing', for_editing );
-
- //TODO: incorrect id (encoded - use hid?)
- var body = $( '<div/>' )
- .attr( 'id', 'info-' + this.model.get( 'id' ) )
- .addClass( 'historyItemBody' )
- .attr( 'style', 'display: block' );
-
- //TODO: not a fan of this dispatch
- switch( this.model.get( 'state' ) ){
- case HistoryDatasetAssociation.STATES.NOT_VIEWABLE :
- this._render_body_not_viewable( body );
- break;
- case HistoryDatasetAssociation.STATES.UPLOAD :
- this._render_body_uploading( body );
- break;
- case HistoryDatasetAssociation.STATES.QUEUED :
- this._render_body_queued( body );
- break;
- case HistoryDatasetAssociation.STATES.RUNNING :
- this._render_body_running( body );
- break;
- case HistoryDatasetAssociation.STATES.ERROR :
- this._render_body_error( body );
- break;
- case HistoryDatasetAssociation.STATES.DISCARDED :
- this._render_body_discarded( body );
- break;
- case HistoryDatasetAssociation.STATES.SETTING_METADATA :
- this._render_body_setting_metadata( body );
- break;
- case HistoryDatasetAssociation.STATES.EMPTY :
- this._render_body_empty( body );
- break;
- case HistoryDatasetAssociation.STATES.FAILED_METADATA :
- this._render_body_failed_metadata( body );
- break;
- case HistoryDatasetAssociation.STATES.OK :
- this._render_body_ok( body );
- break;
- default:
- //??: no body?
- body.append( $( '<div>Error: unknown dataset state "' + state + '".</div>' ) );
- }
- body.append( '<div style="clear: both"></div>' );
-
- if( this.expanded ){
- body.show();
- } else {
- body.hide();
- }
- return body;
- },
-
- // ................................................................................ EVENTS
- events : {
- 'click .historyItemTitle' : 'toggleBodyVisibility',
- 'click a.icon-button.tags' : 'loadAndDisplayTags',
- 'click a.icon-button.annotate' : 'loadAndDisplayAnnotation'
- },
-
- // ................................................................................ STATE CHANGES / MANIPULATION
- // find the tag area and, if initial: (via ajax) load the html for displaying them; otherwise, unhide/hide
- //TODO: into sub-MV
- loadAndDisplayTags : function( event ){
- //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' ),
- tagElt = tagArea.find( '.tag-elt' );
-
- // 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.
- $.ajax({
- //TODO: the html from this breaks a couple of times
- url: this.urls.tags.get,
- error: function() { alert( "Tagging failed" ); },
- success: function(tag_elt_html) {
- tagElt.html(tag_elt_html);
- tagElt.find(".tooltip").tooltip();
- tagArea.slideDown("fast");
- }
- });
- } else {
- // Tag element is filled; show.
- tagArea.slideDown("fast");
- }
- } else {
- // Hide.
- tagArea.slideUp("fast");
- }
- return false;
- },
-
- // find the annotation area and, if initial: (via ajax) load the html for displaying it; otherwise, unhide/hide
- //TODO: into sub-MV
- loadAndDisplayAnnotation : function( event ){
- //TODO: this is a drop in from history.mako - should use MV as well
- this.log( this + '.loadAndDisplayAnnotation', event );
- var annotationArea = this.$el.find( '.annotation-area' ),
- annotationElem = annotationArea.find( '.annotation-elt' ),
- setAnnotationUrl = this.urls.annotation.set;
-
- // Show or hide annotation area; if showing annotation area and it's empty, fill it.
- if ( annotationArea.is( ":hidden" ) ){
- if( !jQuery.trim( annotationElem.html() ) ){
- // Need to fill annotation element.
- $.ajax({
- url: this.urls.annotation.get,
- error: function(){ alert( "Annotations failed" ); },
- success: function( htmlFromAjax ){
- if( htmlFromAjax === "" ){
- htmlFromAjax = "<em>Describe or add notes to dataset</em>";
- }
- annotationElem.html( htmlFromAjax );
- annotationArea.find(".tooltip").tooltip();
-
- async_save_text(
- annotationElem.attr("id"), annotationElem.attr("id"),
- setAnnotationUrl,
- "new_annotation", 18, true, 4
- );
- annotationArea.slideDown("fast");
- }
- });
- } else {
- annotationArea.slideDown("fast");
- }
-
- } else {
- // Hide.
- annotationArea.slideUp("fast");
- }
- return false;
- },
-
- // expand/collapse body
- //side effect: trigger event
- toggleBodyVisibility : function( event, expanded ){
- var $body = this.$el.find( '.historyItemBody' );
- expanded = ( expanded === undefined )?( !$body.is( ':visible' ) ):( expanded );
- //this.log( 'toggleBodyVisibility, expanded:', expanded, '$body:', $body );
-
- if( expanded ){
- $body.slideDown( 'fast' );
- } else {
- $body.slideUp( 'fast' );
- }
- this.trigger( 'toggleBodyVisibility', this.model.get( 'id' ), expanded );
- },
-
- // ................................................................................ UTILTIY
- toString : function(){
- var modelString = ( this.model )?( this.model + '' ):( '(no model)' );
- return 'HDAView(' + modelString + ')';
- }
-});
-
-//------------------------------------------------------------------------------
-HDAView.templates = {
- warningMsg : Handlebars.templates[ 'template-warningmessagesmall' ],
-
- messages : Handlebars.templates[ 'template-history-warning-messages' ],
- titleLink : Handlebars.templates[ 'template-history-titleLink' ],
- hdaSummary : Handlebars.templates[ 'template-history-hdaSummary' ],
- downloadLinks : Handlebars.templates[ 'template-history-downloadLinks' ],
- failedMetadata : Handlebars.templates[ 'template-history-failedMetaData' ],
- tagArea : Handlebars.templates[ 'template-history-tagArea' ],
- annotationArea : Handlebars.templates[ 'template-history-annotationArea' ],
- displayApps : Handlebars.templates[ 'template-history-displayApps' ]
-};
-
-//==============================================================================
-//TODO: these belong somewhere else
-
-//TODO: should be imported from scatterplot.js
-//TODO: OR abstracted to 'load this in the galaxy_main frame'
-function create_scatterplot_action_fn( url, params ){
- action = function() {
- var galaxy_main = $( window.parent.document ).find( 'iframe#galaxy_main' ),
- final_url = url + '/scatterplot?' + $.param(params);
- galaxy_main.attr( 'src', final_url );
- //TODO: this needs to go away
- $( 'div.popmenu-wrapper' ).remove();
- return false;
- };
- return action;
-}
-
-// -----------------------------------------------------------------------------
-// Create trackster action function.
-//TODO: should be imported from trackster.js
-function create_trackster_action_fn(vis_url, dataset_params, dbkey) {
- return function() {
- var params = {};
- if (dbkey) { params.dbkey = dbkey; }
- $.ajax({
- url: vis_url + '/list_tracks?f-' + $.param(params),
- dataType: "html",
- error: function() { alert( "Could not add this dataset to browser." ); },
- success: function(table_html) {
- var parent = window.parent;
-
- parent.show_modal("View Data in a New or Saved Visualization", "", {
- "Cancel": function() {
- parent.hide_modal();
- },
- "View in saved visualization": function() {
- // Show new modal with saved visualizations.
- parent.show_modal("Add Data to Saved Visualization", table_html, {
- "Cancel": function() {
- parent.hide_modal();
- },
- "Add to visualization": function() {
- $(parent.document).find('input[name=id]:checked').each(function() {
- var vis_id = $(this).val();
- dataset_params.id = vis_id;
- parent.location = vis_url + "/trackster?" + $.param(dataset_params);
- });
- }
- });
- },
- "View in new visualization": function() {
- parent.location = vis_url + "/trackster?" + $.param(dataset_params);
- }
- });
- }
- });
- return false;
- };
-}
-
-
-//==============================================================================
-// view for the HDACollection (as per current right hand panel)
-var HistoryView = BaseView.extend( LoggableMixin ).extend({
-
- // uncomment this out see log messages
- //logger : console,
-
- // direct attachment to existing element
- el : 'body.historyPage',
-
- // init with the model, urlTemplates, set up storage, bind HDACollection events
- //NOTE: this will create or load PersistantStorage keyed under 'HistoryView.<id>'
- //pre: you'll need to pass in the urlTemplates (urlTemplates : { history : {...}, hda : {...} })
- initialize : function( attributes ){
- this.log( this + '.initialize:', attributes );
-
- // set up url templates
- //TODO: prob. better to put this in class scope (as the handlebars templates), but...
- // they're added to GalaxyPaths on page load (after this file is loaded)
- if( !attributes.urlTemplates ){ throw( 'HDAView needs urlTemplates on initialize' ); }
- if( !attributes.urlTemplates.history ){ throw( 'HDAView needs urlTemplates.history on initialize' ); }
- if( !attributes.urlTemplates.hda ){ throw( 'HDAView needs urlTemplates.hda on initialize' ); }
- this.urlTemplates = attributes.urlTemplates.history;
- this.hdaUrlTemplates = attributes.urlTemplates.hda;
-
- // data that needs to be persistant over page refreshes
- // (note the key function which uses the history id as well)
- this.storage = new PersistantStorage(
- 'HistoryView.' + this.model.get( 'id' ),
- { expandedHdas : {} }
- );
-
- // bind events from the model's hda collection
- //this.model.bind( 'change', this.render, this );
- this.model.bind( 'change:nice_size', this.updateHistoryDiskSize, this );
-
- this.model.hdas.bind( 'add', this.add, this );
- this.model.hdas.bind( 'reset', this.addAll, this );
- this.model.hdas.bind( 'all', this.all, this );
-
- //this.bind( 'all', function(){
- // this.log( arguments );
- //}, this );
-
- // set up instance vars
- this.hdaViews = {};
- this.urls = {};
- },
-
- add : function( hda ){
- //console.debug( 'add.' + this, hda );
- //TODO
- },
-
- addAll : function(){
- //console.debug( 'addAll.' + this );
- // re render when all hdas are reset
- this.render();
- },
-
- all : function( event ){
- //console.debug( 'allItemEvents.' + this, event );
- //...for which to do the debuggings
- },
-
- // render the urls for this view using urlTemplates and the model data
- renderUrls : function( modelJson ){
- var historyView = this;
-
- historyView.urls = {};
- _.each( this.urlTemplates, function( urlTemplate, urlKey ){
- historyView.urls[ urlKey ] = _.template( urlTemplate, modelJson );
- });
- return historyView.urls;
- },
-
- // render urls, historyView body, and hdas (if any are shown), fade out, swap, fade in, set up behaviours
- render : function(){
- var historyView = this,
- setUpQueueName = historyView.toString() + '.set-up',
- newRender = $( '<div/>' ),
- modelJson = this.model.toJSON(),
- initialRender = ( this.$el.children().size() === 0 );
-
- //console.debug( this + '.render, initialRender:', initialRender );
-
- // render the urls and add them to the model json
- modelJson.urls = this.renderUrls( modelJson );
-
- // render the main template, tooltips
- //NOTE: this is done before the items, since item views should handle theirs themselves
- newRender.append( HistoryView.templates.historyPanel( modelJson ) );
- newRender.find( '.tooltip' ).tooltip();
-
- // render hda views (if any and any shown (show_deleted/hidden)
- if( !this.model.hdas.length
- || !this.renderItems( newRender.find( '#' + this.model.get( 'id' ) + '-datasets' ) ) ){
- // if history is empty or no hdas would be rendered, show the empty message
- newRender.find( '#emptyHistoryMessage' ).show();
- }
-
- // fade out existing, swap with the new, fade in, set up behaviours
- $( historyView ).queue( setUpQueueName, function( next ){
- historyView.$el.fadeOut( 'fast', function(){ next(); });
- });
- $( historyView ).queue( setUpQueueName, function( next ){
- // swap over from temp div newRender
- historyView.$el.html( '' );
- historyView.$el.append( newRender.children() );
-
- historyView.$el.fadeIn( 'fast', function(){ next(); });
- });
- $( historyView ).queue( setUpQueueName, function( next ){
- this.log( historyView + ' rendered:', historyView.$el );
-
- //TODO: ideally, these would be set up before the fade in (can't because of async save text)
- historyView.setUpBehaviours();
-
- if( initialRender ){
- historyView.trigger( 'rendered:initial' );
-
- } else {
- historyView.trigger( 'rendered' );
- }
- next();
- });
- $( historyView ).dequeue( setUpQueueName );
- return this;
- },
-
- // set up a view for each item to be shown, init with model and listeners, cache to map ( model.id : view )
- renderItems : function( $whereTo ){
- this.hdaViews = {};
- var historyView = this,
- show_deleted = this.model.get( 'show_deleted' ),
- show_hidden = this.model.get( 'show_hidden' ),
- visibleHdas = this.model.hdas.getVisible( show_deleted, show_hidden );
-
- // only render the shown hdas
- _.each( visibleHdas, function( hda ){
- var hdaId = hda.get( 'id' ),
- expanded = historyView.storage.get( 'expandedHdas' ).get( hdaId );
- historyView.hdaViews[ hdaId ] = new HDAView({
- model : hda,
- expanded : expanded,
- urlTemplates : historyView.hdaUrlTemplates
- });
- historyView.setUpHdaListeners( historyView.hdaViews[ hdaId ] );
- // render it (NOTE: reverse order, newest on top (prepend))
- //TODO: by default send a reverse order list (although this may be more efficient - it's more confusing)
- $whereTo.prepend( historyView.hdaViews[ hdaId ].render().$el );
- });
- return visibleHdas.length;
- },
-
- // set up HistoryView->HDAView listeners
- setUpHdaListeners : function( hdaView ){
- var historyView = this;
-
- // use storage to maintain a list of hdas whose bodies are expanded
- hdaView.bind( 'toggleBodyVisibility', function( id, visible ){
- if( visible ){
- historyView.storage.get( 'expandedHdas' ).set( id, true );
- } else {
- historyView.storage.get( 'expandedHdas' ).deleteKey( id );
- }
- });
-
- // rendering listeners
- hdaView.bind( 'rendered:ready', function(){ historyView.trigger( 'hda:rendered:ready' ); });
- },
-
- // set up js/widget behaviours: tooltips,
- //TODO: these should be either sub-MVs, or handled by events
- setUpBehaviours : function(){
- // anon users shouldn't have access to any of these
- if( !( this.model.get( 'user' ) && this.model.get( 'user' ).email ) ){ return; }
-
- // annotation slide down
- var historyAnnotationArea = this.$( '#history-annotation-area' );
- this.$( '#history-annotate' ).click( function() {
- if ( historyAnnotationArea.is( ":hidden" ) ) {
- historyAnnotationArea.slideDown( "fast" );
- } else {
- historyAnnotationArea.slideUp( "fast" );
- }
- return false;
- });
-
- // title and annotation editable text
- //NOTE: these use page scoped selectors - so these need to be in the page DOM before they're applicable
- async_save_text( "history-name-container", "history-name",
- this.urls.rename, "new_name", 18 );
-
- async_save_text( "history-annotation-container", "history-annotation",
- this.urls.annotate, "new_annotation", 18, true, 4 );
- },
-
- // update the history size display (curr. upper right of panel)
- updateHistoryDiskSize : function(){
- this.$el.find( '#history-size' ).text( this.model.get( 'nice_size' ) );
- },
-
- //TODO: this seems more like a per user message than a history message; IOW, this doesn't belong here
- showQuotaMessage : function( userData ){
- var msg = this.$el.find( '#quota-message-container' );
- //this.log( this + ' showing quota message:', msg, userData );
- if( msg.is( ':hidden' ) ){ msg.slideDown( 'fast' ); }
- },
-
- //TODO: this seems more like a per user message than a history message
- hideQuotaMessage : function( userData ){
- var msg = this.$el.find( '#quota-message-container' );
- //this.log( this + ' hiding quota message:', msg, userData );
- if( !msg.is( ':hidden' ) ){ msg.slideUp( 'fast' ); }
- },
-
- events : {
- 'click #history-collapse-all' : 'hideAllHdaBodies',
- 'click #history-tag' : 'loadAndDisplayTags'
- },
-
- // collapse all hda bodies
- hideAllHdaBodies : function(){
- _.each( this.hdaViews, function( item ){
- item.toggleBodyVisibility( null, false );
- });
- this.storage.set( 'expandedHdas', {} );
- },
-
- // find the tag area and, if initial: (via ajax) load the html for displaying them; otherwise, unhide/hide
- //TODO: into sub-MV
- loadAndDisplayTags : function( event ){
- this.log( this + '.loadAndDisplayTags', event );
- var tagArea = this.$el.find( '#history-tag-area' ),
- tagElt = tagArea.find( '.tag-elt' );
- this.log( '\t tagArea', tagArea, ' tagElt', tagElt );
-
- // Show or hide tag area; if showing tag area and it's empty, fill it
- if( tagArea.is( ":hidden" ) ){
- if( !jQuery.trim( tagElt.html() ) ){
- var view = this;
- // Need to fill tag element.
- $.ajax({
- //TODO: the html from this breaks a couple of times
- url: view.urls.tag,
- error: function() { alert( "Tagging failed" ); },
- success: function(tag_elt_html) {
- //view.log( view + ' tag elt html (ajax)', tag_elt_html );
- tagElt.html(tag_elt_html);
- tagElt.find(".tooltip").tooltip();
- tagArea.slideDown("fast");
- }
- });
- } else {
- // Tag element already filled: show
- tagArea.slideDown("fast");
- }
-
- } else {
- // Currently shown: Hide
- tagArea.slideUp("fast");
- }
- return false;
- },
-
- toString : function(){
- var nameString = this.model.get( 'name' ) || '';
- return 'HistoryView(' + nameString + ')';
- }
-});
-HistoryView.templates = {
- historyPanel : Handlebars.templates[ 'template-history-historyPanel' ]
-};
-
-//==============================================================================
-//return {
-// HistoryItem : HistoryItem,
-// HDAView : HDAView,
-// HistoryCollection : HistoryCollection,
-// History : History,
-// HistoryView : HistoryView
-//};});
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 static/scripts/mvc/history/history-model.js
--- /dev/null
+++ b/static/scripts/mvc/history/history-model.js
@@ -0,0 +1,180 @@
+//define([
+// "../mvc/base-mvc"
+//], function(){
+//==============================================================================
+/**
+ *
+ */
+var History = BaseModel.extend( LoggableMixin ).extend({
+ //TODO: bind change events from items and collection to this (itemLengths, states)
+
+ // uncomment this out see log messages
+ //logger : console,
+
+ // values from api (may need more)
+ defaults : {
+ id : '',
+ name : '',
+ state : '',
+
+ //TODO: wire these to items (or this)
+ show_deleted : false,
+ show_hidden : false,
+
+ diskSize : 0,
+ deleted : false,
+
+ tags : [],
+ annotation : null,
+
+ //TODO: quota msg and message? how to get those over the api?
+ message : null,
+ quotaMsg : false
+ },
+
+ url : function(){
+ // api location of history resource
+ //TODO: hardcoded
+ return 'api/histories/' + this.get( 'id' );
+ },
+
+ initialize : function( initialSettings, initialHdas ){
+ this.log( this + ".initialize:", initialSettings, initialHdas );
+
+ 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();
+ }
+
+ //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)
+ loadFromApi : function( historyId, callback ){
+ 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 ){
+ //console.warn( 'fetched user, history: ', userResponse, historyResponse );
+ history.attributes.user = userResponse[0]; //? meh.
+ history.log( history );
+
+ }).then( function(){
+ // ...then the hdas (using contents?ids=...)
+ jQuery.ajax( history.url() + '/contents?' + jQuery.param({
+ ids : history.itemIdsFromStateIds().join( ',' )
+
+ // reset the collection to the hdas returned
+ })).success( function( hdas ){
+ //console.warn( 'fetched hdas' );
+ history.hdas.reset( hdas );
+ history.checkForUpdates();
+ callback();
+ });
+ });
+ },
+
+ // reduce the state_ids map of hda id lists -> a single list of ids
+ //...ugh - seems roundabout; necessary because the history doesn't have a straightforward list of ids
+ // (and history_contents/index curr returns a summary only)
+ hdaIdsFromStateIds : function(){
+ return _.reduce( _.values( this.get( 'state_ids' ) ), function( reduction, currIdList ){
+ return reduction.concat( currIdList );
+ });
+ },
+
+ // get the history's state from it's cummulative ds states, delay + update if needed
+ checkForUpdates : function( datasets ){
+ // 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 ){
+ this.stateUpdater();
+ }
+ return this;
+ },
+
+ // update this history, find any hda's running/queued, update ONLY those that have changed states,
+ // set up to run this again in some interval of time
+ stateUpdater : function(){
+ var history = this,
+ oldState = this.get( 'state' ),
+ // state ids is a map of every possible hda state, each containing a list of ids for hdas in that state
+ oldStateIds = this.get( 'state_ids' );
+
+ // pull from the history api
+ //TODO: fetch?
+ jQuery.ajax( 'api/histories/' + this.get( 'id' )
+
+ ).success( function( response ){
+ //this.log( 'historyApiRequest, response:', response );
+ history.set( response );
+ history.log( 'current history state:', history.get( 'state' ),
+ '(was)', oldState,
+ 'new size:', history.get( 'nice_size' ) );
+
+ //TODO: revisit this - seems too elaborate, need something straightforward
+ // for each state, check for the difference between old dataset states and new
+ // the goal here is to check ONLY those datasets that have changed states (not all datasets)
+ var changedIds = [];
+ _.each( _.keys( response.state_ids ), function( state ){
+ var diffIds = _.difference( response.state_ids[ state ], oldStateIds[ state ] );
+ // aggregate those changed ids
+ changedIds = changedIds.concat( diffIds );
+ });
+
+ // send the changed ids (if any) to dataset collection to have them fetch their own model changes
+ if( changedIds.length ){
+ history.hdas.update( changedIds );
+ }
+
+ // 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(){
+ history.stateUpdater();
+ }, 4000 );
+ }
+
+ }).error( function( xhr, status, error ){
+ if( console && console.warn ){
+ console.warn( 'Error getting history updates from the server:', xhr, status, error );
+ }
+ alert( 'Error getting history updates from the server.\n' + error );
+ });
+ },
+
+ toString : function(){
+ var nameString = ( this.get( 'name' ) )?
+ ( ',' + this.get( 'name' ) ) : ( '' );
+ return 'History(' + this.get( 'id' ) + nameString + ')';
+ }
+});
+
+//==============================================================================
+/** A collection of histories (per user or admin)
+ * (stub) currently unused
+ */
+var HistoryCollection = Backbone.Collection.extend( LoggableMixin ).extend({
+ model : History,
+ urlRoot : 'api/histories',
+ logger : console
+});
+
+//==============================================================================
+//return {
+// History : History,
+// HistoryCollection : HistoryCollection,
+//};});
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 static/scripts/mvc/history/history-panel.js
--- /dev/null
+++ b/static/scripts/mvc/history/history-panel.js
@@ -0,0 +1,380 @@
+//define([
+// "../mvc/base-mvc"
+//], function(){
+/* =============================================================================
+Backbone.js implementation of history panel
+
+TODO:
+ anon user, mako template init:
+ bug: rename url seems to be wrong url
+
+ logged in, mako template:
+ BUG: meter is not updating RELIABLY on change:nice_size
+ BUG: am able to start upload even if over quota - 'runs' forever
+ bug: quotaMeter bar rendering square in chrome
+ BUG: quotaMsg not showing when 100% (on load)
+ BUG: imported, shared history with unaccessible dataset errs in historycontents when getting history
+ (entire history is inaccessible)
+ ??: still happening?
+
+ from loadFromApi:
+ BUG: not showing previous annotations
+
+ fixed:
+ BUG: upload, history size, doesn't change
+ FIXED: using change:nice_size to trigger re-render of history size
+ BUG: delete uploading hda - now in state 'discarded'! ...new state to handle
+ FIXED: handled state
+ BUG: historyItem, error'd ds show display, download?
+ FIXED: removed
+ bug: loading hdas (alt_hist)
+ FIXED: added anon user api request ( trans.user == None and trans.history.id == requested id )
+ bug: quota meter not updating on upload/tool run
+ FIXED: quotaMeter now listens for 'state:ready' from glx_history in alternate_history.mako
+ bug: use of new HDACollection with event listener in init doesn't die...keeps reporting
+ FIXED: change getVisible to return an array
+ BUG: history, broken intial hist state (running, updater, etc.)
+ ??: doesn't seem to happen anymore
+ BUG: collapse all should remove all expanded from storage
+ FIXED: hideAllItemBodies now resets storage.expandedItems
+ BUG: historyItem, shouldn't allow tag, annotate, peek on purged datasets
+ FIXED: ok state now shows only: info, rerun
+ BUG: history?, some ids aren't returning encoded...
+ FIXED:???
+ BUG: history, showing deleted ds
+ FIXED
+ UGH: historyItems have to be decorated with history_ids (api/histories/:history_id/contents/:id)
+ FIXED by adding history_id to history_contents.show
+ BUG: history, if hist has err'd ds, hist has perm state 'error', updater on following ds's doesn't run
+ FIXED by reordering history state from ds' states here and histories
+ BUG: history, broken annotation on reload (can't get thru api (sets fine, tho))
+ FIXED: get thru api for now
+
+ replication:
+ show_deleted/hidden:
+ use storage
+ on/off ui
+ move histview fadein/out in render to app?
+ don't draw body until it's first expand event
+ localize all
+ ?: render url templates on init or render?
+ ?: history, annotation won't accept unicode
+
+ RESTful:
+ move over webui functions available in api
+ delete, undelete
+ update?
+ currently, adding a dataset (via tool execute, etc.) creates a new dataset and refreshes the page
+ provide a means to update the panel via js
+
+ hierarchy:
+ to relational model?
+ HDACollection, meta_files, display_apps, etc.
+ dataset -> hda
+ history -> historyForEditing, historyForViewing
+ display_structured?
+
+ meta:
+ css/html class/id 'item' -> hda
+ add classes, ids on empty divs
+ events (local/ui and otherwise)
+ list in docs as well
+ require.js
+ convert function comments to jsDoc style, complete comments
+ move inline styles into base.less
+ watch the magic strings
+ watch your globals
+
+ feature creep:
+ lineage
+ hide button
+ show permissions in info
+ show shared/sharing status on ds, history
+ maintain scroll position on refresh (storage?)
+ selection, multi-select (and actions common to selected (ugh))
+ searching
+ sorting, re-shuffling
+
+============================================================================= */
+/** view for the HDACollection (as per current right hand panel)
+ *
+ */
+var HistoryPanel = BaseView.extend( LoggableMixin ).extend({
+
+ // uncomment this out see log messages
+ //logger : console,
+
+ // direct attachment to existing element
+ el : 'body.historyPage',
+
+ // init with the model, urlTemplates, set up storage, bind HDACollection events
+ //NOTE: this will create or load PersistantStorage keyed under 'HistoryView.<id>'
+ //pre: you'll need to pass in the urlTemplates (urlTemplates : { history : {...}, hda : {...} })
+ initialize : function( attributes ){
+ this.log( this + '.initialize:', attributes );
+
+ // set up url templates
+ //TODO: prob. better to put this in class scope (as the handlebars templates), but...
+ // they're added to GalaxyPaths on page load (after this file is loaded)
+ if( !attributes.urlTemplates ){ throw( 'HDAView needs urlTemplates on initialize' ); }
+ if( !attributes.urlTemplates.history ){ throw( 'HDAView needs urlTemplates.history on initialize' ); }
+ if( !attributes.urlTemplates.hda ){ throw( 'HDAView needs urlTemplates.hda on initialize' ); }
+ this.urlTemplates = attributes.urlTemplates.history;
+ this.hdaUrlTemplates = attributes.urlTemplates.hda;
+
+ // data that needs to be persistant over page refreshes
+ // (note the key function which uses the history id as well)
+ this.storage = new PersistantStorage(
+ 'HistoryView.' + this.model.get( 'id' ),
+ { expandedHdas : {} }
+ );
+
+ // bind events from the model's hda collection
+ //this.model.bind( 'change', this.render, this );
+ this.model.bind( 'change:nice_size', this.updateHistoryDiskSize, this );
+
+ this.model.hdas.bind( 'add', this.add, this );
+ this.model.hdas.bind( 'reset', this.addAll, this );
+ this.model.hdas.bind( 'all', this.all, this );
+
+ //this.bind( 'all', function(){
+ // this.log( arguments );
+ //}, this );
+
+ // set up instance vars
+ this.hdaViews = {};
+ this.urls = {};
+ },
+
+ add : function( hda ){
+ //console.debug( 'add.' + this, hda );
+ //TODO
+ },
+
+ addAll : function(){
+ //console.debug( 'addAll.' + this );
+ // re render when all hdas are reset
+ this.render();
+ },
+
+ all : function( event ){
+ //console.debug( 'allItemEvents.' + this, event );
+ //...for which to do the debuggings
+ },
+
+ // render the urls for this view using urlTemplates and the model data
+ renderUrls : function( modelJson ){
+ var historyView = this;
+
+ historyView.urls = {};
+ _.each( this.urlTemplates, function( urlTemplate, urlKey ){
+ historyView.urls[ urlKey ] = _.template( urlTemplate, modelJson );
+ });
+ return historyView.urls;
+ },
+
+ // render urls, historyView body, and hdas (if any are shown), fade out, swap, fade in, set up behaviours
+ render : function(){
+ var historyView = this,
+ setUpQueueName = historyView.toString() + '.set-up',
+ newRender = $( '<div/>' ),
+ modelJson = this.model.toJSON(),
+ initialRender = ( this.$el.children().size() === 0 );
+
+ //console.debug( this + '.render, initialRender:', initialRender );
+
+ // render the urls and add them to the model json
+ modelJson.urls = this.renderUrls( modelJson );
+
+ // render the main template, tooltips
+ //NOTE: this is done before the items, since item views should handle theirs themselves
+ newRender.append( HistoryPanel.templates.historyPanel( modelJson ) );
+ newRender.find( '.tooltip' ).tooltip();
+
+ // render hda views (if any and any shown (show_deleted/hidden)
+ if( !this.model.hdas.length
+ || !this.renderItems( newRender.find( '#' + this.model.get( 'id' ) + '-datasets' ) ) ){
+ // if history is empty or no hdas would be rendered, show the empty message
+ newRender.find( '#emptyHistoryMessage' ).show();
+ }
+
+ // fade out existing, swap with the new, fade in, set up behaviours
+ $( historyView ).queue( setUpQueueName, function( next ){
+ historyView.$el.fadeOut( 'fast', function(){ next(); });
+ });
+ $( historyView ).queue( setUpQueueName, function( next ){
+ // swap over from temp div newRender
+ historyView.$el.html( '' );
+ historyView.$el.append( newRender.children() );
+
+ historyView.$el.fadeIn( 'fast', function(){ next(); });
+ });
+ $( historyView ).queue( setUpQueueName, function( next ){
+ this.log( historyView + ' rendered:', historyView.$el );
+
+ //TODO: ideally, these would be set up before the fade in (can't because of async save text)
+ historyView.setUpBehaviours();
+
+ if( initialRender ){
+ historyView.trigger( 'rendered:initial' );
+
+ } else {
+ historyView.trigger( 'rendered' );
+ }
+ next();
+ });
+ $( historyView ).dequeue( setUpQueueName );
+ return this;
+ },
+
+ // set up a view for each item to be shown, init with model and listeners, cache to map ( model.id : view )
+ renderItems : function( $whereTo ){
+ this.hdaViews = {};
+ var historyView = this,
+ show_deleted = this.model.get( 'show_deleted' ),
+ show_hidden = this.model.get( 'show_hidden' ),
+ visibleHdas = this.model.hdas.getVisible( show_deleted, show_hidden );
+
+ // only render the shown hdas
+ _.each( visibleHdas, function( hda ){
+ var hdaId = hda.get( 'id' ),
+ expanded = historyView.storage.get( 'expandedHdas' ).get( hdaId );
+ historyView.hdaViews[ hdaId ] = new HDAView({
+ model : hda,
+ expanded : expanded,
+ urlTemplates : historyView.hdaUrlTemplates
+ });
+ historyView.setUpHdaListeners( historyView.hdaViews[ hdaId ] );
+ // render it (NOTE: reverse order, newest on top (prepend))
+ //TODO: by default send a reverse order list (although this may be more efficient - it's more confusing)
+ $whereTo.prepend( historyView.hdaViews[ hdaId ].render().$el );
+ });
+ return visibleHdas.length;
+ },
+
+ // set up HistoryView->HDAView listeners
+ setUpHdaListeners : function( hdaView ){
+ var historyView = this;
+
+ // use storage to maintain a list of hdas whose bodies are expanded
+ hdaView.bind( 'toggleBodyVisibility', function( id, visible ){
+ if( visible ){
+ historyView.storage.get( 'expandedHdas' ).set( id, true );
+ } else {
+ historyView.storage.get( 'expandedHdas' ).deleteKey( id );
+ }
+ });
+
+ // rendering listeners
+ hdaView.bind( 'rendered:ready', function(){ historyView.trigger( 'hda:rendered:ready' ); });
+ },
+
+ // set up js/widget behaviours: tooltips,
+ //TODO: these should be either sub-MVs, or handled by events
+ setUpBehaviours : function(){
+ // anon users shouldn't have access to any of these
+ if( !( this.model.get( 'user' ) && this.model.get( 'user' ).email ) ){ return; }
+
+ // annotation slide down
+ var historyAnnotationArea = this.$( '#history-annotation-area' );
+ this.$( '#history-annotate' ).click( function() {
+ if ( historyAnnotationArea.is( ":hidden" ) ) {
+ historyAnnotationArea.slideDown( "fast" );
+ } else {
+ historyAnnotationArea.slideUp( "fast" );
+ }
+ return false;
+ });
+
+ // title and annotation editable text
+ //NOTE: these use page scoped selectors - so these need to be in the page DOM before they're applicable
+ async_save_text( "history-name-container", "history-name",
+ this.urls.rename, "new_name", 18 );
+
+ async_save_text( "history-annotation-container", "history-annotation",
+ this.urls.annotate, "new_annotation", 18, true, 4 );
+ },
+
+ // update the history size display (curr. upper right of panel)
+ updateHistoryDiskSize : function(){
+ this.$el.find( '#history-size' ).text( this.model.get( 'nice_size' ) );
+ },
+
+ //TODO: this seems more like a per user message than a history message; IOW, this doesn't belong here
+ showQuotaMessage : function( userData ){
+ var msg = this.$el.find( '#quota-message-container' );
+ //this.log( this + ' showing quota message:', msg, userData );
+ if( msg.is( ':hidden' ) ){ msg.slideDown( 'fast' ); }
+ },
+
+ //TODO: this seems more like a per user message than a history message
+ hideQuotaMessage : function( userData ){
+ var msg = this.$el.find( '#quota-message-container' );
+ //this.log( this + ' hiding quota message:', msg, userData );
+ if( !msg.is( ':hidden' ) ){ msg.slideUp( 'fast' ); }
+ },
+
+ events : {
+ 'click #history-collapse-all' : 'hideAllHdaBodies',
+ 'click #history-tag' : 'loadAndDisplayTags'
+ },
+
+ // collapse all hda bodies
+ hideAllHdaBodies : function(){
+ _.each( this.hdaViews, function( item ){
+ item.toggleBodyVisibility( null, false );
+ });
+ this.storage.set( 'expandedHdas', {} );
+ },
+
+ // find the tag area and, if initial: (via ajax) load the html for displaying them; otherwise, unhide/hide
+ //TODO: into sub-MV
+ loadAndDisplayTags : function( event ){
+ this.log( this + '.loadAndDisplayTags', event );
+ var tagArea = this.$el.find( '#history-tag-area' ),
+ tagElt = tagArea.find( '.tag-elt' );
+ this.log( '\t tagArea', tagArea, ' tagElt', tagElt );
+
+ // Show or hide tag area; if showing tag area and it's empty, fill it
+ if( tagArea.is( ":hidden" ) ){
+ if( !jQuery.trim( tagElt.html() ) ){
+ var view = this;
+ // Need to fill tag element.
+ $.ajax({
+ //TODO: the html from this breaks a couple of times
+ url: view.urls.tag,
+ error: function() { alert( "Tagging failed" ); },
+ success: function(tag_elt_html) {
+ //view.log( view + ' tag elt html (ajax)', tag_elt_html );
+ tagElt.html(tag_elt_html);
+ tagElt.find(".tooltip").tooltip();
+ tagArea.slideDown("fast");
+ }
+ });
+ } else {
+ // Tag element already filled: show
+ tagArea.slideDown("fast");
+ }
+
+ } else {
+ // Currently shown: Hide
+ tagArea.slideUp("fast");
+ }
+ return false;
+ },
+
+ toString : function(){
+ var nameString = this.model.get( 'name' ) || '';
+ return 'HistoryView(' + nameString + ')';
+ }
+});
+
+//------------------------------------------------------------------------------
+HistoryPanel.templates = {
+ historyPanel : Handlebars.templates[ 'template-history-historyPanel' ]
+};
+
+//==============================================================================
+//return {
+// HistoryPanel : HistoryPanel
+//};});
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 templates/root/alternate_history.mako
--- a/templates/root/alternate_history.mako
+++ b/templates/root/alternate_history.mako
@@ -222,97 +222,127 @@
"template-user-quotaMeter-usage"
)}
-##TODO: fix: curr hasta be _after_ h.templates - move somehow
+##TODO: fix: curr hasta be _after_ h.templates bc these use those templates - move somehow
${h.js(
- "mvc/history",
+ "mvc/dataset/hda-model", "mvc/dataset/hda-edit",
+ "mvc/history/history-model", "mvc/history/history-panel",
+
##"mvc/tags", "mvc/annotations",
"mvc/user/user-model", "mvc/user/user-quotameter"
)}
<script type="text/javascript">
+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' );
+
+ //modals
+ // other base functions
+
+ // global backbone models
+ top.Galaxy.currUser = top.Galaxy.currUser;
+ top.Galaxy.currHistoryPanel = top.Galaxy.currHistoryPanel;
+ top.Galaxy.historyPanels = top.Galaxy.historyPanels || [];
+
+ top.Galaxy.paths = galaxy_paths;
+
+ window.Galaxy = top.Galaxy;
+}
+
// set js localizable strings
GalaxyLocalization.setLocalizedString( ${ create_localization_json( get_page_localized_strings() ) } );
// add needed controller urls to GalaxyPaths
galaxy_paths.set( 'hda', ${get_hda_url_templates()} );
galaxy_paths.set( 'history', ${get_history_url_templates()} );
+//console.debug( 'galaxy_paths:', galaxy_paths );
$(function(){
+ galaxyPageSetUp();
+ Galaxy.historyFrame = window;
// ostensibly, this is the App
if( console && console.debug ){
//if( console.clear ){ console.clear(); }
- console.debug( 'using backbone.js in history panel' );
console.pretty = function( o ){ $( '<pre/>' ).text( JSON.stringify( o, null, ' ' ) ).appendTo( 'body' ); }
+ top.storage = jQuery.jStorage
}
- // load initial data in this page - since we're already sending it...
+ // LOAD INITIAL DATA IN THIS PAGE - since we're already sending it...
+ // ...use mako to 'bootstrap' the models
var user = ${ get_current_user() },
history = ${ get_history( history.id ) },
hdas = ${ get_hdas( history.id, datasets ) };
//console.debug( 'user:', user );
//console.debug( 'history:', history );
//console.debug( 'hdas:', hdas );
+ var currUser = new User( user );
+ if( !Galaxy.currUser ){ Galaxy.currUser = currUser; }
- // i don't like this relationship, but user authentication changes views/behaviour
+ // add user data to history
+ // i don't like this history+user relationship, but user authentication changes views/behaviour
history.user = user;
+ // is page sending in show settings? if so override history's
+ //TODO: move into historyPanel
history.show_deleted = ${ 'true' if show_deleted else 'false' };
history.show_hidden = ${ 'true' if show_hidden else 'false' };
- //console.debug( 'galaxy_paths:', galaxy_paths );
- var glx_history = new History( history, hdas );
- glx_history.logger = console;
-
- var glx_history_view = new HistoryView({
- model: glx_history,
+ var historyPanel = new HistoryPanel({
+ model : new History( history, hdas ),
urlTemplates: galaxy_paths.attributes,
- logger: console
+ logger : console
});
- glx_history_view.render();
-
-
- // ...OR load from the api
- //var glx_history = new History().setPaths( galaxy_paths ),
- // glx_history_view = new HistoryView({ model: glx_history });
- //console.warn( 'fetching' );
- //glx_history.loadFromApi( pageData.history.id );
+ historyPanel.render();
+ if( !Galaxy.currHistoryPanel ){ Galaxy.currHistoryPanel = historyPanel; }
+ if( !( historyPanel in Galaxy.historyPanels ) ){ Galaxy.historyPanels.unshift( historyPanel ); }
- // quota meter is a cross-frame ui element (meter in masthead, over quota message in history)
+ // ...or LOAD FROM THE API
+ //historyPanel = new HistoryView({ model: new History().setPaths( galaxy_paths ) });
+ //historyPanel.loadFromApi( pageData.history.id );
+
+
+ // QUOTA METER is a cross-frame ui element (meter in masthead, over quota message in history)
// create it and join them here for now (via events)
- window.currUser = new User( user );
+ //TODO: this really belongs in the masthead
+
//window.currUser.logger = console;
- window.quotaMeter = new UserQuotaMeter({ model: currUser, el: $( top.document ).find( '.quota-meter-container' ) });
- window.quotaMeter.render();
- //window.quotaMeter.logger = console;
+ var quotaMeter = new UserQuotaMeter({
+ model : currUser,
+ el : $( top.document ).find( '.quota-meter-container' )
+ });
+ //quotaMeter.logger = console; window.quotaMeter = quotaMeter
+ quotaMeter.render();
// show/hide the 'over quota message' in the history when the meter tells it to
- quotaMeter.bind( 'quota:over', glx_history_view.showQuotaMessage, glx_history_view );
- quotaMeter.bind( 'quota:under', glx_history_view.hideQuotaMessage, glx_history_view );
+ quotaMeter.bind( 'quota:over', historyPanel.showQuotaMessage, historyPanel );
+ quotaMeter.bind( 'quota:under', historyPanel.hideQuotaMessage, historyPanel );
// having to add this to handle re-render of hview while overquota (the above do not fire)
- glx_history_view.on( 'rendered', function(){
- if( window.quotaMeter.isOverQuota() ){
- glx_history_view.showQuotaMessage();
+ historyPanel.on( 'rendered', function(){
+ if( quotaMeter.isOverQuota() ){
+ historyPanel.showQuotaMessage();
}
});
//TODO: this _is_ sent to the page (over_quota)...
// update the quota meter when current history changes size
- glx_history.bind( 'change:nice_size', function(){
- window.quotaMeter.update()
- }, window.quotaMeter );
+ historyPanel.model.bind( 'change:nice_size', function(){
+ quotaMeter.update()
+ }, quotaMeter );
- if( console && console.debug ){
- window.user = top.user = user;
- window._history = top._history = history;
- window.hdas = top.hdas = hdas;
- window.glx_history = top.glx_history = glx_history;
- window.glx_history_view = top.glx_history_view = glx_history_view;
- top.storage = jQuery.jStorage
- }
-
return;
});
</script>
https://bitbucket.org/galaxy/galaxy-central/changeset/191126557331/
changeset: 191126557331
user: carlfeberhard
date: 2012-11-06 21:58:25
summary: merge and pack scripts
affected #: 8 files
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -400,7 +400,6 @@
# the state or whether the tool used exit codes and regular
# expressions to do so. So we use
# job.state == job.states.ERROR to replace this same test.
- #elif not self.external_output_metadata.external_metadata_set_successfully( dataset, self.sa_session ) and not context['stderr']:
elif not self.external_output_metadata.external_metadata_set_successfully( dataset, self.sa_session ) and job.states.ERROR != job.state:
dataset._state = model.Dataset.states.FAILED_METADATA
else:
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/jobs/runners/local.py
--- a/lib/galaxy/jobs/runners/local.py
+++ b/lib/galaxy/jobs/runners/local.py
@@ -183,8 +183,9 @@
def stop_job( self, job ):
#if our local job has JobExternalOutputMetadata associated, then our primary job has to have already finished
- if job.get_external_output_metadata():
- pid = job.get_external_output_metadata()[0].job_runner_external_pid #every JobExternalOutputMetadata has a pid set, we just need to take from one of them
+ job_ext_output_metadata = job.get_external_output_metadata()
+ if job_ext_output_metadata:
+ pid = job_ext_output_metadata[0].job_runner_external_pid #every JobExternalOutputMetadata has a pid set, we just need to take from one of them
else:
pid = job.get_job_runner_external_id()
if pid in [ None, '' ]:
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -18,7 +18,7 @@
from galaxy.model.item_attrs import UsesAnnotations, APIItem
from sqlalchemy.orm import object_session
from sqlalchemy.sql.expression import func
-import sys, os.path, os, errno, codecs, operator, socket, pexpect, logging, time, shutil
+import os.path, os, errno, codecs, operator, socket, pexpect, logging, time, shutil
if sys.version_info[:2] < ( 2, 5 ):
from sets import Set as set
@@ -138,6 +138,12 @@
# TODO: Add accessors for members defined in SQL Alchemy for the Job table and
# for the mapper defined to the Job table.
+ def get_external_output_metadata( self ):
+ """
+ The external_output_metadata is currently a reference from Job to
+ JobExternalOutputMetadata. It exists for a job but not a task.
+ """
+ return self.external_output_metadata
def get_session_id( self ):
return self.session_id
def get_user_id( self ):
@@ -370,6 +376,13 @@
# (e.g., for a session) or never use the member (e.g., external output
# metdata). These can be filled in as needed.
def get_external_output_metadata( self ):
+ """
+ The external_output_metadata is currently a backref to
+ JobExternalOutputMetadata. It exists for a job but not a task,
+ and when a task is cancelled its corresponding parent Job will
+ be cancelled. So None is returned now, but that could be changed
+ to self.get_job().get_external_output_metadata().
+ """
return None
def get_job_runner_name( self ):
"""
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/tool_shed/tool_dependencies/common_util.py
--- a/lib/galaxy/tool_shed/tool_dependencies/common_util.py
+++ b/lib/galaxy/tool_shed/tool_dependencies/common_util.py
@@ -19,6 +19,14 @@
else:
env_var_text = elem.text.replace( '$INSTALL_DIR', tool_shed_repository_install_dir )
return dict( name=env_var_name, action=env_var_action, value=env_var_text )
+ if elem.text:
+ # Allow for environment variables that contain neither REPOSITORY_INSTALL_DIR nor INSTALL_DIR since there may be command line
+ # parameters that are tuned for a Galaxy instance. Allowing them to be set in one location rather than being hard coded into
+ # each tool config is the best approach. For example:
+ # <environment_variable name="GATK2_SITE_OPTIONS" action="set_to">
+ # "--num_threads 4 --num_cpu_threads_per_data_thread 3 --phone_home STANDARD"
+ # </environment_variable>
+ return dict( name=env_var_name, action=env_var_action, value=elem.text)
return None
def create_or_update_env_shell_file( install_dir, env_var_dict ):
env_var_name = env_var_dict[ 'name' ]
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -1731,7 +1731,7 @@
callback( "", input, value[input.name] )
else:
input.visit_inputs( "", value[input.name], callback )
- def handle_input( self, trans, incoming, history=None ):
+ def handle_input( self, trans, incoming, history=None, old_errors=None ):
"""
Process incoming parameters for this tool from the dict `incoming`,
update the tool state (or create if none existed), and either return
@@ -1766,7 +1766,7 @@
else:
# Update state for all inputs on the current page taking new
# values from `incoming`.
- errors = self.update_state( trans, self.inputs_by_page[state.page], state.inputs, incoming )
+ errors = self.update_state( trans, self.inputs_by_page[state.page], state.inputs, incoming, old_errors=old_errors or {} )
# If the tool provides a `validate_input` hook, call it.
validate_input = self.get_hook( 'validate_input' )
if validate_input:
@@ -1895,7 +1895,10 @@
any_group_errors = True
# Only need to find one that can't be removed due to size, since only
# one removal is processed at # a time anyway
- break
+ break
+ elif group_old_errors and group_old_errors[i]:
+ group_errors[i] = group_old_errors[i]
+ any_group_errors = True
# Update state
max_index = -1
for i, rep_state in enumerate( group_state ):
@@ -1978,6 +1981,8 @@
update_only=update_only,
old_errors=group_old_errors,
item_callback=item_callback )
+ if input.test_param.name in group_old_errors and not test_param_error:
+ test_param_error = group_old_errors[ input.test_param.name ]
if test_param_error:
group_errors[ input.test_param.name ] = test_param_error
if group_errors:
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/tools/parameters/__init__.py
--- a/lib/galaxy/tools/parameters/__init__.py
+++ b/lib/galaxy/tools/parameters/__init__.py
@@ -94,3 +94,26 @@
value = params[key].value_from_basic( value, app, ignore_errors )
rval[ key ] = value
return rval
+
+def params_to_incoming( incoming, inputs, input_values, app, name_prefix="" ):
+ """
+ Given a tool's parameter definition (`inputs`) and a specific set of
+ parameter `input_values` objects, populate `incoming` with the html values.
+
+ Useful for e.g. the rerun function.
+ """
+ for input in inputs.itervalues():
+ if isinstance( input, Repeat ) or isinstance( input, UploadDataset ):
+ for i, d in enumerate( input_values[ input.name ] ):
+ index = d['__index__']
+ new_name_prefix = name_prefix + "%s_%d|" % ( input.name, index )
+ params_to_incoming( incoming, input.inputs, d, app, new_name_prefix )
+ elif isinstance( input, Conditional ):
+ values = input_values[ input.name ]
+ current = values["__current_case__"]
+ new_name_prefix = name_prefix + input.name + "|"
+ incoming[ new_name_prefix + input.test_param.name ] = values[ input.test_param.name ]
+ params_to_incoming( incoming, input.cases[current].inputs, values, app, new_name_prefix )
+ else:
+ incoming[ name_prefix + input.name ] = input.to_string( input_values.get( input.name ), app )
+
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -1539,25 +1539,38 @@
if trans.workflow_building_mode:
return None
if not value:
- raise ValueError( "History does not include a dataset of the required format / build" )
+ raise ValueError( "History does not include a dataset of the required format / build" )
if value in [None, "None"]:
return None
if isinstance( value, list ):
- return [ trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( v ) for v in value ]
+ rval = [ trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( v ) for v in value ]
elif isinstance( value, trans.app.model.HistoryDatasetAssociation ):
- return value
+ rval = value
else:
- return trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( value )
+ rval = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( value )
+ if isinstance( rval, list ):
+ values = rval
+ else:
+ values = [ rval ]
+ for v in values:
+ if v:
+ if v.deleted:
+ raise ValueError( "The previously selected dataset has been previously deleted" )
+ if v.dataset.state in [galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED ]:
+ raise ValueError( "The previously selected dataset has entered an unusable state" )
+ return rval
def to_string( self, value, app ):
- if value is None or isinstance( value, str ):
+ if value is None or isinstance( value, basestring ):
return value
+ elif isinstance( value, int ):
+ return str( value )
elif isinstance( value, DummyDataset ):
return None
elif isinstance( value, list) and len(value) > 0 and isinstance( value[0], DummyDataset):
return None
elif isinstance( value, list ):
- return ",".join( [ val if isinstance( val, str ) else str(val.id) for val in value] )
+ return ",".join( [ val if isinstance( val, basestring ) else str(val.id) for val in value] )
return value.id
def to_python( self, value, app ):
diff -r fdea658292c9d5d3b42aaac5dc982708ecacabf9 -r 1911265573315cf71962ac1ae5ca6ba513d051b2 lib/galaxy/webapps/galaxy/controllers/tool_runner.py
--- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
+++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
@@ -6,6 +6,7 @@
from galaxy.util.bunch import Bunch
from galaxy.tools import DefaultToolState
from galaxy.tools.parameters.basic import UnvalidatedValue
+from galaxy.tools.parameters import params_to_incoming
from galaxy.tools.actions import upload_common
import logging
@@ -192,25 +193,29 @@
if isinstance(value,list):
values = []
for val in value:
- if val not in history.datasets and val in hda_source_dict:
+ if val in history.datasets:
+ values.append( val )
+ elif val in hda_source_dict:
values.append( hda_source_dict[ val ])
return values
if value not in history.datasets and value in hda_source_dict:
return hda_source_dict[ value ]
visit_input_values( tool.inputs, params_objects, rerun_callback )
- # Create a fake tool_state for the tool, with the parameters values
+ # Create a fake tool_state for the tool, with the parameters values
state = tool.new_state( trans )
state.inputs = params_objects
- tool_state_string = util.object_to_string(state.encode(tool, trans.app))
- # Setup context for template
- vars = dict( tool_state=state, errors = upgrade_messages )
+ #create an incoming object from the original job's dataset-modified param objects
+ incoming = {}
+ params_to_incoming( incoming, tool.inputs, params_objects, trans.app )
+ incoming[ "tool_state" ] = util.object_to_string( state.encode( tool, trans.app ) )
+ template, vars = tool.handle_input( trans, incoming, old_errors=upgrade_messages ) #update new state with old parameters
# Is the "add frame" stuff neccesary here?
add_frame = AddFrameData()
add_frame.debug = trans.debug
if from_noframe is not None:
add_frame.wiki_url = trans.app.config.wiki_url
add_frame.from_noframe = True
- return trans.fill_template( "tool_form.mako",
+ return trans.fill_template( template,
history=history,
toolbox=self.get_toolbox(),
tool_version_select_field=tool_version_select_field,
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
4 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/67da1098be88/
changeset: 67da1098be88
user: dan
date: 2012-11-06 21:46:57
summary: Add a helper menthol "params_to_incoming" that takes a set of parameters and populates a dictionary as if it were an incoming html post.
affected #: 1 file
diff -r aba101adc4a7012f6c959c23ea5f1b8701667649 -r 67da1098be8881f223f469c53ef7dc3528409343 lib/galaxy/tools/parameters/__init__.py
--- a/lib/galaxy/tools/parameters/__init__.py
+++ b/lib/galaxy/tools/parameters/__init__.py
@@ -94,3 +94,26 @@
value = params[key].value_from_basic( value, app, ignore_errors )
rval[ key ] = value
return rval
+
+def params_to_incoming( incoming, inputs, input_values, app, name_prefix="" ):
+ """
+ Given a tool's parameter definition (`inputs`) and a specific set of
+ parameter `input_values` objects, populate `incoming` with the html values.
+
+ Useful for e.g. the rerun function.
+ """
+ for input in inputs.itervalues():
+ if isinstance( input, Repeat ) or isinstance( input, UploadDataset ):
+ for i, d in enumerate( input_values[ input.name ] ):
+ index = d['__index__']
+ new_name_prefix = name_prefix + "%s_%d|" % ( input.name, index )
+ params_to_incoming( incoming, input.inputs, d, app, new_name_prefix )
+ elif isinstance( input, Conditional ):
+ values = input_values[ input.name ]
+ current = values["__current_case__"]
+ new_name_prefix = name_prefix + input.name + "|"
+ incoming[ new_name_prefix + input.test_param.name ] = values[ input.test_param.name ]
+ params_to_incoming( incoming, input.cases[current].inputs, values, app, new_name_prefix )
+ else:
+ incoming[ name_prefix + input.name ] = input.to_string( input_values.get( input.name ), app )
+
https://bitbucket.org/galaxy/galaxy-central/changeset/50513229f6ef/
changeset: 50513229f6ef
user: dan
date: 2012-11-06 21:46:57
summary: Allow passing old_errors to handle_input. Fixes for handling old_errors in handle_input for grouping parameters.
affected #: 1 file
diff -r 67da1098be8881f223f469c53ef7dc3528409343 -r 50513229f6ef8338361d838a40c956108836465d lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -1731,7 +1731,7 @@
callback( "", input, value[input.name] )
else:
input.visit_inputs( "", value[input.name], callback )
- def handle_input( self, trans, incoming, history=None ):
+ def handle_input( self, trans, incoming, history=None, old_errors=None ):
"""
Process incoming parameters for this tool from the dict `incoming`,
update the tool state (or create if none existed), and either return
@@ -1766,7 +1766,7 @@
else:
# Update state for all inputs on the current page taking new
# values from `incoming`.
- errors = self.update_state( trans, self.inputs_by_page[state.page], state.inputs, incoming )
+ errors = self.update_state( trans, self.inputs_by_page[state.page], state.inputs, incoming, old_errors=old_errors or {} )
# If the tool provides a `validate_input` hook, call it.
validate_input = self.get_hook( 'validate_input' )
if validate_input:
@@ -1895,7 +1895,10 @@
any_group_errors = True
# Only need to find one that can't be removed due to size, since only
# one removal is processed at # a time anyway
- break
+ break
+ elif group_old_errors and group_old_errors[i]:
+ group_errors[i] = group_old_errors[i]
+ any_group_errors = True
# Update state
max_index = -1
for i, rep_state in enumerate( group_state ):
@@ -1978,6 +1981,8 @@
update_only=update_only,
old_errors=group_old_errors,
item_callback=item_callback )
+ if input.test_param.name in group_old_errors and not test_param_error:
+ test_param_error = group_old_errors[ input.test_param.name ]
if test_param_error:
group_errors[ input.test_param.name ] = test_param_error
if group_errors:
https://bitbucket.org/galaxy/galaxy-central/changeset/81a007dbc152/
changeset: 81a007dbc152
user: dan
date: 2012-11-06 21:46:57
summary: Rework rerun functionality to treat the previously set job parameters as though they are an incoming form post. This allows validation and subsequent display of errors between the original and current states.
affected #: 1 file
diff -r 50513229f6ef8338361d838a40c956108836465d -r 81a007dbc1528a6124dfdc9caf05579887fe0d4c lib/galaxy/webapps/galaxy/controllers/tool_runner.py
--- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
+++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
@@ -6,6 +6,7 @@
from galaxy.util.bunch import Bunch
from galaxy.tools import DefaultToolState
from galaxy.tools.parameters.basic import UnvalidatedValue
+from galaxy.tools.parameters import params_to_incoming
from galaxy.tools.actions import upload_common
import logging
@@ -192,25 +193,29 @@
if isinstance(value,list):
values = []
for val in value:
- if val not in history.datasets and val in hda_source_dict:
+ if val in history.datasets:
+ values.append( val )
+ elif val in hda_source_dict:
values.append( hda_source_dict[ val ])
return values
if value not in history.datasets and value in hda_source_dict:
return hda_source_dict[ value ]
visit_input_values( tool.inputs, params_objects, rerun_callback )
- # Create a fake tool_state for the tool, with the parameters values
+ # Create a fake tool_state for the tool, with the parameters values
state = tool.new_state( trans )
state.inputs = params_objects
- tool_state_string = util.object_to_string(state.encode(tool, trans.app))
- # Setup context for template
- vars = dict( tool_state=state, errors = upgrade_messages )
+ #create an incoming object from the original job's dataset-modified param objects
+ incoming = {}
+ params_to_incoming( incoming, tool.inputs, params_objects, trans.app )
+ incoming[ "tool_state" ] = util.object_to_string( state.encode( tool, trans.app ) )
+ template, vars = tool.handle_input( trans, incoming, old_errors=upgrade_messages ) #update new state with old parameters
# Is the "add frame" stuff neccesary here?
add_frame = AddFrameData()
add_frame.debug = trans.debug
if from_noframe is not None:
add_frame.wiki_url = trans.app.config.wiki_url
add_frame.from_noframe = True
- return trans.fill_template( "tool_form.mako",
+ return trans.fill_template( template,
history=history,
toolbox=self.get_toolbox(),
tool_version_select_field=tool_version_select_field,
https://bitbucket.org/galaxy/galaxy-central/changeset/907f364107c5/
changeset: 907f364107c5
user: dan
date: 2012-11-06 21:46:58
summary: Add error messages for a DataToolParameter when the provided value is no longer valid due to be deleted or being in an error state.
affected #: 1 file
diff -r 81a007dbc1528a6124dfdc9caf05579887fe0d4c -r 907f364107c534cd531b4d91fcda7fe3e59eb4b1 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -1539,25 +1539,38 @@
if trans.workflow_building_mode:
return None
if not value:
- raise ValueError( "History does not include a dataset of the required format / build" )
+ raise ValueError( "History does not include a dataset of the required format / build" )
if value in [None, "None"]:
return None
if isinstance( value, list ):
- return [ trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( v ) for v in value ]
+ rval = [ trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( v ) for v in value ]
elif isinstance( value, trans.app.model.HistoryDatasetAssociation ):
- return value
+ rval = value
else:
- return trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( value )
+ rval = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( value )
+ if isinstance( rval, list ):
+ values = rval
+ else:
+ values = [ rval ]
+ for v in values:
+ if v:
+ if v.deleted:
+ raise ValueError( "The previously selected dataset has been previously deleted" )
+ if v.dataset.state in [galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED ]:
+ raise ValueError( "The previously selected dataset has entered an unusable state" )
+ return rval
def to_string( self, value, app ):
- if value is None or isinstance( value, str ):
+ if value is None or isinstance( value, basestring ):
return value
+ elif isinstance( value, int ):
+ return str( value )
elif isinstance( value, DummyDataset ):
return None
elif isinstance( value, list) and len(value) > 0 and isinstance( value[0], DummyDataset):
return None
elif isinstance( value, list ):
- return ",".join( [ val if isinstance( val, str ) else str(val.id) for val in value] )
+ return ",".join( [ val if isinstance( val, basestring ) else str(val.id) for val in value] )
return value.id
def to_python( self, value, app ):
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
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/aba101adc4a7/
changeset: aba101adc4a7
user: smcmanus
date: 2012-11-06 21:18:42
summary: The job's external metadata is returned from the model. The Task class had included a get_external_output_metadata method that was missing from the Job class, and this was the result of trying to merge the two interfaces. This caused problems in cancelling jobs (i.e., the jobs would run to completion) when the jobs were scheduled for the local runner.
affected #: 3 files
diff -r 8fb2a905f2492a388799ac4d861e52bda9365300 -r aba101adc4a7012f6c959c23ea5f1b8701667649 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -400,7 +400,6 @@
# the state or whether the tool used exit codes and regular
# expressions to do so. So we use
# job.state == job.states.ERROR to replace this same test.
- #elif not self.external_output_metadata.external_metadata_set_successfully( dataset, self.sa_session ) and not context['stderr']:
elif not self.external_output_metadata.external_metadata_set_successfully( dataset, self.sa_session ) and job.states.ERROR != job.state:
dataset._state = model.Dataset.states.FAILED_METADATA
else:
diff -r 8fb2a905f2492a388799ac4d861e52bda9365300 -r aba101adc4a7012f6c959c23ea5f1b8701667649 lib/galaxy/jobs/runners/local.py
--- a/lib/galaxy/jobs/runners/local.py
+++ b/lib/galaxy/jobs/runners/local.py
@@ -183,8 +183,9 @@
def stop_job( self, job ):
#if our local job has JobExternalOutputMetadata associated, then our primary job has to have already finished
- if job.get_external_output_metadata():
- pid = job.get_external_output_metadata()[0].job_runner_external_pid #every JobExternalOutputMetadata has a pid set, we just need to take from one of them
+ job_ext_output_metadata = job.get_external_output_metadata()
+ if job_ext_output_metadata:
+ pid = job_ext_output_metadata[0].job_runner_external_pid #every JobExternalOutputMetadata has a pid set, we just need to take from one of them
else:
pid = job.get_job_runner_external_id()
if pid in [ None, '' ]:
diff -r 8fb2a905f2492a388799ac4d861e52bda9365300 -r aba101adc4a7012f6c959c23ea5f1b8701667649 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -18,7 +18,7 @@
from galaxy.model.item_attrs import UsesAnnotations, APIItem
from sqlalchemy.orm import object_session
from sqlalchemy.sql.expression import func
-import sys, os.path, os, errno, codecs, operator, socket, pexpect, logging, time, shutil
+import os.path, os, errno, codecs, operator, socket, pexpect, logging, time, shutil
if sys.version_info[:2] < ( 2, 5 ):
from sets import Set as set
@@ -138,6 +138,12 @@
# TODO: Add accessors for members defined in SQL Alchemy for the Job table and
# for the mapper defined to the Job table.
+ def get_external_output_metadata( self ):
+ """
+ The external_output_metadata is currently a reference from Job to
+ JobExternalOutputMetadata. It exists for a job but not a task.
+ """
+ return self.external_output_metadata
def get_session_id( self ):
return self.session_id
def get_user_id( self ):
@@ -370,6 +376,13 @@
# (e.g., for a session) or never use the member (e.g., external output
# metdata). These can be filled in as needed.
def get_external_output_metadata( self ):
+ """
+ The external_output_metadata is currently a backref to
+ JobExternalOutputMetadata. It exists for a job but not a task,
+ and when a task is cancelled its corresponding parent Job will
+ be cancelled. So None is returned now, but that could be changed
+ to self.get_job().get_external_output_metadata().
+ """
return None
def get_job_runner_name( self ):
"""
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/8fb2a905f249/
changeset: 8fb2a905f249
user: greg
date: 2012-11-06 21:09:34
summary: Per James Johnson, allow for environment variables that contain neither REPOSITORY_INSTALL_DIR nor INSTALL_DIR when defining tool dependencies to be installed along with tool shed repositories.
affected #: 1 file
diff -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 -r 8fb2a905f2492a388799ac4d861e52bda9365300 lib/galaxy/tool_shed/tool_dependencies/common_util.py
--- a/lib/galaxy/tool_shed/tool_dependencies/common_util.py
+++ b/lib/galaxy/tool_shed/tool_dependencies/common_util.py
@@ -19,6 +19,14 @@
else:
env_var_text = elem.text.replace( '$INSTALL_DIR', tool_shed_repository_install_dir )
return dict( name=env_var_name, action=env_var_action, value=env_var_text )
+ if elem.text:
+ # Allow for environment variables that contain neither REPOSITORY_INSTALL_DIR nor INSTALL_DIR since there may be command line
+ # parameters that are tuned for a Galaxy instance. Allowing them to be set in one location rather than being hard coded into
+ # each tool config is the best approach. For example:
+ # <environment_variable name="GATK2_SITE_OPTIONS" action="set_to">
+ # "--num_threads 4 --num_cpu_threads_per_data_thread 3 --phone_home STANDARD"
+ # </environment_variable>
+ return dict( name=env_var_name, action=env_var_action, value=elem.text)
return None
def create_or_update_env_shell_file( install_dir, env_var_dict ):
env_var_name = env_var_dict[ '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/changeset/79c884befb23/
changeset: 79c884befb23
user: carlfeberhard
date: 2012-11-06 20:07:21
summary: quota meter: fix multiple api calls on first history render; controllers/root.py: set up to move show_deleted/hidden into client
affected #: 5 files
diff -r 5dcbbdfe1087e8d75f1df939d363b347e2764dd5 -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b lib/galaxy/webapps/galaxy/controllers/root.py
--- a/lib/galaxy/webapps/galaxy/controllers/root.py
+++ b/lib/galaxy/webapps/galaxy/controllers/root.py
@@ -116,13 +116,23 @@
show_deleted=util.string_as_bool( show_deleted ),
show_hidden=util.string_as_bool( show_hidden ) )
else:
- show_deleted = show_purged = util.string_as_bool( show_deleted )
+ show_deleted = util.string_as_bool( show_deleted )
show_hidden = util.string_as_bool( show_hidden )
- datasets = self.get_history_datasets( trans, history, show_deleted, show_hidden, show_purged )
+ show_purged = util.string_as_bool( show_deleted )
+ datasets = []
history_panel_template = "root/history.mako"
- # history panel -> backbone
- #history_panel_template = "root/alternate_history.mako"
+
+ # history panel -> backbone (WIP - uncomment next to use)
+ #USE_ALTERNATE = True
+ if 'USE_ALTERNATE' in locals():
+ datasets = self.get_history_datasets( trans, history,
+ show_deleted=True, show_hidden=True, show_purged=True )
+ history_panel_template = "root/alternate_history.mako"
+
+ else:
+ datasets = self.get_history_datasets( trans, history, show_deleted, show_hidden, show_purged )
+
return trans.stream_template_mako( history_panel_template,
history = history,
annotation = self.get_item_annotation_str( trans.sa_session, trans.user, history ),
diff -r 5dcbbdfe1087e8d75f1df939d363b347e2764dd5 -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b static/scripts/mvc/history.js
--- a/static/scripts/mvc/history.js
+++ b/static/scripts/mvc/history.js
@@ -27,7 +27,7 @@
bug: quotaMeter bar rendering square in chrome
BUG: quotaMsg not showing when 100% (on load)
BUG: upload, history size, doesn't change
- TODO: on hdas state:final, update ONLY the size...from what? histories.py? in js?
+ TODO: on hdas state:ready, update ONLY the size...from what? histories.py? in js?
BUG: imported, shared history with unaccessible dataset errs in historycontents when getting history
(entire history is inaccessible)
??: still happening?
@@ -41,7 +41,7 @@
bug: loading hdas (alt_hist)
FIXED: added anon user api request ( trans.user == None and trans.history.id == requested id )
bug: quota meter not updating on upload/tool run
- FIXED: quotaMeter now listens for 'state:final' from glx_history in alternate_history.mako
+ FIXED: quotaMeter now listens for 'state:ready' from glx_history in alternate_history.mako
bug: use of new HDACollection with event listener in init doesn't die...keeps reporting
FIXED: change getVisible to return an array
BUG: history, broken intial hist state (running, updater, etc.)
@@ -134,6 +134,7 @@
// array of associated file types (eg. [ 'bam_index', ... ])
meta_files : [],
+
misc_blurb : '',
misc_info : '',
@@ -155,25 +156,32 @@
},
// (curr) only handles changing state of non-accessible hdas to STATES.NOT_VIEWABLE
- //TODO: use initialize (or validate) to check purged AND deleted -> purged XOR deleted
+ //TODO:? use initialize (or validate) to check purged AND deleted -> purged XOR deleted
initialize : function(){
this.log( this + '.initialize', this.attributes );
this.log( '\tparent history_id: ' + this.get( 'history_id' ) );
- //!! this state is not in trans.app.model.Dataset.states - set it here
+ //!! this state is not in trans.app.model.Dataset.states - set it here -
+ //TODO: change to server side.
if( !this.get( 'accessible' ) ){
this.set( 'state', HistoryDatasetAssociation.STATES.NOT_VIEWABLE );
}
+ // if the state has changed and the new state is a ready state, fire an event
+ this.on( 'change:state', function( currModel, newState ){
+ this.log( this + ' has changed state:', currModel, newState );
+ if( this.inReadyState() ){
+ this.trigger( 'state:ready', this.get( 'id' ), newState, this.previous( 'state' ), currModel );
+ }
+ });
+
+ // debug on change events
//this.on( 'change', function( currModel, changedList ){
// this.log( this + ' has changed:', currModel, changedList );
//});
- this.on( 'change:state', function( currModel, newState ){
- this.log( this + ' has changed state:', currModel, newState );
- if( this.inFinalState() ){
- this.trigger( 'state:final', currModel, newState, this.previous( 'state' ) );
- }
- });
+ //this.bind( 'all', function( event ){
+ // this.log( this + '', arguments );
+ //});
},
isDeletedOrPurged : function(){
@@ -195,8 +203,8 @@
return isVisible;
},
- // 'final' states are states where no processing (for the ds) is left to do on the server
- inFinalState : function(){
+ // 'ready' states are states where no processing (for the ds) is left to do on the server
+ inReadyState : function(){
var state = this.get( 'state' );
return (
( state === HistoryDatasetAssociation.STATES.NEW )
@@ -248,8 +256,8 @@
//logger : console,
initialize : function(){
- //this.bind( 'all', function( x, y, z ){
- // console.info( this + '', x, y, z );
+ //this.bind( 'all', function( event ){
+ // this.log( this + '', arguments );
//});
},
@@ -276,11 +284,11 @@
return stateLists;
},
- // returns the id of every hda still running (not in a final state)
+ // returns the id of every hda still running (not in a ready state)
running : function(){
var idList = [];
this.each( function( item ){
- if( !item.inFinalState() ){
+ if( !item.inReadyState() ){
idList.push( item.get( 'id' ) );
}
});
@@ -354,6 +362,9 @@
//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)
@@ -419,7 +430,9 @@
).success( function( response ){
//this.log( 'historyApiRequest, response:', response );
history.set( response );
- history.log( 'current history state:', history.get( 'state' ), '(was)', oldState );
+ history.log( 'current history state:', history.get( 'state' ),
+ '(was)', oldState,
+ 'new size:', history.get( 'nice_size' ) );
//TODO: revisit this - seems too elaborate, need something straightforward
// for each state, check for the difference between old dataset states and new
@@ -484,6 +497,9 @@
// re-render the entire view on any model change
this.model.bind( 'change', this.render, this );
+ //this.bind( 'all', function( event ){
+ // this.log( event );
+ //}, this );
},
// urlTemplates is a map (or nested map) of underscore templates (currently, anyhoo)
@@ -525,7 +541,10 @@
var view = this,
id = this.model.get( 'id' ),
state = this.model.get( 'state' ),
- itemWrapper = $( '<div/>' ).attr( 'id', 'historyItem-' + id );
+ itemWrapper = $( '<div/>' ).attr( 'id', 'historyItem-' + id ),
+ initialRender = ( this.$el.children().size() === 0 );
+
+ //console.debug( this + '.render, initial?:', initialRender );
this._clearReferences();
this.$el.attr( 'id', 'historyItemContainer-' + id );
@@ -551,10 +570,14 @@
view.$el.children().remove();
view.$el.append( itemWrapper ).fadeIn( 'fast', function(){
view.log( view + ' rendered:', view.$el );
- view.trigger( 'rendered' );
- if( view.model.inFinalState() ){
- view.trigger( 'rendered:final' );
+ var renderedEventName = 'rendered';
+
+ if( initialRender ){
+ renderedEventName += ':initial';
+ } else if( view.model.inReadyState() ){
+ renderedEventName += ':ready';
}
+ view.trigger( renderedEventName );
});
});
return this;
@@ -610,8 +633,8 @@
// icon-button to display this hda in the galaxy main iframe
_render_displayButton : function(){
- // don't show display if not in final state, error'd, or not accessible
- if( ( !this.model.inFinalState() )
+ // don't show display if not in ready state, error'd, or not accessible
+ if( ( !this.model.inReadyState() )
|| ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.ERROR )
|| ( this.model.get( 'state' ) === HistoryDatasetAssociation.STATES.NOT_VIEWABLE )
|| ( !this.model.get( 'accessible' ) ) ){
@@ -1335,13 +1358,18 @@
{ expandedHdas : {} }
);
+ // bind events from the model's hda collection
//this.model.bind( 'change', this.render, this );
+ this.model.bind( 'change:nice_size', this.updateHistoryDiskSize, this );
- // bind events from the model's hda collection
this.model.hdas.bind( 'add', this.add, this );
this.model.hdas.bind( 'reset', this.addAll, this );
this.model.hdas.bind( 'all', this.all, this );
+ //this.bind( 'all', function(){
+ // this.log( arguments );
+ //}, this );
+
// set up instance vars
this.hdaViews = {};
this.urls = {};
@@ -1379,7 +1407,10 @@
var historyView = this,
setUpQueueName = historyView.toString() + '.set-up',
newRender = $( '<div/>' ),
- modelJson = this.model.toJSON();
+ modelJson = this.model.toJSON(),
+ initialRender = ( this.$el.children().size() === 0 );
+
+ //console.debug( this + '.render, initialRender:', initialRender );
// render the urls and add them to the model json
modelJson.urls = this.renderUrls( modelJson );
@@ -1389,7 +1420,7 @@
newRender.append( HistoryView.templates.historyPanel( modelJson ) );
newRender.find( '.tooltip' ).tooltip();
- // render hda views (if any)
+ // render hda views (if any and any shown (show_deleted/hidden)
if( !this.model.hdas.length
|| !this.renderItems( newRender.find( '#' + this.model.get( 'id' ) + '-datasets' ) ) ){
// if history is empty or no hdas would be rendered, show the empty message
@@ -1413,7 +1444,12 @@
//TODO: ideally, these would be set up before the fade in (can't because of async save text)
historyView.setUpBehaviours();
- historyView.trigger( 'rendered' );
+ if( initialRender ){
+ historyView.trigger( 'rendered:initial' );
+
+ } else {
+ historyView.trigger( 'rendered' );
+ }
next();
});
$( historyView ).dequeue( setUpQueueName );
@@ -1459,8 +1495,7 @@
});
// rendering listeners
- //hdaView.bind( 'rendered', function(){});
- hdaView.bind( 'rendered:final', function(){ historyView.trigger( 'hda:rendered:final' ); });
+ hdaView.bind( 'rendered:ready', function(){ historyView.trigger( 'hda:rendered:ready' ); });
},
// set up js/widget behaviours: tooltips,
@@ -1488,6 +1523,11 @@
async_save_text( "history-annotation-container", "history-annotation",
this.urls.annotate, "new_annotation", 18, true, 4 );
},
+
+ // update the history size display (curr. upper right of panel)
+ updateHistoryDiskSize : function(){
+ this.$el.find( '#history-size' ).text( this.model.get( 'nice_size' ) );
+ },
//TODO: this seems more like a per user message than a history message; IOW, this doesn't belong here
showQuotaMessage : function( userData ){
diff -r 5dcbbdfe1087e8d75f1df939d363b347e2764dd5 -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b static/scripts/mvc/user/user-model.js
--- a/static/scripts/mvc/user/user-model.js
+++ b/static/scripts/mvc/user/user-model.js
@@ -22,9 +22,9 @@
options = options || {};
var model = this,
userFn = options.success;
- options.success = function( model, response ){
- model.trigger( 'loaded', model, response );
- if( userFn ){ userFn( model, response ); }
+ options.success = function( newModel, response ){
+ model.trigger( 'loaded', newModel, response );
+ if( userFn ){ userFn( newModel, response ); }
};
if( idOrCurrent === User.CURRENT_ID_STR ){
options.url = this.urlRoot + '/' + User.CURRENT_ID_STR;
@@ -41,14 +41,18 @@
return 'User(' + userInfo.join( ':' ) + ')';
}
});
+
+// string to send to tell server to return this transaction's user (see api/users.py)
User.CURRENT_ID_STR = 'current';
+// class method to load the current user via the api and return that model
User.getCurrentUserFromApi = function( options ){
var currentUser = new User();
currentUser.loadFromApi( User.CURRENT_ID_STR, options );
return currentUser;
};
+// (stub) collection for users (shouldn't be common unless admin UI)
var UserCollection = Backbone.Collection.extend( LoggableMixin ).extend({
model : User,
logger : console,
diff -r 5dcbbdfe1087e8d75f1df939d363b347e2764dd5 -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b static/scripts/mvc/user/user-quotameter.js
--- a/static/scripts/mvc/user/user-quotameter.js
+++ b/static/scripts/mvc/user/user-quotameter.js
@@ -5,7 +5,7 @@
// for now, keep the view in the history panel (where the message is), but render ALSO to the masthead
var UserQuotaMeter = BaseView.extend( LoggableMixin ).extend({
- logger : console,
+ //logger : console,
options : {
warnAtPercent : 85,
diff -r 5dcbbdfe1087e8d75f1df939d363b347e2764dd5 -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b templates/root/alternate_history.mako
--- a/templates/root/alternate_history.mako
+++ b/templates/root/alternate_history.mako
@@ -251,9 +251,9 @@
var user = ${ get_current_user() },
history = ${ get_history( history.id ) },
hdas = ${ get_hdas( history.id, datasets ) };
- console.debug( 'user:', user );
- console.debug( 'history:', history );
- console.debug( 'hdas:', hdas );
+ //console.debug( 'user:', user );
+ //console.debug( 'history:', history );
+ //console.debug( 'hdas:', hdas );
// i don't like this relationship, but user authentication changes views/behaviour
history.user = user;
@@ -296,10 +296,12 @@
glx_history_view.showQuotaMessage();
}
});
+ //TODO: this _is_ sent to the page (over_quota)...
- // update the quota meter when any hda reaches a 'final' state
- //NOTE: use an anon function or update will be passed the hda and think it's the options param
- glx_history_view.on( 'hda:rendered:final', function(){ window.quotaMeter.update({}) }, window.quotaMeter )
+ // update the quota meter when current history changes size
+ glx_history.bind( 'change:nice_size', function(){
+ window.quotaMeter.update()
+ }, window.quotaMeter );
if( console && console.debug ){
https://bitbucket.org/galaxy/galaxy-central/changeset/3d27b35e1c1f/
changeset: 3d27b35e1c1f
user: carlfeberhard
date: 2012-11-06 20:09:40
summary: pack scripts
affected #: 7 files
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/mvc/history.js
--- a/static/scripts/packed/mvc/history.js
+++ b/static/scripts/packed/mvc/history.js
@@ -1,1 +1,1 @@
-var HistoryDatasetAssociation=BaseModel.extend(LoggableMixin).extend({defaults:{history_id:null,model_class:"HistoryDatasetAssociation",hid:0,id:null,name:"",state:"",data_type:null,file_size:0,meta_files:[],misc_blurb:"",misc_info:"",deleted:false,purged:false,visible:false,accessible:false,for_editing:true},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",function(b,a,d,c){this.log(this+" has changed:",b,a,d,c)})},isDeletedOrPurged:function(){return(this.get("deleted")||this.get("purged"))},isEditable:function(){return(!this.isDeletedOrPurged())},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},inFinalState:function(){return((this.get("state")===HistoryDatasetAssociation.STATES.OK)||(this.get("state")===HistoryDatasetAssociation.STATES.FAILED_METADATA)||(this.get("state")===HistoryDatasetAssociation.STATES.EMPTY)||(this.get("state")===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("name")+'"'}return"HistoryDatasetAssociation("+a+")"}});HistoryDatasetAssociation.STATES={NOT_VIEWABLE:"noPermission",NEW:"new",UPLOAD:"upload",QUEUED:"queued",RUNNING:"running",OK:"ok",EMPTY:"empty",ERROR:"error",DISCARDED:"discarded",SETTING_METADATA:"setting_metadata",FAILED_METADATA:"failed_metadata"};var HDACollection=Backbone.Collection.extend(LoggableMixin).extend({model:HistoryDatasetAssociation,logger:console,ids:function(){return this.map(function(a){return a.id})},getVisible:function(a,b){return new HDACollection(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},update:function(a){this.log(this+"update:",a);if(!(a&&a.length)){return}var b=this;_.each(a,function(e,c){var d=b.get(e);d.fetch()})},toString:function(){return("HDACollection("+this.ids().join(",")+")")}});var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:"",name:"",state:"",show_deleted:false,show_hidden:false,diskSize:0,deleted:false,tags:[],annotation:null,message:null,quotaMsg:false},url:function(){return"api/histories/"+this.get("id")},initialize:function(a,b){this.log(this+".initialize:",a,b);this.hdas=new HDACollection();if(b&&b.length){this.hdas.reset(b);this.checkForUpdates()}this.on("change",function(d,c,f,e){this.log(this+" has changed:",d,c,f,e)})},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.log(b)}).then(function(){jQuery.ajax(b.url()+"/contents?"+jQuery.param({ids:b.itemIdsFromStateIds().join(",")})).success(function(d){b.hdas.reset(d);b.checkForUpdates();c()})})},hdaIdsFromStateIds:function(){return _.reduce(_.values(this.get("state_ids")),function(b,a){return b.concat(a)})},checkForUpdates:function(a){this.stateFromStateIds();if((this.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(this.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){this.stateUpdater()}return this},stateFromStateIds:function(){var a=this.hdas.getStateLists();this.attributes.state_ids=a;if((a.running.length>0)||(a.upload.length>0)||(a.setting_metadata.length>0)){this.set("state",HistoryDatasetAssociation.STATES.RUNNING)}else{if(a.queued.length>0){this.set("state",HistoryDatasetAssociation.STATES.QUEUED)}else{if((a.error.length>0)||(a.failed_metadata.length>0)){this.set("state",HistoryDatasetAssociation.STATES.ERROR)}else{if(a.ok.length===this.hdas.length){this.set("state",HistoryDatasetAssociation.STATES.OK)}else{throw (this+".stateFromStateDetails: unable to determine history state from hda states: "+this.get("state_ids"))}}}}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);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.hdas.update(e)}if((c.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(c.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){setTimeout(function(){c.stateUpdater()},4000)}}).error(function(f,d,e){if(console&&console.warn){console.warn("Error getting history updates from the server:",f,d,e)}alert("Error getting history updates from the server.\n"+e)})},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});var HDAView=BaseView.extend(LoggableMixin).extend({logger:console,tagName:"div",className:"historyItemContainer",initialize:function(a){this.log(this+".initialize:",a);if(!a.urlTemplates){throw ("HDAView needs urlTemplates on initialize")}this.urls=this.renderUrls(a.urlTemplates,this.model.toJSON());this.expanded=a.expanded||false;this.model.bind("change",this.render,this)},renderUrls:function(d,a){var b=this,c={};_.each(d,function(e,f){if(_.isObject(e)){c[f]=b.renderUrls(e,a)}else{if(f==="meta_download"){c[f]=b.renderMetaDownloadUrls(e,a)}else{c[f]=_.template(e,a)}}});return c},renderMetaDownloadUrls:function(b,a){return _.map(a.meta_files,function(c){return{url:_.template(b,{id:a.id,file_type:c.file_type}),file_type:c.file_type}})},render:function(){var b=this,d=this.model.get("id"),c=this.model.get("state"),a=$("<div/>").attr("id","historyItem-"+d);this._clearReferences();this.$el.attr("id","historyItemContainer-"+d);a.addClass("historyItemWrapper").addClass("historyItem").addClass("historyItem-"+c);a.append(this._render_warnings());a.append(this._render_titleBar());this.body=$(this._render_body());a.append(this.body);make_popup_menus(a);a.find(".tooltip").tooltip({placement:"bottom"});this.$el.fadeOut("fast",function(){b.$el.children().remove();b.$el.append(a).fadeIn("fast",function(){b.log(b+" rendered:",b.$el);b.trigger("rendered")})});return this},_clearReferences:function(){this.displayButton=null;this.editButton=null;this.deleteButton=null;this.errButton=null;this.showParamsButton=null;this.rerunButton=null;this.visualizationsButton=null;this.tagButton=null;this.annotateButton=null},_render_warnings:function(){return $(jQuery.trim(HDAView.templates.messages(_.extend(this.model.toJSON(),{urls:this.urls}))))},_render_titleBar:function(){var a=$('<div class="historyItemTitleBar" style="overflow: hidden"></div>');a.append(this._render_titleButtons());a.append('<span class="state-icon"></span>');a.append(this._render_titleLink());return a},_render_titleButtons:function(){var a=$('<div class="historyItemButtons"></div>');a.append(this._render_displayButton());a.append(this._render_editButton());a.append(this._render_deleteButton());return a},_render_displayButton:function(){if((!this.model.inFinalState())||(this.model.get("state")===HistoryDatasetAssociation.STATES.ERROR)||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var a={icon_class:"display"};if(this.model.get("purged")){a.enabled=false;a.title="Cannot display datasets removed from disk"}else{a.title="Display data in browser";a.href=this.urls.display}if(this.model.get("for_editing")){a.target="galaxy_main"}this.displayButton=new IconButtonView({model:new IconButton(a)});return this.displayButton.render().$el},_render_editButton:function(){if((this.model.get("state")===HistoryDatasetAssociation.STATES.UPLOAD)||(this.model.get("state")===HistoryDatasetAssociation.STATES.ERROR)||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))||(!this.model.get("for_editing"))){return null}var c=this.model.get("purged"),a=this.model.get("deleted"),b={title:"Edit attributes",href:this.urls.edit,target:"galaxy_main",icon_class:"edit"};if(a||c){b.enabled=false;if(c){b.title="Cannot edit attributes of datasets removed from disk"}else{if(a){b.title="Undelete dataset to edit attributes"}}}this.editButton=new IconButtonView({model:new IconButton(b)});return this.editButton.render().$el},_render_deleteButton:function(){if((!this.model.get("for_editing"))||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var a={title:"Delete",href:this.urls["delete"],id:"historyItemDeleter-"+this.model.get("id"),icon_class:"delete"};if(this.model.get("deleted")||this.model.get("purged")){a={title:"Dataset is already deleted",icon_class:"delete",enabled:false}}this.deleteButton=new IconButtonView({model:new IconButton(a)});return this.deleteButton.render().$el},_render_titleLink:function(){return $(jQuery.trim(HDAView.templates.titleLink(_.extend(this.model.toJSON(),{urls:this.urls}))))},_render_hdaSummary:function(){var a=_.extend(this.model.toJSON(),{urls:this.urls});if(this.model.get("metadata_dbkey")==="?"&&this.model.isEditable()){_.extend(a,{dbkey_unknown_and_editable:true})}return HDAView.templates.hdaSummary(a)},_render_primaryActionButtons:function(c){var b=$("<div/>").attr("id","primary-actions-"+this.model.get("id")),a=this;_.each(c,function(d){b.append(d.call(a))});return b},_render_downloadButton:function(){if(this.model.get("purged")){return null}var a=HDAView.templates.downloadLinks(_.extend(this.model.toJSON(),{urls:this.urls}));return $(a)},_render_errButton:function(){if((this.model.get("state")!==HistoryDatasetAssociation.STATES.ERROR)||(!this.model.get("for_editing"))){return null}this.errButton=new IconButtonView({model:new IconButton({title:"View or report this error",href:this.urls.report_error,target:"galaxy_main",icon_class:"bug"})});return this.errButton.render().$el},_render_showParamsButton:function(){this.showParamsButton=new IconButtonView({model:new IconButton({title:"View details",href:this.urls.show_params,target:"galaxy_main",icon_class:"information"})});return this.showParamsButton.render().$el},_render_rerunButton:function(){if(!this.model.get("for_editing")){return null}this.rerunButton=new IconButtonView({model:new IconButton({title:"Run this job again",href:this.urls.rerun,target:"galaxy_main",icon_class:"arrow-circle"})});return this.rerunButton.render().$el},_render_visualizationsButton:function(){var c=this.model.get("dbkey"),a=this.model.get("visualizations"),f=this.urls.visualization,d={},g={dataset_id:this.model.get("id"),hda_ldda:"hda"};if(!(this.model.hasData())||!(this.model.get("for_editing"))||!(a&&a.length)||!(f)){return null}this.visualizationsButton=new IconButtonView({model:new IconButton({title:"Visualize",href:f,icon_class:"chart_curve"})});var b=this.visualizationsButton.render().$el;b.addClass("visualize-icon");if(c){g.dbkey=c}function e(h){switch(h){case"trackster":return create_trackster_action_fn(f,g,c);case"scatterplot":return create_scatterplot_action_fn(f,g);default:return function(){window.parent.location=f+"/"+h+"?"+$.param(g)}}}if(a.length===1){b.attr("title",a[0]);b.click(e(a[0]))}else{_.each(a,function(i){var h=i.charAt(0).toUpperCase()+i.slice(1);d[h]=e(i)});make_popupmenu(b,d)}return b},_render_secondaryActionButtons:function(b){var c=$("<div/>"),a=this;c.attr("style","float: right;").attr("id","secondary-actions-"+this.model.get("id"));_.each(b,function(d){c.append(d.call(a))});return c},_render_tagButton:function(){if(!(this.model.hasData())||!(this.model.get("for_editing"))||(!this.urls.tags.get)){return null}this.tagButton=new IconButtonView({model:new IconButton({title:"Edit dataset tags",target:"galaxy_main",href:this.urls.tags.get,icon_class:"tags"})});return this.tagButton.render().$el},_render_annotateButton:function(){if(!(this.model.hasData())||!(this.model.get("for_editing"))||(!this.urls.annotation.get)){return null}this.annotateButton=new IconButtonView({model:new IconButton({title:"Edit dataset annotation",target:"galaxy_main",icon_class:"annotate"})});return this.annotateButton.render().$el},_render_displayApps:function(){if(!this.model.hasData()){return null}var a=$("<div/>").addClass("display-apps");if(!_.isEmpty(this.model.get("display_types"))){a.append(HDAView.templates.displayApps({displayApps:this.model.get("display_types")}))}if(!_.isEmpty(this.model.get("display_apps"))){a.append(HDAView.templates.displayApps({displayApps:this.model.get("display_apps")}))}return a},_render_tagArea:function(){if(!this.urls.tags.set){return null}return $(HDAView.templates.tagArea(_.extend(this.model.toJSON(),{urls:this.urls})))},_render_annotationArea:function(){if(!this.urls.annotation.get){return null}return $(HDAView.templates.annotationArea(_.extend(this.model.toJSON(),{urls:this.urls})))},_render_peek:function(){if(!this.model.get("peek")){return null}return $("<div/>").append($("<pre/>").attr("id","peek"+this.model.get("id")).addClass("peek").append(this.model.get("peek")))},_render_body_not_viewable:function(a){a.append($("<div>You do not have permission to view dataset.</div>"))},_render_body_uploading:function(a){a.append($("<div>Dataset is uploading</div>"))},_render_body_queued:function(a){a.append($("<div>Job is waiting to run.</div>"));a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_running:function(a){a.append("<div>Job is currently running.</div>");a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_error:function(a){if(!this.model.get("purged")){a.append($("<div>"+this.model.get("misc_blurb")+"</div>"))}a.append(("An error occurred running this job: <i>"+$.trim(this.model.get("misc_info"))+"</i>"));a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_errButton,this._render_showParamsButton,this._render_rerunButton]))},_render_body_discarded:function(a){a.append("<div>The job creating this dataset was cancelled before completion.</div>");a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_setting_metadata:function(a){a.append($("<div>Metadata is being auto-detected.</div>"))},_render_body_empty:function(a){a.append($("<div>No data: <i>"+this.model.get("misc_blurb")+"</i></div>"));a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_failed_metadata:function(a){a.append($(HDAView.templates.failedMetadata(this.model.toJSON())));this._render_body_ok(a)},_render_body_ok:function(a){a.append(this._render_hdaSummary());if(this.model.isDeletedOrPurged()){a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_showParamsButton,this._render_rerunButton]));return}a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_errButton,this._render_showParamsButton,this._render_rerunButton,this._render_visualizationsButton]));a.append(this._render_secondaryActionButtons([this._render_tagButton,this._render_annotateButton]));a.append('<div class="clear"/>');a.append(this._render_tagArea());a.append(this._render_annotationArea());a.append(this._render_displayApps());a.append(this._render_peek())},_render_body:function(){var a=$("<div/>").attr("id","info-"+this.model.get("id")).addClass("historyItemBody").attr("style","display: block");switch(this.model.get("state")){case HistoryDatasetAssociation.STATES.NOT_VIEWABLE:this._render_body_not_viewable(a);break;case HistoryDatasetAssociation.STATES.UPLOAD:this._render_body_uploading(a);break;case HistoryDatasetAssociation.STATES.QUEUED:this._render_body_queued(a);break;case HistoryDatasetAssociation.STATES.RUNNING:this._render_body_running(a);break;case HistoryDatasetAssociation.STATES.ERROR:this._render_body_error(a);break;case HistoryDatasetAssociation.STATES.DISCARDED:this._render_body_discarded(a);break;case HistoryDatasetAssociation.STATES.SETTING_METADATA:this._render_body_setting_metadata(a);break;case HistoryDatasetAssociation.STATES.EMPTY:this._render_body_empty(a);break;case HistoryDatasetAssociation.STATES.FAILED_METADATA:this._render_body_failed_metadata(a);break;case HistoryDatasetAssociation.STATES.OK:this._render_body_ok(a);break;default:a.append($('<div>Error: unknown dataset state "'+state+'".</div>'))}a.append('<div style="clear: both"></div>');if(this.expanded){a.show()}else{a.hide()}return a},events:{"click .historyItemTitle":"toggleBodyVisibility","click a.icon-button.tags":"loadAndDisplayTags","click a.icon-button.annotate":"loadAndDisplayAnnotation"},loadAndDisplayTags:function(b){this.log(this+".loadAndDisplayTags",b);var c=this.$el.find(".tag-area"),a=c.find(".tag-elt");if(c.is(":hidden")){if(!jQuery.trim(a.html())){$.ajax({url:this.urls.tags.get,error:function(){alert("Tagging failed")},success:function(d){a.html(d);a.find(".tooltip").tooltip();c.slideDown("fast")}})}else{c.slideDown("fast")}}else{c.slideUp("fast")}return false},loadAndDisplayAnnotation:function(b){this.log(this+".loadAndDisplayAnnotation",b);var d=this.$el.find(".annotation-area"),c=d.find(".annotation-elt"),a=this.urls.annotation.set;if(d.is(":hidden")){if(!jQuery.trim(c.html())){$.ajax({url:this.urls.annotation.get,error:function(){alert("Annotations failed")},success:function(e){if(e===""){e="<em>Describe or add notes to dataset</em>"}c.html(e);d.find(".tooltip").tooltip();async_save_text(c.attr("id"),c.attr("id"),a,"new_annotation",18,true,4);d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toggleBodyVisibility:function(b,a){var c=this.$el.find(".historyItemBody");a=(a===undefined)?(!c.is(":visible")):(a);if(a){c.slideDown("fast")}else{c.slideUp("fast")}this.trigger("toggleBodyVisibility",this.model.get("id"),a)},toString:function(){var a=(this.model)?(this.model+""):("(no model)");return"HDAView("+a+")"}});HDAView.templates={warningMsg:Handlebars.templates["template-warningmessagesmall"],messages:Handlebars.templates["template-history-warning-messages"],titleLink:Handlebars.templates["template-history-titleLink"],hdaSummary:Handlebars.templates["template-history-hdaSummary"],downloadLinks:Handlebars.templates["template-history-downloadLinks"],failedMetadata:Handlebars.templates["template-history-failedMetaData"],tagArea:Handlebars.templates["template-history-tagArea"],annotationArea:Handlebars.templates["template-history-annotationArea"],displayApps:Handlebars.templates["template-history-displayApps"]};function create_scatterplot_action_fn(a,b){action=function(){var d=$(window.parent.document).find("iframe#galaxy_main"),c=a+"/scatterplot?"+$.param(b);d.attr("src",c);$("div.popmenu-wrapper").remove();return false};return action}function create_trackster_action_fn(a,c,b){return function(){var d={};if(b){d.dbkey=b}$.ajax({url:a+"/list_tracks?f-"+$.param(d),dataType:"html",error:function(){alert("Could not add this dataset to browser.")},success:function(e){var f=window.parent;f.show_modal("View Data in a New or Saved Visualization","",{Cancel:function(){f.hide_modal()},"View in saved visualization":function(){f.show_modal("Add Data to Saved Visualization",e,{Cancel:function(){f.hide_modal()},"Add to visualization":function(){$(f.document).find("input[name=id]:checked").each(function(){var g=$(this).val();c.id=g;f.location=a+"/trackster?"+$.param(c)})}})},"View in new visualization":function(){f.location=a+"/trackster?"+$.param(c)}})}});return false}}var HistoryView=BaseView.extend(LoggableMixin).extend({logger:console,el:"body.historyPage",initialize:function(a){this.log(this+".initialize:",a);if(!a.urlTemplates){throw ("HDAView needs urlTemplates on initialize")}if(!a.urlTemplates.history){throw ("HDAView needs urlTemplates.history on initialize")}if(!a.urlTemplates.hda){throw ("HDAView needs urlTemplates.hda on initialize")}this.urlTemplates=a.urlTemplates.history;this.hdaUrlTemplates=a.urlTemplates.hda;this.storage=new PersistantStorage("HistoryView."+this.model.get("id"),{expandedHdas:{}});this.model.hdas.bind("add",this.add,this);this.model.hdas.bind("reset",this.addAll,this);this.model.hdas.bind("all",this.all,this);this.hdaViews={};this.urls={}},add:function(a){},addAll:function(){this.render()},all:function(a){},renderUrls:function(a){var b=this;b.urls={};_.each(this.urlTemplates,function(d,c){b.urls[c]=_.template(d,a)});return b.urls},render:function(){var b=this,d=b.toString()+".set-up",c=$("<div/>"),a=this.model.toJSON();a.urls=this.renderUrls(a);c.append(HistoryView.templates.historyPanel(a));b.$el.find(".tooltip").tooltip();if(!this.model.hdas.length||!this.renderItems(c.find("#"+this.model.get("id")+"-datasets"))){c.find("#emptyHistoryMessage").show()}$(b).queue(d,function(e){b.$el.fadeOut("fast",function(){e()})});$(b).queue(d,function(e){b.$el.html("");b.$el.append(c.children());b.$el.fadeIn("fast",function(){e()})});$(b).queue(d,function(e){this.log(b+" rendered:",b.$el);b.setUpBehaviours();b.trigger("rendered");e()});$(b).dequeue(d);return this},renderItems:function(c){this.hdaViews={};var b=this,a=this.model.get("show_deleted"),e=this.model.get("show_hidden"),d=this.model.hdas.getVisible(a,e);d.each(function(h){var g=h.get("id"),f=b.storage.get("expandedHdas").get(g);b.hdaViews[g]=new HDAView({model:h,expanded:f,urlTemplates:b.hdaUrlTemplates});b.setUpHdaListeners(b.hdaViews[g]);c.prepend(b.hdaViews[g].render().$el)});return d.length},setUpHdaListeners:function(a){var b=this;a.bind("toggleBodyVisibility",function(d,c){if(c){b.storage.get("expandedHdas").set(d,true)}else{b.storage.get("expandedHdas").deleteKey(d)}})},setUpBehaviours:function(){var a=this.$("#history-annotation-area");this.$("#history-annotate").click(function(){if(a.is(":hidden")){a.slideDown("fast")}else{a.slideUp("fast")}return false});async_save_text("history-name-container","history-name",this.urls.rename,"new_name",18);async_save_text("history-annotation-container","history-annotation",this.urls.annotate,"new_annotation",18,true,4)},events:{"click #history-collapse-all":"hideAllHdaBodies","click #history-tag":"loadAndDisplayTags"},hideAllHdaBodies:function(){_.each(this.itemViews,function(a){a.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},loadAndDisplayTags:function(c){this.log(this+".loadAndDisplayTags",c);var d=this.$el.find("#history-tag-area"),b=d.find(".tag-elt");this.log("\t tagArea",d," tagElt",b);if(d.is(":hidden")){if(!jQuery.trim(b.html())){var a=this;$.ajax({url:a.urls.tag,error:function(){alert("Tagging failed")},success:function(e){b.html(e);b.find(".tooltip").tooltip();d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toString:function(){var a=this.model.get("name")||"";return"HistoryView("+a+")"}});HistoryView.templates={historyPanel:Handlebars.templates["template-history-historyPanel"]};
\ No newline at end of file
+var HistoryDatasetAssociation=BaseModel.extend(LoggableMixin).extend({defaults:{history_id:null,model_class:"HistoryDatasetAssociation",hid:0,id:null,name:"",state:"",data_type:null,file_size:0,meta_files:[],misc_blurb:"",misc_info:"",deleted:false,purged:false,visible:false,accessible:false,for_editing:true},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",this.get("id"),a,this.previous("state"),b)}})},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((a===HistoryDatasetAssociation.STATES.NEW)||(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("name")+'"'}return"HistoryDatasetAssociation("+a+")"}});HistoryDatasetAssociation.STATES={UPLOAD:"upload",QUEUED:"queued",RUNNING:"running",SETTING_METADATA:"setting_metadata",NEW:"new",OK:"ok",EMPTY:"empty",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})},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},update:function(a){this.log(this+"update:",a);if(!(a&&a.length)){return}var b=this;_.each(a,function(e,c){var d=b.get(e);d.fetch()})},toString:function(){return("HDACollection("+this.ids().join(",")+")")}});var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:"",name:"",state:"",show_deleted:false,show_hidden:false,diskSize:0,deleted:false,tags:[],annotation:null,message:null,quotaMsg:false},url:function(){return"api/histories/"+this.get("id")},initialize:function(a,b){this.log(this+".initialize:",a,b);this.hdas=new HDACollection();if(b&&b.length){this.hdas.reset(b);this.checkForUpdates()}},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.log(b)}).then(function(){jQuery.ajax(b.url()+"/contents?"+jQuery.param({ids:b.itemIdsFromStateIds().join(",")})).success(function(d){b.hdas.reset(d);b.checkForUpdates();c()})})},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()}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.hdas.update(e)}if((c.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(c.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){setTimeout(function(){c.stateUpdater()},4000)}}).error(function(f,d,e){if(console&&console.warn){console.warn("Error getting history updates from the server:",f,d,e)}alert("Error getting history updates from the server.\n"+e)})},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});var HDAView=BaseView.extend(LoggableMixin).extend({tagName:"div",className:"historyItemContainer",initialize:function(a){this.log(this+".initialize:",a);if(!a.urlTemplates){throw ("HDAView needs urlTemplates on initialize")}this.urls=this.renderUrls(a.urlTemplates,this.model.toJSON());this.expanded=a.expanded||false;this.model.bind("change",this.render,this)},renderUrls:function(d,a){var b=this,c={};_.each(d,function(e,f){if(_.isObject(e)){c[f]=b.renderUrls(e,a)}else{if(f==="meta_download"){c[f]=b.renderMetaDownloadUrls(e,a)}else{c[f]=_.template(e,a)}}});return c},renderMetaDownloadUrls:function(b,a){return _.map(a.meta_files,function(c){return{url:_.template(b,{id:a.id,file_type:c.file_type}),file_type:c.file_type}})},render:function(){var b=this,e=this.model.get("id"),c=this.model.get("state"),a=$("<div/>").attr("id","historyItem-"+e),d=(this.$el.children().size()===0);this._clearReferences();this.$el.attr("id","historyItemContainer-"+e);a.addClass("historyItemWrapper").addClass("historyItem").addClass("historyItem-"+c);a.append(this._render_warnings());a.append(this._render_titleBar());this.body=$(this._render_body());a.append(this.body);make_popup_menus(a);a.find(".tooltip").tooltip({placement:"bottom"});this.$el.fadeOut("fast",function(){b.$el.children().remove();b.$el.append(a).fadeIn("fast",function(){b.log(b+" rendered:",b.$el);var f="rendered";if(d){f+=":initial"}else{if(b.model.inReadyState()){f+=":ready"}}b.trigger(f)})});return this},_clearReferences:function(){this.displayButton=null;this.editButton=null;this.deleteButton=null;this.errButton=null;this.showParamsButton=null;this.rerunButton=null;this.visualizationsButton=null;this.tagButton=null;this.annotateButton=null},_render_warnings:function(){return $(jQuery.trim(HDAView.templates.messages(_.extend(this.model.toJSON(),{urls:this.urls}))))},_render_titleBar:function(){var a=$('<div class="historyItemTitleBar" style="overflow: hidden"></div>');a.append(this._render_titleButtons());a.append('<span class="state-icon"></span>');a.append(this._render_titleLink());return a},_render_titleButtons:function(){var a=$('<div class="historyItemButtons"></div>');a.append(this._render_displayButton());a.append(this._render_editButton());a.append(this._render_deleteButton());return a},_render_displayButton:function(){if((!this.model.inReadyState())||(this.model.get("state")===HistoryDatasetAssociation.STATES.ERROR)||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var a={icon_class:"display"};if(this.model.get("purged")){a.enabled=false;a.title="Cannot display datasets removed from disk"}else{a.title="Display data in browser";a.href=this.urls.display}if(this.model.get("for_editing")){a.target="galaxy_main"}this.displayButton=new IconButtonView({model:new IconButton(a)});return this.displayButton.render().$el},_render_editButton:function(){if((this.model.get("state")===HistoryDatasetAssociation.STATES.UPLOAD)||(this.model.get("state")===HistoryDatasetAssociation.STATES.ERROR)||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))||(!this.model.get("for_editing"))){return null}var c=this.model.get("purged"),a=this.model.get("deleted"),b={title:"Edit attributes",href:this.urls.edit,target:"galaxy_main",icon_class:"edit"};if(a||c){b.enabled=false;if(c){b.title="Cannot edit attributes of datasets removed from disk"}else{if(a){b.title="Undelete dataset to edit attributes"}}}this.editButton=new IconButtonView({model:new IconButton(b)});return this.editButton.render().$el},_render_deleteButton:function(){if((!this.model.get("for_editing"))||(this.model.get("state")===HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var a={title:"Delete",href:this.urls["delete"],id:"historyItemDeleter-"+this.model.get("id"),icon_class:"delete"};if(this.model.get("deleted")||this.model.get("purged")){a={title:"Dataset is already deleted",icon_class:"delete",enabled:false}}this.deleteButton=new IconButtonView({model:new IconButton(a)});return this.deleteButton.render().$el},_render_titleLink:function(){return $(jQuery.trim(HDAView.templates.titleLink(_.extend(this.model.toJSON(),{urls:this.urls}))))},_render_hdaSummary:function(){var a=_.extend(this.model.toJSON(),{urls:this.urls});if(this.model.get("metadata_dbkey")==="?"&&!this.model.isDeletedOrPurged()){_.extend(a,{dbkey_unknown_and_editable:true})}return HDAView.templates.hdaSummary(a)},_render_primaryActionButtons:function(c){var b=$("<div/>").attr("id","primary-actions-"+this.model.get("id")),a=this;_.each(c,function(d){b.append(d.call(a))});return b},_render_downloadButton:function(){if(this.model.get("purged")){return null}var a=HDAView.templates.downloadLinks(_.extend(this.model.toJSON(),{urls:this.urls}));return $(a)},_render_errButton:function(){if((this.model.get("state")!==HistoryDatasetAssociation.STATES.ERROR)||(!this.model.get("for_editing"))){return null}this.errButton=new IconButtonView({model:new IconButton({title:"View or report this error",href:this.urls.report_error,target:"galaxy_main",icon_class:"bug"})});return this.errButton.render().$el},_render_showParamsButton:function(){this.showParamsButton=new IconButtonView({model:new IconButton({title:"View details",href:this.urls.show_params,target:"galaxy_main",icon_class:"information"})});return this.showParamsButton.render().$el},_render_rerunButton:function(){if(!this.model.get("for_editing")){return null}this.rerunButton=new IconButtonView({model:new IconButton({title:"Run this job again",href:this.urls.rerun,target:"galaxy_main",icon_class:"arrow-circle"})});return this.rerunButton.render().$el},_render_visualizationsButton:function(){var c=this.model.get("dbkey"),a=this.model.get("visualizations"),f=this.urls.visualization,d={},g={dataset_id:this.model.get("id"),hda_ldda:"hda"};if(!(this.model.hasData())||!(this.model.get("for_editing"))||!(a&&a.length)||!(f)){return null}this.visualizationsButton=new IconButtonView({model:new IconButton({title:"Visualize",href:f,icon_class:"chart_curve"})});var b=this.visualizationsButton.render().$el;b.addClass("visualize-icon");if(c){g.dbkey=c}function e(h){switch(h){case"trackster":return create_trackster_action_fn(f,g,c);case"scatterplot":return create_scatterplot_action_fn(f,g);default:return function(){window.parent.location=f+"/"+h+"?"+$.param(g)}}}if(a.length===1){b.attr("title",a[0]);b.click(e(a[0]))}else{_.each(a,function(i){var h=i.charAt(0).toUpperCase()+i.slice(1);d[h]=e(i)});make_popupmenu(b,d)}return b},_render_secondaryActionButtons:function(b){var c=$("<div/>"),a=this;c.attr("style","float: right;").attr("id","secondary-actions-"+this.model.get("id"));_.each(b,function(d){c.append(d.call(a))});return c},_render_tagButton:function(){if(!(this.model.hasData())||!(this.model.get("for_editing"))||(!this.urls.tags.get)){return null}this.tagButton=new IconButtonView({model:new IconButton({title:"Edit dataset tags",target:"galaxy_main",href:this.urls.tags.get,icon_class:"tags"})});return this.tagButton.render().$el},_render_annotateButton:function(){if(!(this.model.hasData())||!(this.model.get("for_editing"))||(!this.urls.annotation.get)){return null}this.annotateButton=new IconButtonView({model:new IconButton({title:"Edit dataset annotation",target:"galaxy_main",icon_class:"annotate"})});return this.annotateButton.render().$el},_render_displayApps:function(){if(!this.model.hasData()){return null}var a=$("<div/>").addClass("display-apps");if(!_.isEmpty(this.model.get("display_types"))){a.append(HDAView.templates.displayApps({displayApps:this.model.get("display_types")}))}if(!_.isEmpty(this.model.get("display_apps"))){a.append(HDAView.templates.displayApps({displayApps:this.model.get("display_apps")}))}return a},_render_tagArea:function(){if(!this.urls.tags.set){return null}return $(HDAView.templates.tagArea(_.extend(this.model.toJSON(),{urls:this.urls})))},_render_annotationArea:function(){if(!this.urls.annotation.get){return null}return $(HDAView.templates.annotationArea(_.extend(this.model.toJSON(),{urls:this.urls})))},_render_peek:function(){if(!this.model.get("peek")){return null}return $("<div/>").append($("<pre/>").attr("id","peek"+this.model.get("id")).addClass("peek").append(this.model.get("peek")))},_render_body_not_viewable:function(a){a.append($("<div>You do not have permission to view dataset.</div>"))},_render_body_uploading:function(a){a.append($("<div>Dataset is uploading</div>"))},_render_body_queued:function(a){a.append($("<div>Job is waiting to run.</div>"));a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_running:function(a){a.append("<div>Job is currently running.</div>");a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_error:function(a){if(!this.model.get("purged")){a.append($("<div>"+this.model.get("misc_blurb")+"</div>"))}a.append(("An error occurred running this job: <i>"+$.trim(this.model.get("misc_info"))+"</i>"));a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_errButton,this._render_showParamsButton,this._render_rerunButton]))},_render_body_discarded:function(a){a.append("<div>The job creating this dataset was cancelled before completion.</div>");a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_setting_metadata:function(a){a.append($("<div>Metadata is being auto-detected.</div>"))},_render_body_empty:function(a){a.append($("<div>No data: <i>"+this.model.get("misc_blurb")+"</i></div>"));a.append(this._render_primaryActionButtons([this._render_showParamsButton,this._render_rerunButton]))},_render_body_failed_metadata:function(a){a.append($(HDAView.templates.failedMetadata(this.model.toJSON())));this._render_body_ok(a)},_render_body_ok:function(a){a.append(this._render_hdaSummary());if(this.model.isDeletedOrPurged()){a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_showParamsButton,this._render_rerunButton]));return}a.append(this._render_primaryActionButtons([this._render_downloadButton,this._render_errButton,this._render_showParamsButton,this._render_rerunButton,this._render_visualizationsButton]));a.append(this._render_secondaryActionButtons([this._render_tagButton,this._render_annotateButton]));a.append('<div class="clear"/>');a.append(this._render_tagArea());a.append(this._render_annotationArea());a.append(this._render_displayApps());a.append(this._render_peek())},_render_body:function(){var a=$("<div/>").attr("id","info-"+this.model.get("id")).addClass("historyItemBody").attr("style","display: block");switch(this.model.get("state")){case HistoryDatasetAssociation.STATES.NOT_VIEWABLE:this._render_body_not_viewable(a);break;case HistoryDatasetAssociation.STATES.UPLOAD:this._render_body_uploading(a);break;case HistoryDatasetAssociation.STATES.QUEUED:this._render_body_queued(a);break;case HistoryDatasetAssociation.STATES.RUNNING:this._render_body_running(a);break;case HistoryDatasetAssociation.STATES.ERROR:this._render_body_error(a);break;case HistoryDatasetAssociation.STATES.DISCARDED:this._render_body_discarded(a);break;case HistoryDatasetAssociation.STATES.SETTING_METADATA:this._render_body_setting_metadata(a);break;case HistoryDatasetAssociation.STATES.EMPTY:this._render_body_empty(a);break;case HistoryDatasetAssociation.STATES.FAILED_METADATA:this._render_body_failed_metadata(a);break;case HistoryDatasetAssociation.STATES.OK:this._render_body_ok(a);break;default:a.append($('<div>Error: unknown dataset state "'+state+'".</div>'))}a.append('<div style="clear: both"></div>');if(this.expanded){a.show()}else{a.hide()}return a},events:{"click .historyItemTitle":"toggleBodyVisibility","click a.icon-button.tags":"loadAndDisplayTags","click a.icon-button.annotate":"loadAndDisplayAnnotation"},loadAndDisplayTags:function(b){this.log(this+".loadAndDisplayTags",b);var c=this.$el.find(".tag-area"),a=c.find(".tag-elt");if(c.is(":hidden")){if(!jQuery.trim(a.html())){$.ajax({url:this.urls.tags.get,error:function(){alert("Tagging failed")},success:function(d){a.html(d);a.find(".tooltip").tooltip();c.slideDown("fast")}})}else{c.slideDown("fast")}}else{c.slideUp("fast")}return false},loadAndDisplayAnnotation:function(b){this.log(this+".loadAndDisplayAnnotation",b);var d=this.$el.find(".annotation-area"),c=d.find(".annotation-elt"),a=this.urls.annotation.set;if(d.is(":hidden")){if(!jQuery.trim(c.html())){$.ajax({url:this.urls.annotation.get,error:function(){alert("Annotations failed")},success:function(e){if(e===""){e="<em>Describe or add notes to dataset</em>"}c.html(e);d.find(".tooltip").tooltip();async_save_text(c.attr("id"),c.attr("id"),a,"new_annotation",18,true,4);d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toggleBodyVisibility:function(b,a){var c=this.$el.find(".historyItemBody");a=(a===undefined)?(!c.is(":visible")):(a);if(a){c.slideDown("fast")}else{c.slideUp("fast")}this.trigger("toggleBodyVisibility",this.model.get("id"),a)},toString:function(){var a=(this.model)?(this.model+""):("(no model)");return"HDAView("+a+")"}});HDAView.templates={warningMsg:Handlebars.templates["template-warningmessagesmall"],messages:Handlebars.templates["template-history-warning-messages"],titleLink:Handlebars.templates["template-history-titleLink"],hdaSummary:Handlebars.templates["template-history-hdaSummary"],downloadLinks:Handlebars.templates["template-history-downloadLinks"],failedMetadata:Handlebars.templates["template-history-failedMetaData"],tagArea:Handlebars.templates["template-history-tagArea"],annotationArea:Handlebars.templates["template-history-annotationArea"],displayApps:Handlebars.templates["template-history-displayApps"]};function create_scatterplot_action_fn(a,b){action=function(){var d=$(window.parent.document).find("iframe#galaxy_main"),c=a+"/scatterplot?"+$.param(b);d.attr("src",c);$("div.popmenu-wrapper").remove();return false};return action}function create_trackster_action_fn(a,c,b){return function(){var d={};if(b){d.dbkey=b}$.ajax({url:a+"/list_tracks?f-"+$.param(d),dataType:"html",error:function(){alert("Could not add this dataset to browser.")},success:function(e){var f=window.parent;f.show_modal("View Data in a New or Saved Visualization","",{Cancel:function(){f.hide_modal()},"View in saved visualization":function(){f.show_modal("Add Data to Saved Visualization",e,{Cancel:function(){f.hide_modal()},"Add to visualization":function(){$(f.document).find("input[name=id]:checked").each(function(){var g=$(this).val();c.id=g;f.location=a+"/trackster?"+$.param(c)})}})},"View in new visualization":function(){f.location=a+"/trackster?"+$.param(c)}})}});return false}}var HistoryView=BaseView.extend(LoggableMixin).extend({el:"body.historyPage",initialize:function(a){this.log(this+".initialize:",a);if(!a.urlTemplates){throw ("HDAView needs urlTemplates on initialize")}if(!a.urlTemplates.history){throw ("HDAView needs urlTemplates.history on initialize")}if(!a.urlTemplates.hda){throw ("HDAView needs urlTemplates.hda on initialize")}this.urlTemplates=a.urlTemplates.history;this.hdaUrlTemplates=a.urlTemplates.hda;this.storage=new PersistantStorage("HistoryView."+this.model.get("id"),{expandedHdas:{}});this.model.bind("change:nice_size",this.updateHistoryDiskSize,this);this.model.hdas.bind("add",this.add,this);this.model.hdas.bind("reset",this.addAll,this);this.model.hdas.bind("all",this.all,this);this.hdaViews={};this.urls={}},add:function(a){},addAll:function(){this.render()},all:function(a){},renderUrls:function(a){var b=this;b.urls={};_.each(this.urlTemplates,function(d,c){b.urls[c]=_.template(d,a)});return b.urls},render:function(){var b=this,d=b.toString()+".set-up",c=$("<div/>"),a=this.model.toJSON(),e=(this.$el.children().size()===0);a.urls=this.renderUrls(a);c.append(HistoryView.templates.historyPanel(a));c.find(".tooltip").tooltip();if(!this.model.hdas.length||!this.renderItems(c.find("#"+this.model.get("id")+"-datasets"))){c.find("#emptyHistoryMessage").show()}$(b).queue(d,function(f){b.$el.fadeOut("fast",function(){f()})});$(b).queue(d,function(f){b.$el.html("");b.$el.append(c.children());b.$el.fadeIn("fast",function(){f()})});$(b).queue(d,function(f){this.log(b+" rendered:",b.$el);b.setUpBehaviours();if(e){b.trigger("rendered:initial")}else{b.trigger("rendered")}f()});$(b).dequeue(d);return this},renderItems:function(c){this.hdaViews={};var b=this,a=this.model.get("show_deleted"),e=this.model.get("show_hidden"),d=this.model.hdas.getVisible(a,e);_.each(d,function(h){var g=h.get("id"),f=b.storage.get("expandedHdas").get(g);b.hdaViews[g]=new HDAView({model:h,expanded:f,urlTemplates:b.hdaUrlTemplates});b.setUpHdaListeners(b.hdaViews[g]);c.prepend(b.hdaViews[g].render().$el)});return d.length},setUpHdaListeners:function(b){var a=this;b.bind("toggleBodyVisibility",function(d,c){if(c){a.storage.get("expandedHdas").set(d,true)}else{a.storage.get("expandedHdas").deleteKey(d)}});b.bind("rendered:ready",function(){a.trigger("hda:rendered:ready")})},setUpBehaviours:function(){if(!(this.model.get("user")&&this.model.get("user").email)){return}var a=this.$("#history-annotation-area");this.$("#history-annotate").click(function(){if(a.is(":hidden")){a.slideDown("fast")}else{a.slideUp("fast")}return false});async_save_text("history-name-container","history-name",this.urls.rename,"new_name",18);async_save_text("history-annotation-container","history-annotation",this.urls.annotate,"new_annotation",18,true,4)},updateHistoryDiskSize:function(){this.$el.find("#history-size").text(this.model.get("nice_size"))},showQuotaMessage:function(a){var b=this.$el.find("#quota-message-container");if(b.is(":hidden")){b.slideDown("fast")}},hideQuotaMessage:function(a){var b=this.$el.find("#quota-message-container");if(!b.is(":hidden")){b.slideUp("fast")}},events:{"click #history-collapse-all":"hideAllHdaBodies","click #history-tag":"loadAndDisplayTags"},hideAllHdaBodies:function(){_.each(this.hdaViews,function(a){a.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},loadAndDisplayTags:function(c){this.log(this+".loadAndDisplayTags",c);var d=this.$el.find("#history-tag-area"),b=d.find(".tag-elt");this.log("\t tagArea",d," tagElt",b);if(d.is(":hidden")){if(!jQuery.trim(b.html())){var a=this;$.ajax({url:a.urls.tag,error:function(){alert("Tagging failed")},success:function(e){b.html(e);b.find(".tooltip").tooltip();d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toString:function(){var a=this.model.get("name")||"";return"HistoryView("+a+")"}});HistoryView.templates={historyPanel:Handlebars.templates["template-history-historyPanel"]};
\ No newline at end of file
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/mvc/user/user-model.js
--- /dev/null
+++ b/static/scripts/packed/mvc/user/user-model.js
@@ -0,0 +1,1 @@
+var User=BaseModel.extend(LoggableMixin).extend({defaults:{id:null,username:"(anonymous user)",email:"",total_disk_usage:0,nice_total_disk_usage:"0 bytes"},initialize:function(a){this.log("User.initialize:",a);this.on("loaded",function(b,c){this.log(this+" has loaded:",b,c)});this.on("change",function(b,c){this.log(this+" has changed:",b,c.changes)})},urlRoot:"api/users",loadFromApi:function(d,b){d=d||User.CURRENT_ID_STR;b=b||{};var a=this,c=b.success;b.success=function(f,e){a.trigger("loaded",f,e);if(c){c(f,e)}};if(d===User.CURRENT_ID_STR){b.url=this.urlRoot+"/"+User.CURRENT_ID_STR}return BaseModel.prototype.fetch.call(this,b)},toString:function(){var a=[this.get("username")];if(this.get("id")){a.unshift(this.get("id"));a.push(this.get("email"))}return"User("+a.join(":")+")"}});User.CURRENT_ID_STR="current";User.getCurrentUserFromApi=function(b){var a=new User();a.loadFromApi(User.CURRENT_ID_STR,b);return a};var UserCollection=Backbone.Collection.extend(LoggableMixin).extend({model:User,logger:console,urlRoot:"api/users"});
\ No newline at end of file
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/mvc/user/user-quotameter.js
--- /dev/null
+++ b/static/scripts/packed/mvc/user/user-quotameter.js
@@ -0,0 +1,1 @@
+var UserQuotaMeter=BaseView.extend(LoggableMixin).extend({options:{warnAtPercent:85,errorAtPercent:100,meterDocument:window.top.document,containerSelector:".quota-meter-container",meterSelector:"#quota-meter",barSelector:"#quota-meter-bar",textSelector:"#quota-meter-text",msgDocument:(top.frames.galaxy_history)?(top.frames.galaxy_history.document):(top.document),msgSelector:"#quota-message-container",warnClass:"quota-meter-bar-warn",errorClass:"quota-meter-bar-error",usageTemplate:"Using <%= nice_total_disk_usage %>",quotaTemplate:"Using <%= quota_percent %>%",meterTemplate:"",animationSpeed:"fast"},initialize:function(a){this.log(this+".initialize:",a);_.extend(this.options,a);this.model.bind("change:quota_percent change:total_disk_usage",this.render,this)},update:function(a){this.log(this+" updating user data...",a);this.model.loadFromApi(this.model.get("id"),a);return this},isOverQuota:function(){return(this.model.get("quota_percent")!==null&&this.model.get("quota_percent")>=this.options.errorAtPercent)},_render_quota:function(){var a=this.model.toJSON(),b=a.quota_percent,c=$(UserQuotaMeter.templates.quota(a));if(this.isOverQuota()){c.addClass("progress-danger");c.find("#quota-meter-text").css("color","white");this.trigger("quota:over",a)}else{if(b>=this.options.warnAtPercent){c.addClass("progress-warning");this.trigger("quota:under quota:under:approaching",a)}else{c.addClass("progress-success");this.trigger("quota:under quota:under:ok",a)}}return c},_render_usage:function(){var a=$(UserQuotaMeter.templates.usage(this.model.toJSON()));this.log(this+".rendering usage:",a);return a},render:function(){var a=null;this.log(this+".model.quota_percent:",this.model.get("quota_percent"));if((this.model.get("quota_percent")===null)||(this.model.get("quota_percent")===undefined)){a=this._render_usage()}else{a=this._render_quota()}this.$el.html(a);return this},toString:function(){return"UserQuotaMeter("+this.model+")"}});UserQuotaMeter.templates={quota:Handlebars.templates["template-user-quotaMeter-quota"],usage:Handlebars.templates["template-user-quotaMeter-usage"]};
\ No newline at end of file
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 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(k,y,w,r,G){w=w||k.helpers;var x="",n,m,f="function",e=this.escapeExpression,u=this,c=w.blockHelperMissing;function t(L,K){var I="",J,H;I+='\n <div id="history-name" style="margin-right: 50px;" class="tooltip editable-text"\n title="Click to rename history">';H=w.name;if(H){J=H.call(L,{hash:{}})}else{J=L.name;J=typeof J===f?J():J}I+=e(J)+"</div>\n ";return I}function s(I,H){return"refresh"}function q(I,H){return"collapse all"}function p(L,K){var I="",J,H;I+='\n <a id="history-tag" title="';H=w.local;if(H){J=H.call(L,{hash:{},inverse:u.noop,fn:u.program(8,l,K)})}else{J=L.local;J=typeof J===f?J():J}if(!w.local){J=c.call(L,J,{hash:{},inverse:u.noop,fn:u.program(8,l,K)})}if(J||J===0){I+=J}I+='"\n class="icon-button tags tooltip" target="galaxy_main" href="javascript:void(0)"></a>\n <a id="history-annotate" title="';H=w.local;if(H){J=H.call(L,{hash:{},inverse:u.noop,fn:u.program(10,F,K)})}else{J=L.local;J=typeof J===f?J():J}if(!w.local){J=c.call(L,J,{hash:{},inverse:u.noop,fn:u.program(10,F,K)})}if(J||J===0){I+=J}I+='"\n class="icon-button annotate tooltip" target="galaxy_main" href="javascript:void(0)"></a>\n ';return I}function l(I,H){return"Edit history tags"}function F(I,H){return"Edit history annotation"}function E(L,K){var I="",J,H;I+='\n <a href="';J=L.urls;J=J==null||J===false?J:J.hide_deleted;J=typeof J===f?J():J;I+=e(J)+'">';H=w.local;if(H){J=H.call(L,{hash:{},inverse:u.noop,fn:u.program(13,D,K)})}else{J=L.local;J=typeof J===f?J():J}if(!w.local){J=c.call(L,J,{hash:{},inverse:u.noop,fn:u.program(13,D,K)})}if(J||J===0){I+=J}I+="</a>\n ";return I}function D(I,H){return"hide deleted"}function C(L,K){var I="",J,H;I+='\n <a href="';J=L.urls;J=J==null||J===false?J:J.hide_hidden;J=typeof J===f?J():J;I+=e(J)+'">';H=w.local;if(H){J=H.call(L,{hash:{},inverse:u.noop,fn:u.program(16,B,K)})}else{J=L.local;J=typeof J===f?J():J}if(!w.local){J=c.call(L,J,{hash:{},inverse:u.noop,fn:u.program(16,B,K)})}if(J||J===0){I+=J}I+="</a>\n ";return I}function B(I,H){return"hide hidden"}function A(L,K){var I="",J,H;I+="\n";H=w.warningmessagesmall;if(H){J=H.call(L,{hash:{},inverse:u.noop,fn:u.program(19,z,K)})}else{J=L.warningmessagesmall;J=typeof J===f?J():J}if(!w.warningmessagesmall){J=c.call(L,J,{hash:{},inverse:u.noop,fn:u.program(19,z,K)})}if(J||J===0){I+=J}I+="\n";return I}function z(K,J){var I,H;H=w.local;if(H){I=H.call(K,{hash:{},inverse:u.noop,fn:u.program(20,o,J)})}else{I=K.local;I=typeof I===f?I():I}if(!w.local){I=c.call(K,I,{hash:{},inverse:u.noop,fn:u.program(20,o,J)})}if(I||I===0){return I}else{return""}}function o(I,H){return"You are currently viewing a deleted history!"}function j(K,J){var H="",I;H+='\n <div id="history-tag-area" style="display: none">\n <strong>Tags:</strong>\n <div class="tag-elt"></div>\n </div>\n\n <div id="history-annotation-area" style="display: none">\n <strong>Annotation / Notes:</strong>\n <div id="history-annotation-container">\n <div id="history-annotation" class="tooltip editable-text" title="Click to edit annotation">\n ';I=K.annotation;I=w["if"].call(K,I,{hash:{},inverse:u.program(25,h,J),fn:u.program(23,i,J)});if(I||I===0){H+=I}H+="\n </div>\n </div>\n </div>\n ";return H}function i(L,K){var I="",J,H;I+="\n ";H=w.annotation;if(H){J=H.call(L,{hash:{}})}else{J=L.annotation;J=typeof J===f?J():J}I+=e(J)+"\n ";return I}function h(I,H){return"\n <em>Describe or add notes to history</em>\n "}function g(L,K){var I="",J,H;I+='\n<div id="message-container">\n <div class="';H=w.status;if(H){J=H.call(L,{hash:{}})}else{J=L.status;J=typeof J===f?J():J}I+=e(J)+'message">\n ';H=w.message;if(H){J=H.call(L,{hash:{}})}else{J=L.message;J=typeof J===f?J():J}I+=e(J)+"\n </div><br />\n</div>\n";return I}function d(I,H){return'\n <div id="quota-message" class="errormessage">\n You are over your disk quota. Tool execution is on hold until your disk usage drops below your allocated quota.\n </div>\n <br/>\n '}function v(I,H){return"Your history is empty. Click 'Get Data' on the left pane to start"}x+='\n<div id="history-name-area" class="historyLinks">\n <div id="history-name-container" style="position: relative;">\n ';x+='\n <div id="history-size" style="position: absolute; top: 3px; right: 0px;">';m=w.nice_size;if(m){n=m.call(y,{hash:{}})}else{n=y.nice_size;n=typeof n===f?n():n}x+=e(n)+"</div>\n ";n=y.user;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(1,t,G)});if(n||n===0){x+=n}x+='\n </div> \n</div>\n<div style="clear: both;"></div>\n\n<div id="top-links" class="historyLinks">\n <a title="';m=w.local;if(m){n=m.call(y,{hash:{},inverse:u.noop,fn:u.program(3,s,G)})}else{n=y.local;n=typeof n===f?n():n}if(!w.local){n=c.call(y,n,{hash:{},inverse:u.noop,fn:u.program(3,s,G)})}if(n||n===0){x+=n}x+='" class="icon-button arrow-circle tooltip" href="';n=y.urls;n=n==null||n===false?n:n.base;n=typeof n===f?n():n;x+=e(n)+"\"></a>\n <a title='";m=w.local;if(m){n=m.call(y,{hash:{},inverse:u.noop,fn:u.program(5,q,G)})}else{n=y.local;n=typeof n===f?n():n}if(!w.local){n=c.call(y,n,{hash:{},inverse:u.noop,fn:u.program(5,q,G)})}if(n||n===0){x+=n}x+="' id=\"history-collapse-all\"\n class='icon-button toggle tooltip' href='javascript:void(0);'></a>\n <div style=\"width: 40px; float: right; white-space: nowrap;\">\n ";n=y.user;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(7,p,G)});if(n||n===0){x+=n}x+='\n </div>\n</div>\n<div style="clear: both;"></div>\n\n';x+='\n<div class="historyLinks">\n ';n=y.show_deleted;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(12,E,G)});if(n||n===0){x+=n}x+="\n ";n=y.show_hidden;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(15,C,G)});if(n||n===0){x+=n}x+="\n</div>\n\n";n=y.deleted;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(18,A,G)});if(n||n===0){x+=n}x+="\n\n";x+="\n";x+='\n<div style="margin: 0px 5px 10px 5px">\n\n ';n=y.user;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(22,j,G)});if(n||n===0){x+=n}x+="\n</div>\n\n";n=y.message;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(27,g,G)});if(n||n===0){x+=n}x+='\n\n<div id="quota-message-container">\n ';n=y.over_quota;n=w["if"].call(y,n,{hash:{},inverse:u.noop,fn:u.program(29,d,G)});if(n||n===0){x+=n}x+='\n</div>\n\n<div id="';m=w.id;if(m){n=m.call(y,{hash:{}})}else{n=y.id;n=typeof n===f?n():n}x+=e(n)+'-datasets" class="history-datasets-list"></div>\n\n<div class="infomessagesmall" id="emptyHistoryMessage" style="display: none;">\n ';m=w.local;if(m){n=m.call(y,{hash:{},inverse:u.noop,fn:u.program(31,v,G)})}else{n=y.local;n=typeof n===f?n():n}if(!w.local){n=c.call(y,n,{hash:{},inverse:u.noop,fn:u.program(31,v,G)})}if(n||n===0){x+=n}x+="\n</div>";return x})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(l,z,x,s,G){x=x||l.helpers;var y="",o,n,f="function",e=this.escapeExpression,v=this,c=x.blockHelperMissing;function u(L,K){var I="",J,H;I+='\n <div id="history-name" style="margin-right: 50px;" class="tooltip editable-text"\n title="Click to rename history">';H=x.name;if(H){J=H.call(L,{hash:{}})}else{J=L.name;J=typeof J===f?J():J}I+=e(J)+"</div>\n ";return I}function t(L,K){var I="",J,H;I+='\n <div id="history-name" style="margin-right: 50px;" class="tooltip"\n title="You must be logged in to edit your history name">';H=x.name;if(H){J=H.call(L,{hash:{}})}else{J=L.name;J=typeof J===f?J():J}I+=e(J)+"</div>\n ";return I}function r(I,H){return"refresh"}function q(I,H){return"collapse all"}function k(L,K){var I="",J,H;I+='\n <a id="history-tag" title="';H=x.local;if(H){J=H.call(L,{hash:{},inverse:v.noop,fn:v.program(10,F,K)})}else{J=L.local;J=typeof J===f?J():J}if(!x.local){J=c.call(L,J,{hash:{},inverse:v.noop,fn:v.program(10,F,K)})}if(J||J===0){I+=J}I+='"\n class="icon-button tags tooltip" target="galaxy_main" href="javascript:void(0)"></a>\n <a id="history-annotate" title="';H=x.local;if(H){J=H.call(L,{hash:{},inverse:v.noop,fn:v.program(12,E,K)})}else{J=L.local;J=typeof J===f?J():J}if(!x.local){J=c.call(L,J,{hash:{},inverse:v.noop,fn:v.program(12,E,K)})}if(J||J===0){I+=J}I+='"\n class="icon-button annotate tooltip" target="galaxy_main" href="javascript:void(0)"></a>\n ';return I}function F(I,H){return"Edit history tags"}function E(I,H){return"Edit history annotation"}function D(L,K){var I="",J,H;I+='\n <a href="';J=L.urls;J=J==null||J===false?J:J.hide_deleted;J=typeof J===f?J():J;I+=e(J)+'">';H=x.local;if(H){J=H.call(L,{hash:{},inverse:v.noop,fn:v.program(15,C,K)})}else{J=L.local;J=typeof J===f?J():J}if(!x.local){J=c.call(L,J,{hash:{},inverse:v.noop,fn:v.program(15,C,K)})}if(J||J===0){I+=J}I+="</a>\n ";return I}function C(I,H){return"hide deleted"}function B(L,K){var I="",J,H;I+='\n <a href="';J=L.urls;J=J==null||J===false?J:J.hide_hidden;J=typeof J===f?J():J;I+=e(J)+'">';H=x.local;if(H){J=H.call(L,{hash:{},inverse:v.noop,fn:v.program(18,A,K)})}else{J=L.local;J=typeof J===f?J():J}if(!x.local){J=c.call(L,J,{hash:{},inverse:v.noop,fn:v.program(18,A,K)})}if(J||J===0){I+=J}I+="</a>\n ";return I}function A(I,H){return"hide hidden"}function p(L,K){var I="",J,H;I+="\n";H=x.warningmessagesmall;if(H){J=H.call(L,{hash:{},inverse:v.noop,fn:v.program(21,m,K)})}else{J=L.warningmessagesmall;J=typeof J===f?J():J}if(!x.warningmessagesmall){J=c.call(L,J,{hash:{},inverse:v.noop,fn:v.program(21,m,K)})}if(J||J===0){I+=J}I+="\n";return I}function m(K,J){var I,H;H=x.local;if(H){I=H.call(K,{hash:{},inverse:v.noop,fn:v.program(22,j,J)})}else{I=K.local;I=typeof I===f?I():I}if(!x.local){I=c.call(K,I,{hash:{},inverse:v.noop,fn:v.program(22,j,J)})}if(I||I===0){return I}else{return""}}function j(I,H){return"You are currently viewing a deleted history!"}function i(K,J){var H="",I;H+='\n <div id="history-tag-area" style="display: none">\n <strong>Tags:</strong>\n <div class="tag-elt"></div>\n </div>\n\n <div id="history-annotation-area" style="display: none">\n <strong>Annotation / Notes:</strong>\n <div id="history-annotation-container">\n <div id="history-annotation" class="tooltip editable-text" title="Click to edit annotation">\n ';I=K.annotation;I=x["if"].call(K,I,{hash:{},inverse:v.program(27,g,J),fn:v.program(25,h,J)});if(I||I===0){H+=I}H+="\n </div>\n </div>\n </div>\n ";return H}function h(L,K){var I="",J,H;I+="\n ";H=x.annotation;if(H){J=H.call(L,{hash:{}})}else{J=L.annotation;J=typeof J===f?J():J}I+=e(J)+"\n ";return I}function g(I,H){return"\n <em>Describe or add notes to history</em>\n "}function d(L,K){var I="",J,H;I+='\n<div id="message-container">\n <div class="';H=x.status;if(H){J=H.call(L,{hash:{}})}else{J=L.status;J=typeof J===f?J():J}I+=e(J)+'message">\n ';H=x.message;if(H){J=H.call(L,{hash:{}})}else{J=L.message;J=typeof J===f?J():J}I+=e(J)+"\n </div><br />\n</div>\n";return I}function w(I,H){return"Your history is empty. Click 'Get Data' on the left pane to start"}y+='\n<div id="history-name-area" class="historyLinks">\n <div id="history-name-container" style="position: relative;">\n ';y+='\n <div id="history-size" style="position: absolute; top: 3px; right: 0px;">';n=x.nice_size;if(n){o=n.call(z,{hash:{}})}else{o=z.nice_size;o=typeof o===f?o():o}y+=e(o)+"</div>\n ";o=z.user;o=o==null||o===false?o:o.email;o=x["if"].call(z,o,{hash:{},inverse:v.program(3,t,G),fn:v.program(1,u,G)});if(o||o===0){y+=o}y+='\n </div> \n</div>\n<div style="clear: both;"></div>\n\n<div id="top-links" class="historyLinks">\n <a title="';n=x.local;if(n){o=n.call(z,{hash:{},inverse:v.noop,fn:v.program(5,r,G)})}else{o=z.local;o=typeof o===f?o():o}if(!x.local){o=c.call(z,o,{hash:{},inverse:v.noop,fn:v.program(5,r,G)})}if(o||o===0){y+=o}y+='" class="icon-button arrow-circle tooltip" href="';o=z.urls;o=o==null||o===false?o:o.base;o=typeof o===f?o():o;y+=e(o)+"\"></a>\n <a title='";n=x.local;if(n){o=n.call(z,{hash:{},inverse:v.noop,fn:v.program(7,q,G)})}else{o=z.local;o=typeof o===f?o():o}if(!x.local){o=c.call(z,o,{hash:{},inverse:v.noop,fn:v.program(7,q,G)})}if(o||o===0){y+=o}y+="' id=\"history-collapse-all\"\n class='icon-button toggle tooltip' href='javascript:void(0);'></a>\n <div style=\"width: 40px; float: right; white-space: nowrap;\">\n ";o=z.user;o=o==null||o===false?o:o.email;o=x["if"].call(z,o,{hash:{},inverse:v.noop,fn:v.program(9,k,G)});if(o||o===0){y+=o}y+='\n </div>\n</div>\n<div style="clear: both;"></div>\n\n';y+='\n<div class="historyLinks">\n ';o=z.show_deleted;o=x["if"].call(z,o,{hash:{},inverse:v.noop,fn:v.program(14,D,G)});if(o||o===0){y+=o}y+="\n ";o=z.show_hidden;o=x["if"].call(z,o,{hash:{},inverse:v.noop,fn:v.program(17,B,G)});if(o||o===0){y+=o}y+="\n</div>\n\n";o=z.deleted;o=x["if"].call(z,o,{hash:{},inverse:v.noop,fn:v.program(20,p,G)});if(o||o===0){y+=o}y+="\n\n";y+="\n";y+='\n<div style="margin: 0px 5px 10px 5px">\n\n ';o=z.user;o=o==null||o===false?o:o.email;o=x["if"].call(z,o,{hash:{},inverse:v.noop,fn:v.program(24,i,G)});if(o||o===0){y+=o}y+="\n</div>\n\n";o=z.message;o=x["if"].call(z,o,{hash:{},inverse:v.noop,fn:v.program(29,d,G)});if(o||o===0){y+=o}y+='\n\n<div id="quota-message-container" style="display: none">\n <div id="quota-message" class="errormessage">\n You are over your disk quota. Tool execution is on hold until your disk usage drops below your allocated quota.\n </div>\n</div>\n\n<div id="';n=x.id;if(n){o=n.call(z,{hash:{}})}else{o=z.id;o=typeof o===f?o():o}y+=e(o)+'-datasets" class="history-datasets-list"></div>\n\n<div class="infomessagesmall" id="emptyHistoryMessage" style="display: none;">\n ';n=x.local;if(n){o=n.call(z,{hash:{},inverse:v.noop,fn:v.program(31,w,G)})}else{o=z.local;o=typeof o===f?o():o}if(!x.local){o=c.call(z,o,{hash:{},inverse:v.noop,fn:v.program(31,w,G)})}if(o||o===0){y+=o}y+="\n</div>";return y})})();
\ No newline at end of file
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/templates/compiled/template-user-quotaMeter-quota.js
--- /dev/null
+++ b/static/scripts/packed/templates/compiled/template-user-quotaMeter-quota.js
@@ -0,0 +1,1 @@
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-user-quotaMeter-quota"]=b(function(e,l,d,k,j){d=d||e.helpers;var h="",c,g,f="function",i=this.escapeExpression;h+='<div id="quota-meter" class="quota-meter progress">\n <div id="quota-meter-bar" class="quota-meter-bar bar" style="width: ';g=d.quota_percent;if(g){c=g.call(l,{hash:{}})}else{c=l.quota_percent;c=typeof c===f?c():c}h+=i(c)+'%"></div>\n ';h+='\n <div id="quota-meter-text" class="quota-meter-text"style="top: 6px">\n Using ';g=d.quota_percent;if(g){c=g.call(l,{hash:{}})}else{c=l.quota_percent;c=typeof c===f?c():c}h+=i(c)+"%\n </div>\n</div>";return h})})();
\ No newline at end of file
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/templates/compiled/template-user-quotaMeter-usage.js
--- /dev/null
+++ b/static/scripts/packed/templates/compiled/template-user-quotaMeter-usage.js
@@ -0,0 +1,1 @@
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-user-quotaMeter-usage"]=b(function(e,l,d,k,j){d=d||e.helpers;var h="",c,g,f="function",i=this.escapeExpression;h+='\n<div id="quota-meter" class="quota-meter" style="background-color: transparent">\n <div id="quota-meter-text" class="quota-meter-text" style="top: 6px; color: white">\n Using ';g=d.nice_total_disk_usage;if(g){c=g.call(l,{hash:{}})}else{c=l.nice_total_disk_usage;c=typeof c===f?c():c}h+=i(c)+"\n </div>\n</div>";return h})})();
\ No newline at end of file
diff -r 79c884befb2333f79d1fd8a9af3e531c42a59d6b -r 3d27b35e1c1fa629ec4175f5e613b5a771152377 static/scripts/packed/viz/circster.js
--- a/static/scripts/packed/viz/circster.js
+++ b/static/scripts/packed/viz/circster.js
@@ -1,1 +1,1 @@
-define(["libs/underscore","libs/d3","viz/visualization"],function(g,l,i){var m=Backbone.Model.extend({is_visible:function(q,n){var o=q.getBoundingClientRect(),p=$("svg")[0].getBoundingClientRect();if(o.right<0||o.left>p.right||o.bottom<0||o.top>p.bottom){return false}return true}});var h={drawTicks:function(r,q,v,p,n){var u=r.append("g").selectAll("g").data(q).enter().append("g").selectAll("g").data(v).enter().append("g").attr("class","tick").attr("transform",function(w){return"rotate("+(w.angle*180/Math.PI-90)+")translate("+w.radius+",0)"});var t=[],s=[],o=function(w){return w.angle>Math.PI?"end":null};if(n){t=[0,0,0,-4];s=[4,0,"",".35em"];o=null}else{t=[1,0,4,0];s=[0,4,".35em",""]}u.append("line").attr("x1",t[0]).attr("y1",t[1]).attr("x2",t[2]).attr("y1",t[3]).style("stroke","#000");u.append("text").attr("x",s[0]).attr("y",s[1]).attr("dx",s[2]).attr("dy",s[3]).attr("text-anchor",o).attr("transform",p).text(function(w){return w.label})},formatNum:function(o,n){var q=null;if(o<1){q=o.toPrecision(n)}else{var p=Math.round(o.toPrecision(n));if(o<1000){q=p}else{if(o<1000000){q=Math.round((p/1000).toPrecision(3)).toFixed(0)+"K"}else{if(o<1000000000){q=Math.round((p/1000000).toPrecision(3)).toFixed(0)+"M"}}}}return q}};var c=Backbone.Model.extend({});var a=Backbone.View.extend({className:"circster",initialize:function(n){this.total_gap=n.total_gap;this.genome=n.genome;this.dataset_arc_height=n.dataset_arc_height;this.track_gap=10;this.label_arc_height=50;this.scale=1;this.circular_views=null;this.chords_views=null;this.model.get("tracks").on("add",this.add_track,this);this.model.get("tracks").on("remove",this.remove_track,this);this.get_circular_tracks()},get_circular_tracks:function(){return this.model.get("tracks").filter(function(n){return n.get("track_type")!=="DiagonalHeatmapTrack"})},get_chord_tracks:function(){return this.model.get("tracks").filter(function(n){return n.get("track_type")==="DiagonalHeatmapTrack"})},get_tracks_bounds:function(){var o=this.get_circular_tracks();dataset_arc_height=this.dataset_arc_height,min_dimension=Math.min(this.$el.width(),this.$el.height()),radius_start=min_dimension/2-o.length*(this.dataset_arc_height+this.track_gap)-(this.label_arc_height+this.track_gap),tracks_start_radii=l.range(radius_start,min_dimension/2,this.dataset_arc_height+this.track_gap);var n=this;return g.map(tracks_start_radii,function(p){return[p,p+n.dataset_arc_height]})},render:function(){var w=this,q=this.dataset_arc_height,n=w.$el.width(),v=w.$el.height(),s=this.get_circular_tracks(),p=this.get_chord_tracks(),r=this.get_tracks_bounds(),o=l.select(w.$el[0]).append("svg").attr("width",n).attr("height",v).attr("pointer-events","all").append("svg:g").call(l.behavior.zoom().on("zoom",function(){var x=l.event.scale;o.attr("transform","translate("+l.event.translate+") scale("+x+")");if(w.scale!==x){if(w.zoom_drag_timeout){clearTimeout(w.zoom_drag_timeout)}w.zoom_drag_timeout=setTimeout(function(){},400)}})).attr("transform","translate("+n/2+","+v/2+")").append("svg:g").attr("class","tracks");this.circular_views=s.map(function(y,z){var A=(y.get("track_type")==="LineTrack"?d:e),x=new A({el:o.append("g")[0],track:y,radius_bounds:r[z],genome:w.genome,total_gap:w.total_gap});x.render();return x});this.chords_views=p.map(function(y){var x=new j({el:o.append("g")[0],track:y,radius_bounds:r[0],genome:w.genome,total_gap:w.total_gap});x.render();return x});var u=this.circular_views[this.circular_views.length-1].radius_bounds[1],t=[u,u+this.label_arc_height];this.label_track_view=new b({el:o.append("g")[0],track:new c(),radius_bounds:t,genome:w.genome,total_gap:w.total_gap});this.label_track_view.render()},add_track:function(t){if(t.get("track_type")==="DiagonalHeatmapTrack"){var p=this.circular_views[0].radius_bounds,s=new j({el:l.select("g.tracks").append("g")[0],track:t,radius_bounds:p,genome:this.genome,total_gap:this.total_gap});s.render();this.chords_views.push(s)}else{var r=this.get_tracks_bounds();g.each(this.circular_views,function(v,w){v.update_radius_bounds(r[w])});g.each(this.chords_views,function(v){v.update_radius_bounds(r[0])});var q=this.circular_views.length,u=(t.get("track_type")==="LineTrack"?d:e),n=new u({el:l.select("g.tracks").append("g")[0],track:t,radius_bounds:r[q],genome:this.genome,total_gap:this.total_gap});n.render();this.circular_views.push(n);var o=r[r.length-1];o[1]=o[0];this.label_track_view.update_radius_bounds(o)}},remove_track:function(o,q,p){var n=this.circular_views[p.index];this.circular_views.splice(p.index,1);n.$el.remove();var r=this.get_tracks_bounds();g.each(this.circular_views,function(s,t){s.update_radius_bounds(r[t])})}});var k=Backbone.View.extend({tagName:"g",initialize:function(n){this.bg_stroke="ccc";this.loading_bg_fill="000";this.bg_fill="ccc";this.total_gap=n.total_gap;this.track=n.track;this.radius_bounds=n.radius_bounds;this.genome=n.genome;this.chroms_layout=this._chroms_layout();this.data_bounds=[];this.scale=1;this.parent_elt=l.select(this.$el[0])},get_fill_color:function(){var n=this.track.get("config").get_value("block_color");if(!n){n=this.track.get("config").get_value("color")}return n},render:function(){var r=this.parent_elt;if(!r){console.log("no parent elt")}var q=this.chroms_layout,t=l.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]),n=r.selectAll("g").data(q).enter().append("svg:g"),p=n.append("path").attr("d",t).attr("class","chrom-background").style("stroke",this.bg_stroke).style("fill",this.loading_bg_fill);p.append("title").text(function(v){return v.data.chrom});var o=this,s=o.track.get("data_manager"),u=(s?s.data_is_ready():true);$.when(u).then(function(){$.when(o._render_data(r)).then(function(){p.style("fill",o.bg_fill);o.render_labels()})})},render_labels:function(){},update_radius_bounds:function(o){this.radius_bounds=o;var n=l.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]);this.parent_elt.selectAll("g>path.chrom-background").transition().duration(1000).attr("d",n);this._transition_chrom_data();this._transition_labels()},update_scale:function(q){var p=this.scale;this.scale=q;if(q<=p){return}var o=this,n=new m();this.parent_elt.selectAll("path.chrom-data").filter(function(s,r){return n.is_visible(this)}).each(function(x,t){var w=l.select(this),s=w.attr("chrom"),v=o.genome.get_chrom_region(s),u=o.track.get("data_manager"),r;if(!u.can_get_more_detailed_data(v)){return}r=o.track.get("data_manager").get_more_detailed_data(v,"Coverage",0,q);$.when(r).then(function(A){w.remove();o._update_data_bounds();var z=g.find(o.chroms_layout,function(B){return B.data.chrom===s});var y=o.get_fill_color();o._render_chrom_data(o.parent_elt,z,A).style("stroke",y).style("fill",y)})});return o},_transition_chrom_data:function(){var o=this.track,q=this.chroms_layout,n=this.parent_elt.selectAll("g>path.chrom-data"),r=n[0].length;if(r>0){var p=this;$.when(o.get("data_manager").get_genome_wide_data(this.genome)).then(function(t){var s=g.reject(g.map(t,function(u,v){var w=null,x=p._get_path_function(q[v],u);if(x){w=x(u.data)}return w}),function(u){return u===null});n.each(function(v,u){l.select(this).transition().duration(1000).attr("d",s[u])})})}},_transition_labels:function(){},_update_data_bounds:function(){var n=this.data_bounds;this.data_bounds=this.get_data_bounds(this.track.get("data_manager").get_genome_wide_data(this.genome));if(this.data_bounds[0]<n[0]||this.data_bounds[1]>n[1]){this._transition_chrom_data()}},_render_data:function(q){var p=this,o=this.chroms_layout,n=this.track,r=$.Deferred();$.when(n.get("data_manager").get_genome_wide_data(this.genome)).then(function(t){p.data_bounds=p.get_data_bounds(t);layout_and_data=g.zip(o,t),chroms_data_layout=g.map(layout_and_data,function(u){var v=u[0],w=u[1];return p._render_chrom_data(q,v,w)});var s=p.get_fill_color();p.parent_elt.selectAll("path.chrom-data").style("stroke",s).style("fill",s);r.resolve(q)});return r},_render_chrom_data:function(n,o,p){},_get_path_function:function(o,n){},_chroms_layout:function(){var o=this.genome.get_chroms_info(),q=l.layout.pie().value(function(s){return s.len}).sort(null),r=q(o),n=this.total_gap/o.length,p=g.map(r,function(u,t){var s=u.endAngle-n;u.endAngle=(s>u.startAngle?s:u.startAngle);return u});return p}});var b=k.extend({initialize:function(n){k.prototype.initialize.call(this,n);this.innerRadius=this.radius_bounds[0];this.radius_bounds[0]=this.radius_bounds[1];this.bg_stroke="fff";this.bg_fill="fff";this.min_arc_len=0.08},_render_data:function(p){var o=this,n=p.selectAll("g");n.selectAll("path").attr("id",function(t){return"label-"+t.data.chrom});n.append("svg:text").filter(function(t){return t.endAngle-t.startAngle>o.min_arc_len}).attr("text-anchor","middle").append("svg:textPath").attr("xlink:href",function(t){return"#label-"+t.data.chrom}).attr("startOffset","25%").attr("font-weight","bold").text(function(t){return t.data.chrom});var q=function(v){var t=(v.endAngle-v.startAngle)/v.value,u=l.range(0,v.value,25000000).map(function(w,x){return{radius:o.innerRadius,angle:w*t+v.startAngle,label:x===0?0:(x%3?null:o.formatNum(w))}});if(u.length<4){u[u.length-1].label=o.formatNum(Math.round((u[u.length-1].angle-v.startAngle)/t))}return u};var s=function(t){return t.angle>Math.PI?"rotate(180)translate(-16)":null};var r=g.filter(this.chroms_layout,function(t){return t.endAngle-t.startAngle>o.min_arc_len});this.drawTicks(this.parent_elt,r,q,s)}});g.extend(b.prototype,h);var f=k.extend({_render_chrom_data:function(n,q,o){var r=this._get_path_function(q,o);if(!r){return null}var p=n.datum(o.data),s=p.append("path").attr("class","chrom-data").attr("chrom",q.data.chrom).attr("d",r);return s},_get_path_function:function(q,p){if(typeof p==="string"||!p.data||p.data.length===0){return null}var n=l.scale.linear().domain(this.data_bounds).range(this.radius_bounds);var r=l.scale.linear().domain([0,p.data.length]).range([q.startAngle,q.endAngle]);var o=l.svg.line.radial().interpolate("linear").radius(function(s){return n(s[1])}).angle(function(t,s){return r(s)});return l.svg.area.radial().interpolate(o.interpolate()).innerRadius(n(0)).outerRadius(o.radius()).angle(o.angle())},render_labels:function(){var n=this,q=function(){return"rotate(90)"};var p=g.filter(this.chroms_layout,function(r){return r.endAngle-r.startAngle>0.08}),o=g.filter(p,function(s,r){return r%3===0});this.drawTicks(this.parent_elt,o,this._data_bounds_ticks_fn(),q,true)},_transition_labels:function(){var o=this,q=g.filter(this.chroms_layout,function(r){return r.endAngle-r.startAngle>0.08}),p=g.filter(q,function(s,r){return r%3===0}),n=g.flatten(g.map(p,function(r){return o._data_bounds_ticks_fn()(r)}));this.parent_elt.selectAll("g.tick").data(n).transition().attr("transform",function(r){return"rotate("+(r.angle*180/Math.PI-90)+")translate("+r.radius+",0)"})},_data_bounds_ticks_fn:function(){var n=this;visibleChroms=0;return function(o){return[{radius:n.radius_bounds[0],angle:o.startAngle,label:n.formatNum(n.data_bounds[0])},{radius:n.radius_bounds[1],angle:o.startAngle,label:n.formatNum(n.data_bounds[1])}]}},get_data_bounds:function(n){}});g.extend(f.prototype,h);var e=f.extend({get_data_bounds:function(o){var n=g.map(o,function(p){if(typeof p==="string"||!p.max){return 0}return p.max});return[0,(n&&typeof n!=="string"?g.max(n):0)]}});var d=f.extend({get_data_bounds:function(o){var n=g.flatten(g.map(o,function(p){if(p){return g.map(p.data,function(q){return q[1]})}else{return 0}}));return[g.min(n),g.max(n)]}});var j=k.extend({render:function(){var n=this;$.when(n.track.get("data_manager").data_is_ready()).then(function(){$.when(n.track.get("data_manager").get_genome_wide_data(n.genome)).then(function(q){var p=[],o=n.genome.get_chroms_info();g.each(q,function(u,t){var r=o[t].chrom;var s=g.map(u.data,function(w){var v=n._get_region_angle(r,w[1]),x=n._get_region_angle(w[3],w[4]);return{source:{startAngle:v,endAngle:v+0.01},target:{startAngle:x,endAngle:x+0.01}}});p=p.concat(s)});n.parent_elt.append("g").attr("class","chord").selectAll("path").data(p).enter().append("path").style("fill",n.get_fill_color()).attr("d",l.svg.chord().radius(n.radius_bounds[0])).style("opacity",1)})})},update_radius_bounds:function(n){this.radius_bounds=n;this.parent_elt.selectAll("path").transition().attr("d",l.svg.chord().radius(this.radius_bounds[0]))},_get_region_angle:function(p,n){var o=g.find(this.chroms_layout,function(q){return q.data.chrom===p});return o.endAngle-((o.endAngle-o.startAngle)*(o.data.len-n)/o.data.len)}});return{CircsterView:a}});
\ No newline at end of file
+define(["libs/underscore","libs/d3","viz/visualization"],function(g,l,i){var m=Backbone.Model.extend({is_visible:function(q,n){var o=q.getBoundingClientRect(),p=$("svg")[0].getBoundingClientRect();if(o.right<0||o.left>p.right||o.bottom<0||o.top>p.bottom){return false}return true}});var h={drawTicks:function(r,q,v,p,n){var u=r.append("g").selectAll("g").data(q).enter().append("g").selectAll("g").data(v).enter().append("g").attr("class","tick").attr("transform",function(w){return"rotate("+(w.angle*180/Math.PI-90)+")translate("+w.radius+",0)"});var t=[],s=[],o=function(w){return w.angle>Math.PI?"end":null};if(n){t=[0,0,0,-4];s=[4,0,"",".35em"];o=null}else{t=[1,0,4,0];s=[0,4,".35em",""]}u.append("line").attr("x1",t[0]).attr("y1",t[1]).attr("x2",t[2]).attr("y1",t[3]).style("stroke","#000");u.append("text").attr("x",s[0]).attr("y",s[1]).attr("dx",s[2]).attr("dy",s[3]).attr("text-anchor",o).attr("transform",p).text(function(w){return w.label})},formatNum:function(o,n){var q=null;if(o<1){q=o.toPrecision(n)}else{var p=Math.round(o.toPrecision(n));if(o<1000){q=p}else{if(o<1000000){q=Math.round((p/1000).toPrecision(3)).toFixed(0)+"K"}else{if(o<1000000000){q=Math.round((p/1000000).toPrecision(3)).toFixed(0)+"M"}}}}return q}};var c=Backbone.Model.extend({});var a=Backbone.View.extend({className:"circster",initialize:function(n){this.total_gap=n.total_gap;this.genome=n.genome;this.dataset_arc_height=n.dataset_arc_height;this.track_gap=10;this.label_arc_height=50;this.scale=1;this.circular_views=null;this.chords_views=null;this.model.get("tracks").on("add",this.add_track,this);this.model.get("tracks").on("remove",this.remove_track,this);this.get_circular_tracks()},get_circular_tracks:function(){return this.model.get("tracks").filter(function(n){return n.get("track_type")!=="DiagonalHeatmapTrack"})},get_chord_tracks:function(){return this.model.get("tracks").filter(function(n){return n.get("track_type")==="DiagonalHeatmapTrack"})},get_tracks_bounds:function(){var o=this.get_circular_tracks();dataset_arc_height=this.dataset_arc_height,min_dimension=Math.min(this.$el.width(),this.$el.height()),radius_start=min_dimension/2-o.length*(this.dataset_arc_height+this.track_gap)-(this.label_arc_height+this.track_gap),tracks_start_radii=l.range(radius_start,min_dimension/2,this.dataset_arc_height+this.track_gap);var n=this;return g.map(tracks_start_radii,function(p){return[p,p+n.dataset_arc_height]})},render:function(){var w=this,q=this.dataset_arc_height,n=w.$el.width(),v=w.$el.height(),s=this.get_circular_tracks(),p=this.get_chord_tracks(),r=this.get_tracks_bounds(),o=l.select(w.$el[0]).append("svg").attr("width",n).attr("height",v).attr("pointer-events","all").append("svg:g").call(l.behavior.zoom().on("zoom",function(){var x=l.event.scale;o.attr("transform","translate("+l.event.translate+") scale("+x+")");if(w.scale!==x){if(w.zoom_drag_timeout){clearTimeout(w.zoom_drag_timeout)}w.zoom_drag_timeout=setTimeout(function(){},400)}})).attr("transform","translate("+n/2+","+v/2+")").append("svg:g").attr("class","tracks");this.circular_views=s.map(function(y,z){var A=(y.get("track_type")==="LineTrack"?d:e),x=new A({el:o.append("g")[0],track:y,radius_bounds:r[z],genome:w.genome,total_gap:w.total_gap});x.render();return x});this.chords_views=p.map(function(y){var x=new j({el:o.append("g")[0],track:y,radius_bounds:r[0],genome:w.genome,total_gap:w.total_gap});x.render();return x});var u=this.circular_views[this.circular_views.length-1].radius_bounds[1],t=[u,u+this.label_arc_height];this.label_track_view=new b({el:o.append("g")[0],track:new c(),radius_bounds:t,genome:w.genome,total_gap:w.total_gap});this.label_track_view.render()},add_track:function(t){if(t.get("track_type")==="DiagonalHeatmapTrack"){var p=this.circular_views[0].radius_bounds,s=new j({el:l.select("g.tracks").append("g")[0],track:t,radius_bounds:p,genome:this.genome,total_gap:this.total_gap});s.render();this.chords_views.push(s)}else{var r=this.get_tracks_bounds();g.each(this.circular_views,function(v,w){v.update_radius_bounds(r[w])});g.each(this.chords_views,function(v){v.update_radius_bounds(r[0])});var q=this.circular_views.length,u=(t.get("track_type")==="LineTrack"?d:e),n=new u({el:l.select("g.tracks").append("g")[0],track:t,radius_bounds:r[q],genome:this.genome,total_gap:this.total_gap});n.render();this.circular_views.push(n);var o=r[r.length-1];o[1]=o[0];this.label_track_view.update_radius_bounds(o)}},remove_track:function(o,q,p){var n=this.circular_views[p.index];this.circular_views.splice(p.index,1);n.$el.remove();var r=this.get_tracks_bounds();g.each(this.circular_views,function(s,t){s.update_radius_bounds(r[t])})}});var k=Backbone.View.extend({tagName:"g",initialize:function(n){this.bg_stroke="ccc";this.loading_bg_fill="000";this.bg_fill="ccc";this.total_gap=n.total_gap;this.track=n.track;this.radius_bounds=n.radius_bounds;this.genome=n.genome;this.chroms_layout=this._chroms_layout();this.data_bounds=[];this.scale=1;this.parent_elt=l.select(this.$el[0])},get_fill_color:function(){var n=this.track.get("config").get_value("block_color");if(!n){n=this.track.get("config").get_value("color")}return n},render:function(){var r=this.parent_elt;if(!r){console.log("no parent elt")}var q=this.chroms_layout,t=l.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]),n=r.selectAll("g").data(q).enter().append("svg:g"),p=n.append("path").attr("d",t).attr("class","chrom-background").style("stroke",this.bg_stroke).style("fill",this.loading_bg_fill);p.append("title").text(function(v){return v.data.chrom});var o=this,s=o.track.get("data_manager"),u=(s?s.data_is_ready():true);$.when(u).then(function(){$.when(o._render_data(r)).then(function(){p.style("fill",o.bg_fill);o.render_labels()})})},render_labels:function(){},update_radius_bounds:function(o){this.radius_bounds=o;var n=l.svg.arc().innerRadius(this.radius_bounds[0]).outerRadius(this.radius_bounds[1]);this.parent_elt.selectAll("g>path.chrom-background").transition().duration(1000).attr("d",n);this._transition_chrom_data();this._transition_labels()},update_scale:function(q){var p=this.scale;this.scale=q;if(q<=p){return}var o=this,n=new m();this.parent_elt.selectAll("path.chrom-data").filter(function(s,r){return n.is_visible(this)}).each(function(x,t){var w=l.select(this),s=w.attr("chrom"),v=o.genome.get_chrom_region(s),u=o.track.get("data_manager"),r;if(!u.can_get_more_detailed_data(v)){return}r=o.track.get("data_manager").get_more_detailed_data(v,"Coverage",0,q);$.when(r).then(function(A){w.remove();o._update_data_bounds();var z=g.find(o.chroms_layout,function(B){return B.data.chrom===s});var y=o.get_fill_color();o._render_chrom_data(o.parent_elt,z,A).style("stroke",y).style("fill",y)})});return o},_transition_chrom_data:function(){var o=this.track,q=this.chroms_layout,n=this.parent_elt.selectAll("g>path.chrom-data"),r=n[0].length;if(r>0){var p=this;$.when(o.get("data_manager").get_genome_wide_data(this.genome)).then(function(t){var s=g.reject(g.map(t,function(u,v){var w=null,x=p._get_path_function(q[v],u);if(x){w=x(u.data)}return w}),function(u){return u===null});n.each(function(v,u){l.select(this).transition().duration(1000).attr("d",s[u])})})}},_transition_labels:function(){},_update_data_bounds:function(){var n=this.data_bounds;this.data_bounds=this.get_data_bounds(this.track.get("data_manager").get_genome_wide_data(this.genome));if(this.data_bounds[0]<n[0]||this.data_bounds[1]>n[1]){this._transition_chrom_data()}},_render_data:function(q){var p=this,o=this.chroms_layout,n=this.track,r=$.Deferred();$.when(n.get("data_manager").get_genome_wide_data(this.genome)).then(function(t){p.data_bounds=p.get_data_bounds(t);layout_and_data=g.zip(o,t),chroms_data_layout=g.map(layout_and_data,function(u){var v=u[0],w=u[1];return p._render_chrom_data(q,v,w)});var s=p.get_fill_color();p.parent_elt.selectAll("path.chrom-data").style("stroke",s).style("fill",s);r.resolve(q)});return r},_render_chrom_data:function(n,o,p){},_get_path_function:function(o,n){},_chroms_layout:function(){var o=this.genome.get_chroms_info(),q=l.layout.pie().value(function(s){return s.len}).sort(null),r=q(o),n=this.total_gap/o.length,p=g.map(r,function(u,t){var s=u.endAngle-n;u.endAngle=(s>u.startAngle?s:u.startAngle);return u});return p}});var b=k.extend({initialize:function(n){k.prototype.initialize.call(this,n);this.innerRadius=this.radius_bounds[0];this.radius_bounds[0]=this.radius_bounds[1];this.bg_stroke="fff";this.bg_fill="fff";this.min_arc_len=0.08},_render_data:function(p){var o=this,n=p.selectAll("g");n.selectAll("path").attr("id",function(t){return"label-"+t.data.chrom});n.append("svg:text").filter(function(t){return t.endAngle-t.startAngle>o.min_arc_len}).attr("text-anchor","middle").append("svg:textPath").attr("xlink:href",function(t){return"#label-"+t.data.chrom}).attr("startOffset","25%").attr("font-weight","bold").text(function(t){return t.data.chrom});var q=function(v){var t=(v.endAngle-v.startAngle)/v.value,u=l.range(0,v.value,25000000).map(function(w,x){return{radius:o.innerRadius,angle:w*t+v.startAngle,label:x===0?0:(x%3?null:o.formatNum(w))}});if(u.length<4){u[u.length-1].label=o.formatNum(Math.round((u[u.length-1].angle-v.startAngle)/t))}return u};var s=function(t){return t.angle>Math.PI?"rotate(180)translate(-16)":null};var r=g.filter(this.chroms_layout,function(t){return t.endAngle-t.startAngle>o.min_arc_len});this.drawTicks(this.parent_elt,r,q,s)}});g.extend(b.prototype,h);var f=k.extend({_quantile:function(o,n){o.sort(l.ascending);return l.quantile(o,n)},_render_chrom_data:function(n,q,o){var r=this._get_path_function(q,o);if(!r){return null}var p=n.datum(o.data),s=p.append("path").attr("class","chrom-data").attr("chrom",q.data.chrom).attr("d",r);return s},_get_path_function:function(q,p){if(typeof p==="string"||!p.data||p.data.length===0){return null}var n=l.scale.linear().domain(this.data_bounds).range(this.radius_bounds).clamp(true);var r=l.scale.linear().domain([0,p.data.length]).range([q.startAngle,q.endAngle]);var o=l.svg.line.radial().interpolate("linear").radius(function(s){return n(s[1])}).angle(function(t,s){return r(s)});return l.svg.area.radial().interpolate(o.interpolate()).innerRadius(n(0)).outerRadius(o.radius()).angle(o.angle())},render_labels:function(){var n=this,q=function(){return"rotate(90)"};var p=g.filter(this.chroms_layout,function(r){return r.endAngle-r.startAngle>0.08}),o=g.filter(p,function(s,r){return r%3===0});this.drawTicks(this.parent_elt,o,this._data_bounds_ticks_fn(),q,true)},_transition_labels:function(){var o=this,q=g.filter(this.chroms_layout,function(r){return r.endAngle-r.startAngle>0.08}),p=g.filter(q,function(s,r){return r%3===0}),n=g.flatten(g.map(p,function(r){return o._data_bounds_ticks_fn()(r)}));this.parent_elt.selectAll("g.tick").data(n).transition().attr("transform",function(r){return"rotate("+(r.angle*180/Math.PI-90)+")translate("+r.radius+",0)"})},_data_bounds_ticks_fn:function(){var n=this;visibleChroms=0;return function(o){return[{radius:n.radius_bounds[0],angle:o.startAngle,label:n.formatNum(n.data_bounds[0])},{radius:n.radius_bounds[1],angle:o.startAngle,label:n.formatNum(n.data_bounds[1])}]}},get_data_bounds:function(n){}});g.extend(f.prototype,h);var e=f.extend({get_data_bounds:function(o){var n=g.map(o,function(p){if(typeof p==="string"||!p.max){return 0}return p.max});return[0,(n&&typeof n!=="string"?this._quantile(values,0.98):0)]}});var d=f.extend({get_data_bounds:function(o){var n=g.flatten(g.map(o,function(p){if(p){return g.map(p.data,function(q){return q[1]})}else{return 0}}));return[g.min(n),this._quantile(n,0.98)]}});var j=k.extend({render:function(){var n=this;$.when(n.track.get("data_manager").data_is_ready()).then(function(){$.when(n.track.get("data_manager").get_genome_wide_data(n.genome)).then(function(q){var p=[],o=n.genome.get_chroms_info();g.each(q,function(u,t){var r=o[t].chrom;var s=g.map(u.data,function(w){var v=n._get_region_angle(r,w[1]),x=n._get_region_angle(w[3],w[4]);return{source:{startAngle:v,endAngle:v+0.01},target:{startAngle:x,endAngle:x+0.01}}});p=p.concat(s)});n.parent_elt.append("g").attr("class","chord").selectAll("path").data(p).enter().append("path").style("fill",n.get_fill_color()).attr("d",l.svg.chord().radius(n.radius_bounds[0])).style("opacity",1)})})},update_radius_bounds:function(n){this.radius_bounds=n;this.parent_elt.selectAll("path").transition().attr("d",l.svg.chord().radius(this.radius_bounds[0]))},_get_region_angle:function(p,n){var o=g.find(this.chroms_layout,function(q){return q.data.chrom===p});return o.endAngle-((o.endAngle-o.startAngle)*(o.data.len-n)/o.data.len)}});return{CircsterView:a}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/5dcbbdfe1087/
changeset: 5dcbbdfe1087
user: dan
date: 2012-11-06 18:57:23
summary: Allow rerun to access hidden datasets.
affected #: 1 file
diff -r 6624cd467f30618d5a1319e14b2a41ab7c6a2407 -r 5dcbbdfe1087e8d75f1df939d363b347e2764dd5 lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -1434,7 +1434,7 @@
else:
hid = str( hda.hid )
if not hda.dataset.state in [galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED] and \
- hda.visible and \
+ ( hda.visible or ( value and hda in value and not hda.implicitly_converted_parent_datasets ) ) and \
trans.app.security_agent.can_access_dataset( current_user_roles, hda.dataset ):
# If we are sending data to an external application, then we need to make sure there are no roles
# associated with the dataset that restrict it's access from "public".
@@ -1444,7 +1444,11 @@
continue
if isinstance( hda.datatype, self.formats):
selected = ( value and ( hda in value ) )
- field.add_option( "%s: %s" % ( hid, hda_name ), hda.id, selected )
+ if hda.visible:
+ hidden_text = ""
+ else:
+ hidden_text = " (hidden)"
+ field.add_option( "%s:%s %s" % ( hid, hidden_text, hda_name ), hda.id, selected )
else:
target_ext, converted_dataset = hda.find_conversion_destination( self.formats )
if target_ext:
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: richard_burhans: Fixed installation of toolsheds containing proprietary datatypes
by Bitbucket 06 Nov '12
by Bitbucket 06 Nov '12
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/6624cd467f30/
changeset: 6624cd467f30
user: richard_burhans
date: 2012-11-06 17:22:56
summary: Fixed installation of toolsheds containing proprietary datatypes
affected #: 1 file
diff -r efccb227d72ccb2764544fd461195789653b4509 -r 6624cd467f30618d5a1319e14b2a41ab7c6a2407 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
@@ -745,10 +745,11 @@
Generate the metadata for the installed tool shed repository, among other things. This method is called from Galaxy (never the tool shed)
when an admin is installing a new repository or reinstalling an uninstalled repository.
"""
+ shed_config_dict = trans.app.toolbox.get_shed_config_dict_by_filename( shed_tool_conf )
metadata_dict, invalid_file_tups = generate_metadata_for_changeset_revision( app=trans.app,
repository=tool_shed_repository,
repository_clone_url=repository_clone_url,
- shed_config_dict = trans.app.toolbox.get_shed_config_dict_by_filename( shed_tool_conf ),
+ shed_config_dict=shed_config_dict,
relative_install_dir=relative_install_dir,
repository_files_dir=None,
resetting_all_metadata_on_repository=False,
@@ -791,9 +792,12 @@
tool_shed_repository.includes_datatypes = True
trans.sa_session.add( tool_shed_repository )
trans.sa_session.flush()
- datatypes_config = get_config_from_disk( 'datatypes_conf.xml', relative_install_dir )
+ files_dir = relative_install_dir
+ if shed_config_dict.get( 'tool_path' ):
+ files_dir = os.path.join( shed_config_dict['tool_path'], files_dir )
+ datatypes_config = get_config_from_disk( 'datatypes_conf.xml', files_dir )
# Load data types required by tools.
- converter_path, display_path = alter_config_and_load_prorietary_datatypes( trans.app, datatypes_config, relative_install_dir, override=False )
+ converter_path, display_path = alter_config_and_load_prorietary_datatypes( trans.app, datatypes_config, files_dir, override=False )
if converter_path or display_path:
# Create a dictionary of tool shed repository related information.
repository_dict = create_repository_dict_for_proprietary_datatypes( tool_shed=tool_shed,
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: jgoecks: Circster improvements: (a) fetch a smaller amount of data on load and (b) use large quantile rather than max for top data range to limit impact of exceptionally large values.
by Bitbucket 06 Nov '12
by Bitbucket 06 Nov '12
06 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/efccb227d72c/
changeset: efccb227d72c
user: jgoecks
date: 2012-11-06 15:52:23
summary: Circster improvements: (a) fetch a smaller amount of data on load and (b) use large quantile rather than max for top data range to limit impact of exceptionally large values.
affected #: 2 files
diff -r dd4786c5b4e48db780f6ec5da42dfafec19538f4 -r efccb227d72ccb2764544fd461195789653b4509 lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -636,9 +636,12 @@
data_provider = trans.app.data_provider_registry.get_data_provider( trans,
original_dataset=dataset,
source=source )
- # HACK: pass in additional params which are used for only some types of data providers;
- # level, cutoffs used for summary tree, and interchromosomal used for chromatin interactions.
- rval = data_provider.get_genome_data( chroms_info, level=4, detail_cutoff=0, draw_cutoff=0,
+ # HACK: pass in additional params which are used for only some
+ # types of data providers; level, cutoffs used for summary tree,
+ # num_samples for BBI, and interchromosomal used for chromatin interactions.
+ rval = data_provider.get_genome_data( chroms_info,
+ level=4, detail_cutoff=0, draw_cutoff=0,
+ num_samples=150,
interchromosomal=True )
return rval
diff -r dd4786c5b4e48db780f6ec5da42dfafec19538f4 -r efccb227d72ccb2764544fd461195789653b4509 static/scripts/viz/circster.js
--- a/static/scripts/viz/circster.js
+++ b/static/scripts/viz/circster.js
@@ -720,6 +720,14 @@
var CircsterQuantitativeTrackView = CircsterTrackView.extend({
/**
+ * Returns quantile for an array of numbers.
+ */
+ _quantile: function(numbers, quantile) {
+ numbers.sort(d3.ascending);
+ return d3.quantile(numbers, quantile);
+ },
+
+ /**
* Renders quantitative data with the form [x, value] and assumes data is equally spaced across
* chromosome. Attachs a dict with track and chrom name information to DOM element.
*/
@@ -750,7 +758,8 @@
// Radius scaler.
var radius = d3.scale.linear()
.domain(this.data_bounds)
- .range(this.radius_bounds);
+ .range(this.radius_bounds)
+ .clamp(true);
// Scaler for placing data points across arc.
var angle = d3.scale.linear()
@@ -854,7 +863,7 @@
if (typeof d === 'string' || !d.max) { return 0; }
return d.max;
});
- return [ 0, (max_data && typeof max_data !== 'string' ? _.max(max_data) : 0) ];
+ return [ 0, (max_data && typeof max_data !== 'string' ? this._quantile(values, 0.98) : 0) ];
}
});
@@ -865,7 +874,7 @@
get_data_bounds: function(data) {
// Set max across dataset by extracting all values, flattening them into a
- // single array, and getting the min and max.
+ // single array, and getting third quartile.
var values = _.flatten( _.map(data, function(d) {
if (d) {
// Each data point has the form [position, value], so return all values.
@@ -878,7 +887,7 @@
}
}) );
- return [ _.min(values), _.max(values) ];
+ return [ _.min(values), this._quantile(values, 0.98) ];
}
});
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