galaxy-commits
Threads by month
- ----- 2025 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 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
- 15302 discussions

commit/galaxy-central: carlfeberhard: scatterplot: renaming/re-organization pass; layout change to vertical tabs and inclusion of chart under tabs; pack scripts
by Bitbucket 27 Nov '12
by Bitbucket 27 Nov '12
27 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/9bf411ee2476/
changeset: 9bf411ee2476
user: carlfeberhard
date: 2012-11-27 17:19:32
summary: scatterplot: renaming/re-organization pass; layout change to vertical tabs and inclusion of chart under tabs; pack scripts
affected #: 19 files
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/mvc/visualizations/scatterplotControlForm.js
--- /dev/null
+++ b/static/scripts/mvc/visualizations/scatterplotControlForm.js
@@ -0,0 +1,602 @@
+/* =============================================================================
+todo:
+ I'd like to move the svg creation out of the splot constr. to:
+ allow adding splots to an existing canvas
+ allow mult. splots sharing a canvas
+
+
+ outside this:
+ BUG: setting width, height in plot controls doesn't re-interpolate data locations!!
+ BUG?: get metadata_column_names (from datatype if necessary)
+ BUG: single vis in popupmenu should have tooltip with that name NOT 'Visualizations'
+
+ wire label setters, anim setter
+
+ TwoVarScatterplot:
+ ??: maybe better to do this with a canvas...
+ save as visualization
+ to seperate file?
+ remove underscore dependencies
+ add interface to change values (seperate)?
+ download svg -> base64 encode
+ incorporate glyphs, glyph state renderers
+
+ ScatterplotSettingsForm:
+ some css bug that lowers the width of settings form when plot-controls tab is open
+ causes chart to shift
+ what can be abstracted/reused for other graphs?
+ avoid direct manipulation of this.plot
+ allow option to put plot into seperate tab of interface (for small multiples)
+
+ provide callback in view to load data incrementally - for large sets
+ paginate
+ handle rerender
+ use endpoint (here and on the server (fileptr))
+ fetch (new?) data
+ handle rerender
+ use d3.TSV?
+ render warning on long data (> maxDataPoints)
+ adjust endpoint
+
+ selectable list of preset column comparisons (rnaseq etc.)
+ how to know what sort of Tabular the data is?
+ smarter about headers
+ validate columns selection (here or server)
+
+ set stats column names by selected columns
+ move chart into tabbed area...
+
+ Scatterplot.mako:
+ multiple plots on one page (small multiples)
+ ?? ensure svg styles thru d3 or css?
+ d3: configable (easily)
+ css: standard - better maintenance
+ ? override at config
+
+============================================================================= */
+/**
+ * Scatterplot control UI as a backbone view
+ * handles:
+ * getting the desired data
+ * configuring the plot display
+ * showing (general) statistics
+ *
+ * initialize attributes REQUIRES a dataset and an apiDatasetsURL
+ */
+var ScatterplotControlForm = BaseView.extend( LoggableMixin ).extend({
+ //logger : console,
+ className : 'scatterplot-control-form',
+
+ dataLoadDelay : 500,
+ dataLoadSize : 3001,
+
+ loadingIndicatorImage : 'loading_small_white_bg.gif',
+ fetchMsg : 'Fetching data...',
+ renderMsg : 'Rendering...',
+
+ initialize : function( attributes ){
+ this.log( this + '.initialize, attributes:', attributes );
+
+ this.dataset = null;
+ this.chartConfig = null;
+ this.chart = null;
+ this.loader = null;
+
+ // set up refs to the four tab areas
+ this.$dataControl = null;
+ this.$chartControl = null;
+ this.$statsDisplay = null;
+ this.$chartDisplay = null;
+
+ this.dataFetch = null;
+
+ this.initializeFromAttributes( attributes );
+ this.initializeChart( attributes );
+ this.initializeDataLoader( attributes );
+ },
+
+ initializeFromAttributes : function( attributes ){
+ // required settings: ensure certain vars we need are passed in attributes
+ if( !attributes || !attributes.dataset ){
+ throw( "ScatterplotView requires a dataset" );
+ } else {
+ this.dataset = attributes.dataset;
+ }
+ if( jQuery.type( this.dataset.metadata_column_types ) === 'string' ){
+ this.dataset.metadata_column_types = this.dataset.metadata_column_types.split( ', ' );
+ }
+ this.log( '\t dataset:', this.dataset );
+
+ // passed from mako helper
+ //TODO: integrate to galaxyPaths
+ //TODO: ?? seems like data loader section would be better
+ if( !attributes.apiDatasetsURL ){
+ throw( "ScatterplotView requires a apiDatasetsURL" );
+ } else {
+ this.dataURL = attributes.apiDatasetsURL + '/' + this.dataset.id + '?';
+ }
+ this.log( '\t dataURL:', this.dataURL );
+ },
+
+ initializeChart : function( attributes ){
+ // set up the basic chart infrastructure and config (if any)
+ this.chartConfig = attributes.chartConfig || {};
+ //if( this.logger ){ this.chartConfig.debugging = true; }
+ this.log( '\t initial chartConfig:', this.chartConfig );
+
+ this.chart = new TwoVarScatterplot( this.chartConfig );
+ //TODO: remove 2nd ref, use this.chart.config
+ this.chartConfig = this.chart.config;
+ },
+
+ initializeDataLoader : function( attributes ){
+ // set up data loader
+ var view = this;
+ this.loader = new LazyDataLoader({
+ //logger : ( this.logger )?( this.logger ):( null ),
+ // we'll generate this when columns are chosen
+ url : null,
+ start : attributes.start || 0,
+ //NOTE: metadata_data_lines can be null (so we won't know the total)
+ total : attributes.total || this.dataset.metadata_data_lines,
+ delay : this.dataLoadDelay,
+ size : this.dataLoadSize,
+
+ buildUrl : function( start, size ){
+ // currently VERY SPECIFIC to using data_providers.py start_val, max_vals params
+ return this.url + '&' + jQuery.param({
+ start_val: start,
+ max_vals: size
+ });
+ }
+ });
+ $( this.loader ).bind( 'error', function( event, status, error ){
+ view.log( 'ERROR:', status, error );
+ alert( 'ERROR fetching data:\n' + status + '\n' + error );
+ view.hideLoadingIndicator();
+ });
+ },
+
+ // ------------------------------------------------------------------------- CONTROLS RENDERING
+ render : function(){
+ this.log( this + '.render' );
+
+ // render the tab controls, areas and loading indicator
+ this.$el.append( ScatterplotControlForm.templates.mainLayout({
+ loadingIndicatorImagePath : galaxy_paths.get( 'image_path' ) + '/' + this.loadingIndicatorImage,
+ message : ''
+ }));
+
+ // render the tab content
+ this.$dataControl = this._render_dataControl();
+ this.$chartControl = this._render_chartControl();
+ this.$statsDisplay = this.$el.find( '.tab-pane#stats-display' );
+ this.$chartDisplay = this._render_chartDisplay();
+
+ // auto render if given both x, y column choices in query for page
+ //TODO:?? add autoRender=1 to query maybe?
+ if( this.chartConfig.xColumn && this.chartConfig.yColumn ){
+ this.renderChart();
+ }
+
+ // set up behaviours
+ this.$el.find( '.tooltip' ).tooltip();
+
+ // uncomment any of the following to have that tab show on initial load (for testing)
+ //this.$el.find( 'ul.nav' ).find( 'a[href="#data-control"]' ).tab( 'show' );
+ //this.$el.find( 'ul.nav' ).find( 'a[href="#chart-control"]' ).tab( 'show' );
+ //this.$el.find( 'ul.nav' ).find( 'a[href="#stats-display"]' ).tab( 'show' );
+ //this.$el.find( 'ul.nav' ).find( 'a[href="#chart-display"]' ).tab( 'show' );
+ return this;
+ },
+
+ _render_dataControl : function(){
+ // controls for which columns are used to plot datapoints (and ids/additional info to attach if desired)
+ var view = this,
+ allColumns = [],
+ numericColumns = [];
+
+ // gather column indeces (from metadata_column_types) and names (from metadata_columnnames)
+ _.each( this.dataset.metadata_column_types, function( type, index ){
+ // use a 1 based index in names/values within the form (will be dec. when parsed out)
+ var oneBasedIndex = index + 1,
+ // label with the name if available (fall back on 'column <index>')
+ name = 'column ' + oneBasedIndex;
+ if( view.dataset.metadata_column_names ){
+ name = view.dataset.metadata_column_names[ index ];
+ }
+
+ // cache all columns here
+ allColumns.push({ index: oneBasedIndex, name: name });
+
+ // filter numeric columns to their own list
+ if( type === 'int' || type === 'float' ){
+ numericColumns.push({ index: oneBasedIndex, name: name });
+ }
+ });
+ //TODO: other vals: max_vals, start_val, pagination (chart-settings)
+
+ // render the html
+ var $dataControl = this.$el.find( '.tab-pane#data-control' );
+ $dataControl.append( ScatterplotControlForm.templates.dataControl({
+ allColumns : allColumns,
+ numericColumns : numericColumns
+ }));
+
+ // preset to column selectors if they were passed in the config in the query string
+ $dataControl.find( '#X-select' ).val( this.chartConfig.xColumn );
+ $dataControl.find( '#Y-select' ).val( this.chartConfig.yColumn );
+ if( this.chartConfig.idColumn !== undefined ){
+ $dataControl.find( '#include-id-checkbox' )
+ .attr( 'checked', true ).trigger( 'change' );
+ $dataControl.find( '#ID-select' ).val( this.chartConfig.idColumn );
+ }
+
+ return $dataControl;
+ },
+
+ _render_chartControl : function(){
+ // tab content to control how the chart is rendered (data glyph size, chart size, etc.)
+ var view = this,
+ $chartControl = this.$el.find( '.tab-pane#chart-control' ),
+ // limits for controls (by control/chartConfig id)
+ //TODO: move into TwoVarScatterplot
+ controlRanges = {
+ 'datapointSize' : { min: 2, max: 10, step: 1 },
+ 'width' : { min: 200, max: 800, step: 20 },
+ 'height' : { min: 200, max: 800, step: 20 }
+ };
+
+ // render the html
+ $chartControl.append( ScatterplotControlForm.templates.chartControl( this.chartConfig ) );
+
+ // set up behaviours, js on sliders
+ $chartControl.find( '.numeric-slider-input' ).each( function(){
+ var $this = $( this ),
+ $output = $this.find( '.slider-output' ),
+ $slider = $this.find( '.slider' ),
+ id = $this.attr( 'id' );
+ //chartControl.log( 'slider set up', 'this:', $this, 'slider:', $slider, 'id', id );
+
+ // what to do when the slider changes: update display and update chartConfig
+ //TODO: move out of loop
+ function onSliderChange(){
+ var $this = $( this ),
+ newValue = $this.slider( 'value' );
+ //chartControl.log( 'slider change', 'this:', $this, 'output:', $output, 'value', newValue );
+ $output.text( newValue );
+ //chartControl.chartConfig[ id ] = newValue;
+ }
+
+ $slider.slider( _.extend( controlRanges[ id ], {
+ value : view.chartConfig[ id ],
+ change : onSliderChange,
+ slide : onSliderChange
+ }));
+ });
+
+ return $chartControl;
+ },
+
+ _render_chartDisplay : function(){
+ // render the tab content where the chart is displayed (but not the chart itself)
+ var $chartDisplay = this.$el.find( '.tab-pane#chart-display' );
+ $chartDisplay.append( ScatterplotControlForm.templates.chartDisplay( this.chartConfig ) );
+ return $chartDisplay;
+ },
+
+ // ------------------------------------------------------------------------- EVENTS
+ events : {
+ 'change #include-id-checkbox' : 'toggleThirdColumnSelector',
+ 'click #data-control #render-button' : 'renderChart',
+ 'click #chart-control #render-button' : 'changeChartSettings'
+ },
+
+ toggleThirdColumnSelector : function(){
+ // show/hide the id selector on the data settings panel
+ this.$el.find( 'select[name="ID"]' ).parent().toggle();
+ },
+
+ showLoadingIndicator : function( message, callback ){
+ // display the loading indicator over the tab panels if hidden, update message (if passed)
+ message = message || '';
+ var indicator = this.$el.find( 'div#loading-indicator' );
+ messageBox = indicator.find( '.loading-message' );
+
+ if( indicator.is( ':visible' ) ){
+ if( message ){
+ messageBox.fadeOut( 'fast', function(){
+ messageBox.text( message );
+ messageBox.fadeIn( 'fast', callback );
+ });
+ } else {
+ callback();
+ }
+
+ } else {
+ if( message ){ messageBox.text( message ); }
+ indicator.fadeIn( 'fast', callback );
+ }
+ },
+
+ hideLoadingIndicator : function( callback ){
+ this.$el.find( 'div#loading-indicator' ).fadeOut( 'fast', callback );
+ },
+
+ // ------------------------------------------------------------------------- CHART/STATS RENDERING
+ renderChart : function(){
+ // fetch the data, (re-)render the chart
+ this.log( this + '.renderChart' );
+
+ //TODO: separate data fetch
+
+ // this is a complete re-render, so clear the prev. data
+ this.data = null;
+ this.meta = null;
+
+ // update the chartConfig (here and chart) using chart settings
+ //TODO: separate and improve (used in changeChartSettings too)
+ _.extend( this.chartConfig, this.getChartSettings() );
+ this.log( '\t chartConfig:', this.chartConfig );
+ this.chart.updateConfig( this.chartConfig, false );
+
+ // build the url with the current data settings
+ this.loader.url = this.dataURL + '&' + jQuery.param( this.getDataSettings() );
+ this.log( '\t loader: total lines:', this.loader.total, ' url:', this.loader.url );
+
+ // bind the new data event to: aggregate data, update the chart and stats with new data
+ var view = this;
+ $( this.loader ).bind( 'loaded.new', function( event, response ){
+ view.log( view + ' loaded.new', response );
+
+ // aggregate data and meta
+ view.postProcessDataFetchResponse( response );
+ view.log( '\t postprocessed data:', view.data );
+ view.log( '\t postprocessed meta:', view.meta );
+
+ // update the chart and stats
+ view.showLoadingIndicator( view.renderMsg, function(){
+ view.chart.render( view.data, view.meta );
+ view.renderStats( view.data, view.meta );
+ view.hideLoadingIndicator();
+ });
+ });
+ // when all data loaded - unbind (or we'll start doubling event handlers)
+ $( this.loader ).bind( 'complete', function( event, data ){
+ view.log( view + ' complete', data );
+ $( view.loader ).unbind();
+ });
+
+ // begin loading the data, switch to the chart display tab
+ view.showLoadingIndicator( view.fetchMsg, function(){
+ view.$el.find( 'ul.nav' ).find( 'a[href="#chart-display"]' ).tab( 'show' );
+ view.loader.load();
+ });
+ },
+
+ renderStats : function(){
+ this.log( this + '.renderStats' );
+ // render the stats table in the stats panel
+ //TODO: there's a better way
+ this.$statsDisplay.html( ScatterplotControlForm.templates.statsDisplay({
+ stats: [
+ { name: 'Count', xval: this.meta[0].count, yval: this.meta[1].count },
+ { name: 'Min', xval: this.meta[0].min, yval: this.meta[1].min },
+ { name: 'Max', xval: this.meta[0].max, yval: this.meta[1].max },
+ { name: 'Sum', xval: this.meta[0].sum, yval: this.meta[1].sum },
+ { name: 'Mean', xval: this.meta[0].mean, yval: this.meta[1].mean },
+ { name: 'Median', xval: this.meta[0].median, yval: this.meta[1].median }
+ ]
+ }));
+ },
+
+ changeChartSettings : function(){
+ // re-render the chart with new chart settings and OLD data
+ var view = this;
+ newChartSettings = this.getChartSettings();
+
+ // update the chart config from the chartSettings panel controls
+ _.extend( this.chartConfig, newChartSettings );
+ this.log( 'this.chartConfig:', this.chartConfig );
+ this.chart.updateConfig( this.chartConfig, false );
+
+ // if there's current data, call chart.render with it (no data fetch)
+ if( view.data && view.meta ){
+ view.showLoadingIndicator( view.renderMsg, function(){
+ view.$el.find( 'ul.nav' ).find( 'a[href="#chart-display"]' ).tab( 'show' );
+ view.chart.render( view.data, view.meta );
+ view.hideLoadingIndicator();
+ });
+
+ // no current data, call renderChart instead (which will fetch data)
+ } else {
+ this.renderChart();
+ }
+ },
+
+ // ------------------------------------------------------------------------- DATA AGGREGATION
+ postProcessDataFetchResponse : function( response ){
+ // the loader only returns new data - it's up to this to munge the fetches together properly
+ //TODO: we're now storing data in two places: loader and here
+ // can't we reduce incoming data into loader.data[0]? are there concurrency problems?
+ this.postProcessData( response.data );
+ this.postProcessMeta( response.meta );
+ },
+
+ postProcessData : function( newData ){
+ // stack the column data on top of each other into this.data
+ //this.log( this + '.postProcessData:', newData );
+ var view = this;
+
+ // if we already have data: aggregate
+ if( view.data ){
+ _.each( newData, function( newColData, colIndex ){
+ //view.log( colIndex + ' data:', newColData );
+ //TODO??: time, space efficiency of this?
+ view.data[ colIndex ] = view.data[ colIndex ].concat( newColData );
+ });
+
+ // otherwise: assign (first load)
+ } else {
+ view.data = newData;
+ }
+ },
+
+ postProcessMeta : function( newMeta ){
+ // munge the meta data (stats) from the server fetches together
+ //pre: this.data must be preprocessed (needed for medians)
+ //this.log( this + '.postProcessMeta:', newMeta );
+ var view = this,
+ colTypes = this.dataset.metadata_column_types;
+
+ // if we already have meta: aggregate
+ if( view.meta ){
+ _.each( newMeta, function( newColMeta, colIndex ){
+ var colMeta = view.meta[ colIndex ],
+ colType = colTypes[ colIndex ];
+ //view.log( '\t ' + colIndex + ' postprocessing meta:', newColMeta );
+ //view.log( colIndex + ' old meta:',
+ // 'min:', colMeta.min,
+ // 'max:', colMeta.max,
+ // 'sum:', colMeta.sum,
+ // 'mean:', colMeta.mean,
+ // 'median:', colMeta.median
+ //);
+
+ //!TODO: at what point are we getting int/float overflow on these?!
+ //??: need to be null safe?
+ colMeta.count += ( newColMeta.count )?( newColMeta.count ):( 0 );
+ //view.log( colIndex, 'count:', colMeta.count );
+
+ if( ( colType === 'int' ) || ( colType === 'float' ) ){
+ //view.log( colIndex + ' incoming meta:',
+ // 'min:', newColMeta.min,
+ // 'max:', newColMeta.max,
+ // 'sum:', newColMeta.sum,
+ // 'mean:', newColMeta.mean,
+ // 'median:', newColMeta.median
+ //);
+
+ colMeta.min = Math.min( newColMeta.min, colMeta.min );
+ colMeta.max = Math.max( newColMeta.max, colMeta.max );
+ colMeta.sum = newColMeta.sum + colMeta.sum;
+ colMeta.mean = ( colMeta.count )?( colMeta.sum / colMeta.count ):( null );
+
+ // median's a pain bc of sorting (requires the data as well)
+ var sortedCol = view.data[ colIndex ].slice().sort(),
+ middleIndex = Math.floor( sortedCol.length / 2 );
+
+ if( sortedCol.length % 2 === 0 ){
+ colMeta.median = ( ( sortedCol[ middleIndex ] + sortedCol[( middleIndex + 1 )] ) / 2 );
+
+ } else {
+ colMeta.median = sortedCol[ middleIndex ];
+ }
+
+ //view.log( colIndex + ' new meta:',
+ // 'min:', colMeta.min,
+ // 'max:', colMeta.max,
+ // 'sum:', colMeta.sum,
+ // 'mean:', colMeta.mean,
+ // 'median:', colMeta.median
+ //);
+ }
+ });
+
+ // otherwise: assign (first load)
+ } else {
+ view.meta = newMeta;
+ //view.log( '\t meta (first load):', view.meta );
+ }
+ },
+
+ // ------------------------------------------------------------------------- GET DATA/CHART SETTINGS
+ getDataSettings : function(){
+ // parse the column values for both indeces (for the data fetch) and names (for the chart)
+ var columnSelections = this.getColumnSelections(),
+ columns = [];
+ this.log( '\t columnSelections:', columnSelections );
+
+ //TODO: validate columns - minimally: we can assume either set by selectors or via a good query string
+
+ // get column indices for params, include the desired ID column (if any)
+ //NOTE: these are presented in human-readable 1 base index (to match the data.peek) - adjust
+ columns = [
+ columnSelections.X.colIndex - 1,
+ columnSelections.Y.colIndex - 1
+ ];
+ if( this.$dataControl.find( '#include-id-checkbox' ).attr( 'checked' ) ){
+ columns.push( columnSelections.ID.colIndex - 1 );
+ }
+ //TODO: other vals: max, start, page
+
+ var params = {
+ data_type : 'raw_data',
+ columns : '[' + columns + ']'
+ };
+ this.log( '\t data settings (url params):', params );
+ return params;
+ },
+
+ getColumnSelections : function(){
+ // gets the current user-selected values for which columns to fetch from the data settings panel
+ // returns a map: { column-select name (eg. X) : { colIndex : column-selector val,
+ // colName : selected option text }, ... }
+ var selections = {};
+ this.$dataControl.find( 'div.column-select select' ).each( function(){
+ var $this = $( this ),
+ val = $this.val();
+ selections[ $this.attr( 'name' ) ] = {
+ colIndex : val,
+ colName : $this.children( '[value="' + val + '"]' ).text()
+ };
+ });
+ return selections;
+ },
+
+ getChartSettings : function(){
+ // gets the user-selected chartConfig from the chart settings panel
+ var settings = {},
+ colSelections = this.getColumnSelections();
+ //this.log( 'colSelections:', colSelections );
+
+ //TODO: simplify with keys and loop
+ settings.datapointSize = this.$chartControl.find( '#datapointSize.numeric-slider-input' )
+ .find( '.slider' ).slider( 'value' );
+ settings.width = this.$chartControl.find( '#width.numeric-slider-input' )
+ .find( '.slider' ).slider( 'value' );
+ settings.height = this.$chartControl.find( '#height.numeric-slider-input' )
+ .find( '.slider' ).slider( 'value' );
+
+ // update axes labels using chartSettings inputs (if not at defaults), otherwise the selects' colName
+ //TODO: a little confusing
+ var chartSettingsXLabel = this.$chartControl.find( 'input#X-axis-label' ).val(),
+ chartSettingsYLabel = this.$chartControl.find( 'input#Y-axis-label' ).val();
+ settings.xLabel = ( chartSettingsXLabel === 'X' )?
+ ( colSelections.X.colName ):( chartSettingsXLabel );
+ settings.yLabel = ( chartSettingsYLabel === 'Y' )?
+ ( colSelections.Y.colName ):( chartSettingsYLabel );
+
+ settings.animDuration = 10;
+ if( this.$chartControl.find( '#animDuration.checkbox-input' ).is( ':checked' ) ){
+ settings.animDuration = 500;
+ }
+
+ this.log( '\t chartSettings:', settings );
+ return settings;
+ },
+
+ toString : function(){
+ return 'ScatterplotControlForm(' + (( this.dataset )?( this.dataset.id ):( '' )) + ')';
+ }
+});
+
+ScatterplotControlForm.templates = {
+ mainLayout : Handlebars.templates[ 'template-visualization-scatterplotControlForm' ],
+ dataControl : Handlebars.templates[ 'template-visualization-dataControl' ],
+ chartControl : Handlebars.templates[ 'template-visualization-chartControl' ],
+ statsDisplay : Handlebars.templates[ 'template-visualization-statsDisplay' ],
+ chartDisplay : Handlebars.templates[ 'template-visualization-chartDisplay' ]
+};
+
+//==============================================================================
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/packed/mvc/visualizations/scatterplotControlForm.js
--- /dev/null
+++ b/static/scripts/packed/mvc/visualizations/scatterplotControlForm.js
@@ -0,0 +1,1 @@
+var ScatterplotControlForm=BaseView.extend(LoggableMixin).extend({className:"scatterplot-control-form",dataLoadDelay:500,dataLoadSize:3001,loadingIndicatorImage:"loading_small_white_bg.gif",fetchMsg:"Fetching data...",renderMsg:"Rendering...",initialize:function(a){this.log(this+".initialize, attributes:",a);this.dataset=null;this.chartConfig=null;this.chart=null;this.loader=null;this.$dataControl=null;this.$chartControl=null;this.$statsDisplay=null;this.$chartDisplay=null;this.dataFetch=null;this.initializeFromAttributes(a);this.initializeChart(a);this.initializeDataLoader(a)},initializeFromAttributes:function(a){if(!a||!a.dataset){throw ("ScatterplotView requires a dataset")}else{this.dataset=a.dataset}if(jQuery.type(this.dataset.metadata_column_types)==="string"){this.dataset.metadata_column_types=this.dataset.metadata_column_types.split(", ")}this.log("\t dataset:",this.dataset);if(!a.apiDatasetsURL){throw ("ScatterplotView requires a apiDatasetsURL")}else{this.dataURL=a.apiDatasetsURL+"/"+this.dataset.id+"?"}this.log("\t dataURL:",this.dataURL)},initializeChart:function(a){this.chartConfig=a.chartConfig||{};this.log("\t initial chartConfig:",this.chartConfig);this.chart=new TwoVarScatterplot(this.chartConfig);this.chartConfig=this.chart.config},initializeDataLoader:function(b){var a=this;this.loader=new LazyDataLoader({url:null,start:b.start||0,total:b.total||this.dataset.metadata_data_lines,delay:this.dataLoadDelay,size:this.dataLoadSize,buildUrl:function(d,c){return this.url+"&"+jQuery.param({start_val:d,max_vals:c})}});$(this.loader).bind("error",function(e,c,d){a.log("ERROR:",c,d);alert("ERROR fetching data:\n"+c+"\n"+d);a.hideLoadingIndicator()})},render:function(){this.log(this+".render");this.$el.append(ScatterplotControlForm.templates.mainLayout({loadingIndicatorImagePath:galaxy_paths.get("image_path")+"/"+this.loadingIndicatorImage,message:""}));this.$dataControl=this._render_dataControl();this.$chartControl=this._render_chartControl();this.$statsDisplay=this.$el.find(".tab-pane#stats-display");this.$chartDisplay=this._render_chartDisplay();if(this.chartConfig.xColumn&&this.chartConfig.yColumn){this.renderChart()}this.$el.find(".tooltip").tooltip();return this},_render_dataControl:function(){var b=this,a=[],d=[];_.each(this.dataset.metadata_column_types,function(h,f){var g=f+1,e="column "+g;if(b.dataset.metadata_column_names){e=b.dataset.metadata_column_names[f]}a.push({index:g,name:e});if(h==="int"||h==="float"){d.push({index:g,name:e})}});var c=this.$el.find(".tab-pane#data-control");c.append(ScatterplotControlForm.templates.dataControl({allColumns:a,numericColumns:d}));c.find("#X-select").val(this.chartConfig.xColumn);c.find("#Y-select").val(this.chartConfig.yColumn);if(this.chartConfig.idColumn!==undefined){c.find("#include-id-checkbox").attr("checked",true).trigger("change");c.find("#ID-select").val(this.chartConfig.idColumn)}return c},_render_chartControl:function(){var a=this,b=this.$el.find(".tab-pane#chart-control"),c={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};b.append(ScatterplotControlForm.templates.chartControl(this.chartConfig));b.find(".numeric-slider-input").each(function(){var f=$(this),e=f.find(".slider-output"),g=f.find(".slider"),h=f.attr("id");function d(){var j=$(this),i=j.slider("value");e.text(i)}g.slider(_.extend(c[h],{value:a.chartConfig[h],change:d,slide:d}))});return b},_render_chartDisplay:function(){var a=this.$el.find(".tab-pane#chart-display");a.append(ScatterplotControlForm.templates.chartDisplay(this.chartConfig));return a},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","click #data-control #render-button":"renderChart","click #chart-control #render-button":"changeChartSettings"},toggleThirdColumnSelector:function(){this.$el.find('select[name="ID"]').parent().toggle()},showLoadingIndicator:function(b,c){b=b||"";var a=this.$el.find("div#loading-indicator");messageBox=a.find(".loading-message");if(a.is(":visible")){if(b){messageBox.fadeOut("fast",function(){messageBox.text(b);messageBox.fadeIn("fast",c)})}else{c()}}else{if(b){messageBox.text(b)}a.fadeIn("fast",c)}},hideLoadingIndicator:function(a){this.$el.find("div#loading-indicator").fadeOut("fast",a)},renderChart:function(){this.log(this+".renderChart");this.data=null;this.meta=null;_.extend(this.chartConfig,this.getChartSettings());this.log("\t chartConfig:",this.chartConfig);this.chart.updateConfig(this.chartConfig,false);this.loader.url=this.dataURL+"&"+jQuery.param(this.getDataSettings());this.log("\t loader: total lines:",this.loader.total," url:",this.loader.url);var a=this;$(this.loader).bind("loaded.new",function(c,b){a.log(a+" loaded.new",b);a.postProcessDataFetchResponse(b);a.log("\t postprocessed data:",a.data);a.log("\t postprocessed meta:",a.meta);a.showLoadingIndicator(a.renderMsg,function(){a.chart.render(a.data,a.meta);a.renderStats(a.data,a.meta);a.hideLoadingIndicator()})});$(this.loader).bind("complete",function(b,c){a.log(a+" complete",c);$(a.loader).unbind()});a.showLoadingIndicator(a.fetchMsg,function(){a.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show");a.loader.load()})},renderStats:function(){this.log(this+".renderStats");this.$statsDisplay.html(ScatterplotControlForm.templates.statsDisplay({stats:[{name:"Count",xval:this.meta[0].count,yval:this.meta[1].count},{name:"Min",xval:this.meta[0].min,yval:this.meta[1].min},{name:"Max",xval:this.meta[0].max,yval:this.meta[1].max},{name:"Sum",xval:this.meta[0].sum,yval:this.meta[1].sum},{name:"Mean",xval:this.meta[0].mean,yval:this.meta[1].mean},{name:"Median",xval:this.meta[0].median,yval:this.meta[1].median}]}))},changeChartSettings:function(){var a=this;newChartSettings=this.getChartSettings();_.extend(this.chartConfig,newChartSettings);this.log("this.chartConfig:",this.chartConfig);this.chart.updateConfig(this.chartConfig,false);if(a.data&&a.meta){a.showLoadingIndicator(a.renderMsg,function(){a.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show");a.chart.render(a.data,a.meta);a.hideLoadingIndicator()})}else{this.renderChart()}},postProcessDataFetchResponse:function(a){this.postProcessData(a.data);this.postProcessMeta(a.meta)},postProcessData:function(b){var a=this;if(a.data){_.each(b,function(d,c){a.data[c]=a.data[c].concat(d)})}else{a.data=b}},postProcessMeta:function(c){var a=this,b=this.dataset.metadata_column_types;if(a.meta){_.each(c,function(e,d){var i=a.meta[d],g=b[d];i.count+=(e.count)?(e.count):(0);if((g==="int")||(g==="float")){i.min=Math.min(e.min,i.min);i.max=Math.max(e.max,i.max);i.sum=e.sum+i.sum;i.mean=(i.count)?(i.sum/i.count):(null);var f=a.data[d].slice().sort(),h=Math.floor(f.length/2);if(f.length%2===0){i.median=((f[h]+f[(h+1)])/2)}else{i.median=f[h]}}})}else{a.meta=c}},getDataSettings:function(){var b=this.getColumnSelections(),a=[];this.log("\t columnSelections:",b);a=[b.X.colIndex-1,b.Y.colIndex-1];if(this.$dataControl.find("#include-id-checkbox").attr("checked")){a.push(b.ID.colIndex-1)}var c={data_type:"raw_data",columns:"["+a+"]"};this.log("\t data settings (url params):",c);return c},getColumnSelections:function(){var a={};this.$dataControl.find("div.column-select select").each(function(){var b=$(this),c=b.val();a[b.attr("name")]={colIndex:c,colName:b.children('[value="'+c+'"]').text()}});return a},getChartSettings:function(){var c={},d=this.getColumnSelections();c.datapointSize=this.$chartControl.find("#datapointSize.numeric-slider-input").find(".slider").slider("value");c.width=this.$chartControl.find("#width.numeric-slider-input").find(".slider").slider("value");c.height=this.$chartControl.find("#height.numeric-slider-input").find(".slider").slider("value");var b=this.$chartControl.find("input#X-axis-label").val(),a=this.$chartControl.find("input#Y-axis-label").val();c.xLabel=(b==="X")?(d.X.colName):(b);c.yLabel=(a==="Y")?(d.Y.colName):(a);c.animDuration=10;if(this.$chartControl.find("#animDuration.checkbox-input").is(":checked")){c.animDuration=500}this.log("\t chartSettings:",c);return c},toString:function(){return"ScatterplotControlForm("+((this.dataset)?(this.dataset.id):(""))+")"}});ScatterplotControlForm.templates={mainLayout:Handlebars.templates["template-visualization-scatterplotControlForm"],dataControl:Handlebars.templates["template-visualization-dataControl"],chartControl:Handlebars.templates["template-visualization-chartControl"],statsDisplay:Handlebars.templates["template-visualization-statsDisplay"],chartDisplay:Handlebars.templates["template-visualization-chartDisplay"]};
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/packed/templates/compiled/template-visualization-chartControl.js
--- /dev/null
+++ b/static/scripts/packed/templates/compiled/template-visualization-chartControl.js
@@ -0,0 +1,1 @@
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-chartControl"]=b(function(f,m,e,l,k){e=e||f.helpers;var i="",c,h,g="function",j=this.escapeExpression,n=this;function d(p,o){return' checked="true"'}i+='<p class="help-text">\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the \'handle\' is in focus, your keyboard\'s arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n </p>\n\n <div id="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">';h=e.datapointSize;if(h){c=h.call(m,{hash:{}})}else{c=m.datapointSize;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n Size of the graphic representation of each data point\n </p>\n </div>\n\n <div id="animDuration" class="form-input checkbox-input">\n <label for="animated">Animate graph transitions?: </label>\n <input type="checkbox" id="animated"\n class="checkbox control"';c=m.animDuration;c=e["if"].call(m,c,{hash:{},inverse:n.noop,fn:n.program(1,d,k)});if(c||c===0){i+=c}i+=' />\n <p class="form-help help-text-small">\n Uncheck this to disable the animations used on the graph\n </p>\n </div>\n\n <div id="width" class="form-input numeric-slider-input">\n <label for="width">Graph width: </label>\n <div class="slider-output">';h=e.width;if(h){c=h.call(m,{hash:{}})}else{c=m.width;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id="height" class="form-input numeric-slider-input">\n <label for="height">Graph height: </label>\n <div class="slider-output">';h=e.height;if(h){c=h.call(m,{hash:{}})}else{c=m.height;c=typeof c===g?c():c}i+=j(c)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id="X-axis-label"class="text-input form-input">\n <label for="X-axis-label">Re-label the X axis: </label>\n <input type="text" name="X-axis-label" id="X-axis-label" value="';h=e.xLabel;if(h){c=h.call(m,{hash:{}})}else{c=m.xLabel;c=typeof c===g?c():c}i+=j(c)+'" />\n <p class="form-help help-text-small"></p>\n </div>\n\n <div id="Y-axis-label" class="text-input form-input">\n <label for="Y-axis-label">Re-label the Y axis: </label>\n <input type="text" name="Y-axis-label" id="Y-axis-label" value="';h=e.yLabel;if(h){c=h.call(m,{hash:{}})}else{c=m.yLabel;c=typeof c===g?c():c}i+=j(c)+'" />\n <p class="form-help help-text-small"></p>\n </div>\n\n <input id="render-button" type="button" value="Draw" />';return i})})();
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/packed/templates/compiled/template-visualization-chartDisplay.js
--- /dev/null
+++ b/static/scripts/packed/templates/compiled/template-visualization-chartDisplay.js
@@ -0,0 +1,1 @@
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-chartDisplay"]=b(function(e,l,d,k,j){d=d||e.helpers;var h="",c,g,f="function",i=this.escapeExpression;h+='<svg width="';g=d.width;if(g){c=g.call(l,{hash:{}})}else{c=l.width;c=typeof c===f?c():c}h+=i(c)+'" height="';g=d.height;if(g){c=g.call(l,{hash:{}})}else{c=l.height;c=typeof c===f?c():c}h+=i(c)+'"></svg>';return h})})();
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/packed/templates/compiled/template-visualization-dataControl.js
--- /dev/null
+++ b/static/scripts/packed/templates/compiled/template-visualization-dataControl.js
@@ -0,0 +1,1 @@
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-dataControl"]=b(function(g,m,f,l,k){f=f||g.helpers;var i="",d,h="function",j=this.escapeExpression,o=this;function e(t,s){var q="",r,p;q+='\n <option value="';p=f.index;if(p){r=p.call(t,{hash:{}})}else{r=t.index;r=typeof r===h?r():r}q+=j(r)+'">';p=f.name;if(p){r=p.call(t,{hash:{}})}else{r=t.name;r=typeof r===h?r():r}q+=j(r)+"</option>\n ";return q}function c(t,s){var q="",r,p;q+='\n <option value="';p=f.index;if(p){r=p.call(t,{hash:{}})}else{r=t.index;r=typeof r===h?r():r}q+=j(r)+'">';p=f.name;if(p){r=p.call(t,{hash:{}})}else{r=t.name;r=typeof r===h?r():r}q+=j(r)+"</option>\n ";return q}function n(t,s){var q="",r,p;q+='\n <option value="';p=f.index;if(p){r=p.call(t,{hash:{}})}else{r=t.index;r=typeof r===h?r():r}q+=j(r)+'">';p=f.name;if(p){r=p.call(t,{hash:{}})}else{r=t.name;r=typeof r===h?r():r}q+=j(r)+"</option>\n ";return q}i+="<p class=\"help-text\">\n Use the following controls to change the data used by the chart.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n </p>\n\n ";i+='\n <div class="column-select">\n <label for="X-select">Data column for X: </label>\n <select name="X" id="X-select">\n ';d=m.numericColumns;d=f.each.call(m,d,{hash:{},inverse:o.noop,fn:o.program(1,e,k)});if(d||d===0){i+=d}i+='\n </select>\n </div>\n <div class="column-select">\n <label for="Y-select">Data column for Y: </label>\n <select name="Y" id="Y-select">\n ';d=m.numericColumns;d=f.each.call(m,d,{hash:{},inverse:o.noop,fn:o.program(3,c,k)});if(d||d===0){i+=d}i+="\n </select>\n </div>\n\n ";i+='\n <div id="include-id">\n <label for="include-id-checkbox">Include a third column as data point IDs?</label>\n <input type="checkbox" name="include-id" id="include-id-checkbox" />\n <p class="help-text-small">\n These will be displayed (along with the x and y values) when you hover over\n a data point.\n </p>\n </div>\n <div class="column-select" style="display: none">\n <label for="ID-select">Data column for IDs: </label>\n <select name="ID" id="ID-select">\n ';d=m.allColumns;d=f.each.call(m,d,{hash:{},inverse:o.noop,fn:o.program(5,n,k)});if(d||d===0){i+=d}i+='\n </select>\n </div>\n\n <input id="render-button" type="button" value="Draw" />\n <div class="clear"></div>';return i})})();
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/packed/templates/compiled/template-visualization-scatterplotControlForm.js
--- a/static/scripts/packed/templates/compiled/template-visualization-scatterplotControlForm.js
+++ b/static/scripts/packed/templates/compiled/template-visualization-scatterplotControlForm.js
@@ -1,1 +1,1 @@
-(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-scatterplotControlForm"]=b(function(g,n,f,m,l){f=f||g.helpers;var j="",d,i,h="function",k=this.escapeExpression,p=this;function e(u,t){var r="",s,q;r+='\n <option value="';q=f.index;if(q){s=q.call(u,{hash:{}})}else{s=u.index;s=typeof s===h?s():s}r+=k(s)+'">';q=f.name;if(q){s=q.call(u,{hash:{}})}else{s=u.name;s=typeof s===h?s():s}r+=k(s)+"</option>\n ";return r}function c(u,t){var r="",s,q;r+='\n <option value="';q=f.index;if(q){s=q.call(u,{hash:{}})}else{s=u.index;s=typeof s===h?s():s}r+=k(s)+'">';q=f.name;if(q){s=q.call(u,{hash:{}})}else{s=u.name;s=typeof s===h?s():s}r+=k(s)+"</option>\n ";return r}function o(u,t){var r="",s,q;r+='\n <option value="';q=f.index;if(q){s=q.call(u,{hash:{}})}else{s=u.index;s=typeof s===h?s():s}r+=k(s)+'">';q=f.name;if(q){s=q.call(u,{hash:{}})}else{s=u.name;s=typeof s===h?s():s}r+=k(s)+"</option>\n ";return r}j+='\n\n<ul class="nav nav-tabs">\n <li class="active"><a data-toggle="tab" href="#data-settings">Data Controls</a></li>\n <li><a data-toggle="tab" href="#chart-settings">Plot Controls</a></li>\n <li><a data-toggle="tab" href="#chart-stats">Statistics</a></li>\n <li><a data-toggle="tab" href="#chart">Chart</a></li>\n</ul>\n\n';j+='\n<div class="tab-content">\n<div id="data-settings" class="tab-pane active">\n\n <p class="help-text">\n Use the following controls to change the data used by the chart.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n </p>\n \n ';j+='\n <div class="column-select">\n <label for="X-select">Data column for X: </label>\n <select name="X" id="X-select">\n ';d=n.numericColumns;d=f.each.call(n,d,{hash:{},inverse:p.noop,fn:p.program(1,e,l)});if(d||d===0){j+=d}j+='\n </select>\n </div>\n <div class="column-select">\n <label for="Y-select">Data column for Y: </label>\n <select name="Y" id="Y-select">\n ';d=n.numericColumns;d=f.each.call(n,d,{hash:{},inverse:p.noop,fn:p.program(3,c,l)});if(d||d===0){j+=d}j+="\n </select>\n </div>\n \n ";j+='\n <div id="include-id">\n <label for="include-id-checkbox">Include a third column as data point IDs?</label>\n <input type="checkbox" name="include-id" id="include-id-checkbox" />\n <p class="help-text-small">\n These will be displayed (along with the x and y values) when you hover over\n a data point.\n </p>\n </div>\n <div class="column-select" style="display: none">\n <label for="ID-select">Data column for IDs: </label>\n <select name="ID" id="ID-select">\n ';d=n.allColumns;d=f.each.call(n,d,{hash:{},inverse:p.noop,fn:p.program(5,o,l)});if(d||d===0){j+=d}j+='\n </select>\n </div>\n \n <input id="render-button" type="button" value="Draw" />\n <input id="save-button" type="button" value="Download Plot as SVG" style="display: none;" />\n <div class="clear"></div>\n</div>\n\n<div id="chart-settings" class="tab-pane">\n</div>\n\n<div id="chart-stats" class="tab-pane">\n</div>\n</div>';j+="\n\n";j+='\n<div id="loading-indicator" style="display: none;">\n <img class="loading-img" src="';i=f.loadingIndicatorImagePath;if(i){d=i.call(n,{hash:{}})}else{d=n.loadingIndicatorImagePath;d=typeof d===h?d():d}j+=k(d)+'" />\n <span class="loading-message">';i=f.message;if(i){d=i.call(n,{hash:{}})}else{d=n.message;d=typeof d===h?d():d}j+=k(d)+"</span>\n</div>";return j})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-scatterplotControlForm"]=b(function(e,l,d,k,j){d=d||e.helpers;var h="",c,g,f="function",i=this.escapeExpression;h+='\n\n<div class="scatterplot-container chart-container tabbable tabs-left">\n ';h+='\n <ul class="nav nav-tabs">\n ';h+='\n <li class="active"><a href="#data-control" data-toggle="tab" class="tooltip"\n title="Use this tab to change which data are used">Data Controls</a></li>\n <li><a href="#chart-control" data-toggle="tab" class="tooltip"\n title="Use this tab to change how the chart is drawn">Chart Controls</a></li>\n <li><a href="#stats-display" data-toggle="tab" class="tooltip"\n title="This tab will display overall statistics for your data">Statistics</a></li>\n <li><a href="#chart-display" data-toggle="tab" class="tooltip"\n title="This tab will display the chart">Chart</a>\n ';h+='\n <div id="loading-indicator" style="display: none;">\n <img class="loading-img" src="';g=d.loadingIndicatorImagePath;if(g){c=g.call(l,{hash:{}})}else{c=l.loadingIndicatorImagePath;c=typeof c===f?c():c}h+=i(c)+'" />\n <span class="loading-message">';g=d.message;if(g){c=g.call(l,{hash:{}})}else{c=l.message;c=typeof c===f?c():c}h+=i(c)+"</span>\n </div>\n </li>\n </ul>\n\n ";h+='\n <div class="tab-content">\n ';h+='\n <div id="data-control" class="tab-pane active">\n ';h+="\n </div>\n \n ";h+='\n <div id="chart-control" class="tab-pane">\n ';h+="\n </div>\n\n ";h+='\n <div id="stats-display" class="tab-pane">\n ';h+="\n </div>\n\n ";h+='\n <div id="chart-display" class="tab-pane">\n ';h+="\n </div>\n\n </div>";h+="\n</div>";return h})})();
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/packed/templates/compiled/template-visualization-statsDisplay.js
--- /dev/null
+++ b/static/scripts/packed/templates/compiled/template-visualization-statsDisplay.js
@@ -0,0 +1,1 @@
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-visualization-statsDisplay"]=b(function(f,l,e,k,j){e=e||f.helpers;var h="",c,g="function",i=this.escapeExpression,m=this;function d(r,q){var o="",p,n;o+="\n <tr><td>";n=e.name;if(n){p=n.call(r,{hash:{}})}else{p=r.name;p=typeof p===g?p():p}o+=i(p)+"</td><td>";n=e.xval;if(n){p=n.call(r,{hash:{}})}else{p=r.xval;p=typeof p===g?p():p}o+=i(p)+"</td><td>";n=e.yval;if(n){p=n.call(r,{hash:{}})}else{p=r.yval;p=typeof p===g?p():p}o+=i(p)+"</td></tr>\n </tr>\n ";return o}h+='<p class="help-text">By column:</p>\n <table id="chart-stats-table">\n <thead><th></th><th>X</th><th>Y</th></thead>\n ';c=l.stats;c=e.each.call(l,c,{hash:{},inverse:m.noop,fn:m.program(1,d,j)});if(c||c===0){h+=c}h+="\n </table>";return h})})();
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/packed/viz/scatterplot.js
--- a/static/scripts/packed/viz/scatterplot.js
+++ b/static/scripts/packed/viz/scatterplot.js
@@ -1,1 +1,1 @@
-define(["../libs/underscore","../mvc/base-mvc","../utils/LazyDataLoader","../templates/compiled/template-visualization-scatterplotControlForm","../templates/compiled/template-visualization-statsTable","../templates/compiled/template-visualization-chartSettings","../libs/d3","../libs/bootstrap","../libs/jquery/jquery-ui"],function(){function a(f){var d=10,h=7,g=10,e=8,c=5;this.log=function(){if(this.debugging&&console&&console.debug){var i=Array.prototype.slice.call(arguments);i.unshift(this.toString());console.debug.apply(console,i)}};this.log("new TwoVarScatterplot:",f);this.defaults={id:"TwoVarScatterplot",containerSelector:"body",maxDataPoints:30000,datapointSize:4,animDuration:500,xNumTicks:10,yNumTicks:10,xAxisLabelBumpY:40,yAxisLabelBumpX:-35,width:500,height:500,marginTop:50,marginRight:50,marginBottom:50,marginLeft:50,xMin:null,xMax:null,yMin:null,yMax:null,xLabel:"X",yLabel:"Y"};this.config=_.extend({},this.defaults,f);this.updateConfig=function(i,j){_.extend(this.config,i)};this.toString=function(){return this.config.id};this.translateStr=function(i,j){return"translate("+i+","+j+")"};this.rotateStr=function(j,i,k){return"rotate("+j+","+i+","+k+")"};this.svg=d3.select(this.config.containerSelector).append("svg:svg").attr("class","chart");this.content=this.svg.append("svg:g").attr("class","content").attr("id",this.config.id);this.xAxis=this.content.append("g").attr("class","axis").attr("id","x-axis");this.xAxisLabel=this.xAxis.append("text").attr("class","axis-label").attr("id","x-axis-label");this.yAxis=this.content.append("g").attr("class","axis").attr("id","y-axis");this.yAxisLabel=this.yAxis.append("text").attr("class","axis-label").attr("id","y-axis-label");this.adjustChartDimensions=function(l,j,i,k){l=l||0;j=j||0;i=i||0;k=k||0;this.svg.attr("width",this.config.width+(this.config.marginRight+j)+(this.config.marginLeft+k)).attr("height",this.config.height+(this.config.marginTop+l)+(this.config.marginBottom+i)).style("display","block");this.content=this.svg.select("g.content").attr("transform",this.translateStr(this.config.marginLeft+k,this.config.marginTop+l))};this.preprocessData=function(k,j,i){return(k.length>this.config.maxDataPoints)?(k.slice(0,this.config.maxDataPoints)):(k)};this.setUpDomains=function(i,k,j){this.xMin=this.config.xMin||(j)?(j[0].min):(d3.min(i));this.xMax=this.config.xMax||(j)?(j[0].max):(d3.max(i));this.yMin=this.config.yMin||(j)?(j[1].min):(d3.min(k));this.yMax=this.config.yMax||(j)?(j[1].max):(d3.max(k))};this.setUpScales=function(){this.xScale=d3.scale.linear().domain([this.xMin,this.xMax]).range([0,this.config.width]),this.yScale=d3.scale.linear().domain([this.yMin,this.yMax]).range([this.config.height,0])};this.setUpXAxis=function(){this.xAxisFn=d3.svg.axis().scale(this.xScale).ticks(this.config.xNumTicks).orient("bottom");this.xAxis.attr("transform",this.translateStr(0,this.config.height)).call(this.xAxisFn);this.xLongestLabel=d3.max(_.map([this.xMin,this.xMax],function(i){return(String(i)).length}));if(this.xLongestLabel>=c){this.xAxis.selectAll("g").filter(":nth-child(odd)").style("display","none")}this.xAxisLabel.attr("x",this.config.width/2).attr("y",this.config.xAxisLabelBumpY).attr("text-anchor","middle").text(this.config.xLabel)};this.setUpYAxis=function(){this.yAxisFn=d3.svg.axis().scale(this.yScale).ticks(this.config.yNumTicks).orient("left");this.yAxis.call(this.yAxisFn);var i=this.yAxis.selectAll("text").filter(function(m,l){return l!==0});this.yLongestLabel=d3.max(i[0].map(function(m,l){return(d3.select(m).text()).length}))||0;var j=d+(this.yLongestLabel*h)+e+g;this.config.yAxisLabelBumpX=-(j-g);if(this.config.marginLeft<j){var k=(j)-this.config.marginLeft;k=(k<0)?(0):(k);this.adjustChartDimensions(0,0,0,k)}this.yAxisLabel.attr("x",this.config.yAxisLabelBumpX).attr("y",this.config.height/2).attr("text-anchor","middle").attr("transform",this.rotateStr(-90,this.config.yAxisLabelBumpX,this.config.height/2)).text(this.config.yLabel)};this.renderGrid=function(){this.vGridLines=this.content.selectAll("line.v-grid-line").data(this.xScale.ticks(this.xAxisFn.ticks()[0]));this.vGridLines.enter().append("svg:line").classed("grid-line v-grid-line",true);this.vGridLines.attr("x1",this.xScale).attr("y1",0).attr("x2",this.xScale).attr("y2",this.config.height);this.vGridLines.exit().remove();this.hGridLines=this.content.selectAll("line.h-grid-line").data(this.yScale.ticks(this.yAxisFn.ticks()[0]));this.hGridLines.enter().append("svg:line").classed("grid-line h-grid-line",true);this.hGridLines.attr("x1",0).attr("y1",this.yScale).attr("x2",this.config.width).attr("y2",this.yScale);this.hGridLines.exit().remove()};this.glyphEnterState=function(i){};this.glyphFinalState=function(i){};this.glyphExitState=function(i){};this.renderDatapoints=function(i,l,j){this.log(this+".renderDatapoints",arguments);var k=0;this.datapoints=this.addDatapoints(i,l,j,".glyph");this.datapoints.exit().each(function(){k+=1}).transition().duration(this.config.animDuration).attr("cy",this.config.height).attr("r",0).remove();this.log(k," glyphs removed")};this.addDatapoints=function(m,k,i,l){this.log(this+".addDatapoints",arguments);var p=this,o=0,q=function(s,r){return p.xScale(m[r])},j=function(s,r){return p.yScale(k[r])};var n=this.content.selectAll(l);this.log("existing datapoints:",n);n=n.data(m);o=0;n.enter().append("svg:circle").each(function(){o+=1}).classed("glyph",true).attr("cx",q).attr("cy",j).attr("r",0);this.log(o," new glyphs created");o=0;n.transition().duration(this.config.animDuration).each(function(){o+=1}).attr("cx",q).attr("cy",j).attr("r",p.config.datapointSize);this.log(o," existing glyphs transitioned");if(i){n.attr("data",function(s,r){return(i[r])})}n.attr("title",function(s,r){return((i)?(i[r]+": "):(""))+m[r]+", "+k[r]});n.on("mouseover",function(t,r){var s=d3.select(this);s.style("fill","red").style("fill-opacity",1);p.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",s.attr("cx")).attr("y1",s.attr("cy")).attr("x2",0).attr("y2",s.attr("cy")).classed("hoverline",true);p.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",s.attr("cx")).attr("y1",s.attr("cy")).attr("x2",s.attr("cx")).attr("y2",p.config.height).classed("hoverline",true)}).on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",0.2);d3.selectAll(".hoverline").remove()});return n};this.render=function(k,l){this.log(this+".render",arguments);var i=k[0],m=k[1],j=(k.length>2)?(k[2]):(undefined);i=this.preprocessData(i);m=this.preprocessData(m);this.log("xCol len",i.length,"yCol len",m.length);this.setUpDomains(i,m,l);this.setUpScales();this.adjustChartDimensions();this.setUpXAxis();this.setUpYAxis();this.renderGrid();this.renderDatapoints(i,m,j)}}var b=BaseView.extend(LoggableMixin).extend({className:"scatterplot-settings-form",dataLoadDelay:500,dataLoadSize:3001,loadingIndicatorImage:"loading_large_white_bg.gif",initialize:function(c){this.log(this+".initialize, attributes:",c);this.dataset=null;this.chartConfig=null;this.plot=null;this.loader=null;this.$statsPanel=null;this.$chartSettingsPanel=null;this.$dataSettingsPanel=null;this.dataFetch=null;this.initializeFromAttributes(c);this.initializeChart(c);this.initializeDataLoader(c)},initializeFromAttributes:function(c){if(!c||!c.dataset){throw ("ScatterplotView requires a dataset")}else{this.dataset=c.dataset}if(jQuery.type(this.dataset.metadata_column_types)==="string"){this.dataset.metadata_column_types=this.dataset.metadata_column_types.split(", ")}this.log("dataset:",this.dataset);if(!c.apiDatasetsURL){throw ("ScatterplotView requires a apiDatasetsURL")}else{this.dataURL=c.apiDatasetsURL+"/"+this.dataset.id+"?"}this.log("this.dataURL:",this.dataURL)},initializeChart:function(c){this.chartConfig=c.chartConfig||{};if(this.logger){this.chartConfig.debugging=true}this.log("initial chartConfig:",this.chartConfig);this.plot=new a(this.chartConfig);this.chartConfig=this.plot.config},initializeDataLoader:function(d){var c=this;this.loader=new LazyDataLoader({logger:(this.logger)?(this.logger):(null),url:null,start:d.start||0,total:d.total||this.dataset.metadata_data_lines,delay:this.dataLoadDelay,size:this.dataLoadSize,buildUrl:function(f,e){return this.url+"&"+jQuery.param({start_val:f,max_vals:e})}});$(this.loader).bind("error",function(g,e,f){c.log("ERROR:",e,f);alert("ERROR fetching data:\n"+e+"\n"+f);c.hideLoadingIndicator()})},render:function(){var c=this,d={config:this.chartConfig,allColumns:[],numericColumns:[],loadingIndicatorImagePath:galaxy_paths.get("image_path")+"/"+this.loadingIndicatorImage};_.each(this.dataset.metadata_column_types,function(g,f){var e="column "+(f+1);if(c.dataset.metadata_column_names){e=c.dataset.metadata_column_names[f]}if(g==="int"||g==="float"){d.numericColumns.push({index:(f+1),name:e})}d.allColumns.push({index:(f+1),name:e})});this.$el.append(b.templates.form(d));this.$dataSettingsPanel=this.$el.find(".tab-pane#data-settings");this.$chartSettingsPanel=this._render_chartSettings();this.$statsPanel=this.$el.find(".tab-pane#chart-stats");this.$dataSettingsPanel.find("#X-select").val(this.chartConfig.xColumn);this.$dataSettingsPanel.find("#Y-select").val(this.chartConfig.yColumn);if(this.chartConfig.idColumn!==undefined){this.$dataSettingsPanel.find("#include-id-checkbox").attr("checked",true).trigger("change");this.$dataSettingsPanel.find("#ID-select").val(this.chartConfig.idColumn)}if(this.chartConfig.xColumn&&this.chartConfig.yColumn){this.renderPlot()}return this},isColumnNumeric:function(c){if((c>=0)&&(c<this.dataset.metadata_column_types.length)){var d=this.dataset.metadata_column_types[c];return(d==="int"||d==="float")}return false},_render_chartSettings:function(){var c=this,d=this.$el.find(".tab-pane#chart-settings"),e={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};d.append(b.templates.chartSettings(this.chartConfig));d.find(".numeric-slider-input").each(function(){var h=$(this),g=h.find(".slider-output"),i=h.find(".slider"),j=h.attr("id");function f(){var l=$(this),k=l.slider("value");g.text(k)}i.slider(_.extend(e[j],{value:c.chartConfig[j],change:f,slide:f}))});return d},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","click #data-settings #render-button":"renderPlot","click #chart-settings #render-button":"changeChartSettings"},toggleThirdColumnSelector:function(){this.$el.find('select[name="ID"]').parent().toggle()},showLoadingIndicator:function(d,e){d=d||"";var c=this.$el.find("div#loading-indicator");messageBox=c.find(".loading-message");if(c.is(":visible")){if(d){messageBox.fadeOut("fast",function(){messageBox.text(d);messageBox.fadeIn("fast",e)})}else{e()}}else{if(d){messageBox.text(d)}c.fadeIn("fast",e)}},hideLoadingIndicator:function(c){this.$el.find("div#loading-indicator").fadeOut("fast",c)},renderPlot:function(){var c=this;c.data=null;c.meta=null;_.extend(this.chartConfig,this.getGraphSettings());this.log("this.chartConfig:",this.chartConfig);this.plot.updateConfig(this.chartConfig,false);this.loader.url=this.dataURL+"&"+jQuery.param(this.getDataSettings());this.log("this.loader, url:",this.loader.url,"total:",this.loader.total);$(this.loader).bind("loaded.new",function(e,d){c.log(c+" loaded.new",d);c.postProcessDataFetchResponse(d);c.log("postprocessed data:",c.data,"meta:",c.meta);c.showLoadingIndicator("Rendering...",function(){c.$el.find("ul.nav").find('a[href="#chart-stats"]').tab("show");c.plot.render(c.data,c.meta);c.renderStats(c.data,c.meta);c.hideLoadingIndicator()})});$(this.loader).bind("complete",function(d,e){c.log("complete",e);$(c.loader).unbind()});c.showLoadingIndicator("Fetching data...",function(){c.loader.load()})},renderStats:function(){this.$statsPanel.html(b.templates.statsTable({stats:[{name:"Count",xval:this.meta[0].count,yval:this.meta[1].count},{name:"Min",xval:this.meta[0].min,yval:this.meta[1].min},{name:"Max",xval:this.meta[0].max,yval:this.meta[1].max},{name:"Sum",xval:this.meta[0].sum,yval:this.meta[1].sum},{name:"Mean",xval:this.meta[0].mean,yval:this.meta[1].mean},{name:"Median",xval:this.meta[0].median,yval:this.meta[1].median}]}))},changeChartSettings:function(){var c=this;newGraphSettings=this.getGraphSettings();this.log("newGraphSettings:",newGraphSettings);_.extend(this.chartConfig,newGraphSettings);this.log("this.chartConfig:",this.chartConfig);this.plot.updateConfig(this.chartConfig,false);if(c.data&&c.meta){c.showLoadingIndicator("Rendering...",function(){c.plot.render(c.data,c.meta);c.hideLoadingIndicator()})}else{this.renderPlot()}},postProcessDataFetchResponse:function(c){this.postProcessData(c.data);this.postProcessMeta(c.meta)},postProcessData:function(d){var c=this;if(c.data){_.each(d,function(f,e){c.data[e]=c.data[e].concat(f)})}else{c.data=d}},postProcessMeta:function(e){var c=this,d=this.dataset.metadata_column_types;if(c.meta){_.each(e,function(g,f){var k=c.meta[f],i=d[f];c.log(f+" postprocessing meta:",g);k.count+=(g.count)?(g.count):(0);c.log(f,"count:",k.count);if((i==="int")||(i==="float")){k.min=Math.min(g.min,k.min);k.max=Math.max(g.max,k.max);k.sum=g.sum+k.sum;k.mean=(k.count)?(k.sum/k.count):(null);var h=c.data[f].slice().sort(),j=Math.floor(h.length/2);if(h.length%2===0){k.median=((h[j]+h[(j+1)])/2)}else{k.median=h[j]}}})}else{c.meta=e;c.log("initial meta:",c.meta)}},getDataSettings:function(){var d=this.getColumnSelections(),c=[];this.log("columnSelections:",d);c=[d.X.colIndex,d.Y.colIndex];if(this.$dataSettingsPanel.find("#include-id-checkbox").attr("checked")){c.push(d.ID.colIndex)}var e={data_type:"raw_data",columns:"["+c+"]"};this.log("params:",e);return e},getColumnSelections:function(){var c={};this.$dataSettingsPanel.find("div.column-select select").each(function(){var d=$(this),e=parseInt(d.val(),10)-1;c[d.attr("name")]={colIndex:e,colName:d.children('[value="'+e+'"]').text()}});return c},getGraphSettings:function(){var e={},f=this.getColumnSelections();e.datapointSize=this.$chartSettingsPanel.find("#datapointSize.numeric-slider-input").find(".slider").slider("value");e.width=this.$chartSettingsPanel.find("#width.numeric-slider-input").find(".slider").slider("value");e.height=this.$chartSettingsPanel.find("#height.numeric-slider-input").find(".slider").slider("value");var d=this.$chartSettingsPanel.find("input#X-axis-label").val(),c=this.$chartSettingsPanel.find("input#Y-axis-label").val();e.xLabel=(d==="X")?(f.X.colName):(d);e.yLabel=(c==="Y")?(f.Y.colName):(c);e.animDuration=10;if(this.$chartSettingsPanel.find("#animDuration.checkbox-input").is(":checked")){e.animDuration=500}this.log("graphSettings:",e);return e},toString:function(){return"ScatterplotControlForm("+((this.dataset)?(this.dataset.id):(""))+")"}});b.templates={form:Handlebars.templates["template-visualization-scatterplotControlForm"],statsTable:Handlebars.templates["template-visualization-statsTable"],chartSettings:Handlebars.templates["template-visualization-chartSettings"]};return{LazyDataLoader:LazyDataLoader,TwoVarScatterplot:a,ScatterplotControlForm:b}});
\ No newline at end of file
+function TwoVarScatterplot(d){var b=10,f=7,e=10,c=8,a=5;this.log=function(){if(this.debugging&&console&&console.debug){var g=Array.prototype.slice.call(arguments);g.unshift(this.toString());console.debug.apply(console,g)}};this.log("new TwoVarScatterplot:",d);this.defaults={id:"TwoVarScatterplot",containerSelector:"body",maxDataPoints:30000,datapointSize:4,animDuration:500,xNumTicks:10,yNumTicks:10,xAxisLabelBumpY:40,yAxisLabelBumpX:-35,width:400,height:400,marginTop:50,marginRight:50,marginBottom:50,marginLeft:50,xMin:null,xMax:null,yMin:null,yMax:null,xLabel:"X",yLabel:"Y"};this.config=_.extend({},this.defaults,d);this.log("intial config:",this.config);this.updateConfig=function(g,h){_.extend(this.config,g);this.log(this+".updateConfig:",this.config)};this.toString=function(){return this.config.id};this.translateStr=function(g,h){return"translate("+g+","+h+")"};this.rotateStr=function(h,g,i){return"rotate("+h+","+g+","+i+")"};this.adjustChartDimensions=function(j,h,g,i){j=j||0;h=h||0;g=g||0;i=i||0;this.svg.attr("width",this.config.width+(this.config.marginRight+h)+(this.config.marginLeft+i)).attr("height",this.config.height+(this.config.marginTop+j)+(this.config.marginBottom+g)).style("display","block");this.content=this.svg.select("g.content").attr("transform",this.translateStr(this.config.marginLeft+i,this.config.marginTop+j))};this.preprocessData=function(i,h,g){return(i.length>this.config.maxDataPoints)?(i.slice(0,this.config.maxDataPoints)):(i)};this.findMinMaxes=function(g,i,h){this.xMin=this.config.xMin||(h)?(h[0].min):(d3.min(g));this.xMax=this.config.xMax||(h)?(h[0].max):(d3.max(g));this.yMin=this.config.yMin||(h)?(h[1].min):(d3.min(i));this.yMax=this.config.yMax||(h)?(h[1].max):(d3.max(i))};this.setUpScales=function(){this.xScale=d3.scale.linear().domain([this.xMin,this.xMax]).range([0,this.config.width]),this.yScale=d3.scale.linear().domain([this.yMin,this.yMax]).range([this.config.height,0])};this.setUpXAxis=function(){this.xAxisFn=d3.svg.axis().scale(this.xScale).ticks(this.config.xNumTicks).orient("bottom");this.xAxis.attr("transform",this.translateStr(0,this.config.height)).call(this.xAxisFn);var g=d3.max(_.map([this.xMin,this.xMax],function(h){return(String(h)).length}));if(g>=a){this.xAxis.selectAll("g").filter(":nth-child(odd)").style("display","none")}this.log("this.config.xLabel:",this.config.xLabel);this.xAxisLabel.attr("x",this.config.width/2).attr("y",this.config.xAxisLabelBumpY).attr("text-anchor","middle").text(this.config.xLabel);this.log("xAxisLabel:",this.xAxisLabel)};this.setUpYAxis=function(){this.yAxisFn=d3.svg.axis().scale(this.yScale).ticks(this.config.yNumTicks).orient("left");this.yAxis.call(this.yAxisFn);var g=this.yAxis.selectAll("text").filter(function(k,j){return j!==0});this.log("yTickLabels:",g);this.yLongestLabel=d3.max(g[0].map(function(k,j){return(d3.select(k).text()).length}))||0;var h=b+(this.yLongestLabel*f)+c+e;this.config.yAxisLabelBumpX=-(h-e);if(this.config.marginLeft<h){var i=(h)-this.config.marginLeft;i=(i<0)?(0):(i);this.adjustChartDimensions(0,0,0,i)}this.yAxisLabel.attr("x",this.config.yAxisLabelBumpX).attr("y",this.config.height/2).attr("text-anchor","middle").attr("transform",this.rotateStr(-90,this.config.yAxisLabelBumpX,this.config.height/2)).text(this.config.yLabel)};this.renderGrid=function(){this.vGridLines=this.content.selectAll("line.v-grid-line").data(this.xScale.ticks(this.xAxisFn.ticks()[0]));this.vGridLines.enter().append("svg:line").classed("grid-line v-grid-line",true);this.vGridLines.attr("x1",this.xScale).attr("y1",0).attr("x2",this.xScale).attr("y2",this.config.height);this.vGridLines.exit().remove();this.hGridLines=this.content.selectAll("line.h-grid-line").data(this.yScale.ticks(this.yAxisFn.ticks()[0]));this.hGridLines.enter().append("svg:line").classed("grid-line h-grid-line",true);this.hGridLines.attr("x1",0).attr("y1",this.yScale).attr("x2",this.config.width).attr("y2",this.yScale);this.hGridLines.exit().remove()};this.glyphEnterState=function(g){};this.glyphFinalState=function(g){};this.glyphExitState=function(g){};this.renderDatapoints=function(g,j,h){this.log(this+".renderDatapoints",arguments);var i=0;this.datapoints=this.addDatapoints(g,j,h,".glyph");this.datapoints.exit().each(function(){i+=1}).transition().duration(this.config.animDuration).attr("cy",this.config.height).attr("r",0).remove();this.log(i," glyphs removed")};this.addDatapoints=function(k,i,g,j){this.log(this+".addDatapoints",arguments);var n=this,m=0,o=function(q,p){return n.xScale(k[p])},h=function(q,p){return n.yScale(i[p])};var l=this.content.selectAll(j);this.log("existing datapoints:",l);l=l.data(k);m=0;l.enter().append("svg:circle").each(function(){m+=1}).classed("glyph",true).attr("cx",o).attr("cy",h).attr("r",0);this.log(m," new glyphs created");m=0;l.transition().duration(this.config.animDuration).each(function(){m+=1}).attr("cx",o).attr("cy",h).attr("r",n.config.datapointSize);this.log(m," existing glyphs transitioned");if(g){l.attr("data",function(q,p){return(g[p])})}l.attr("svg:title",function(q,p){return((g)?(g[p]+": "):(""))+k[p]+", "+i[p]});l.on("mouseover",function(r,p){var q=d3.select(this);q.style("fill","red").style("fill-opacity",1);n.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",q.attr("cx")).attr("y1",q.attr("cy")).attr("x2",0).attr("y2",q.attr("cy")).classed("hoverline",true);n.content.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",q.attr("cx")).attr("y1",q.attr("cy")).attr("x2",q.attr("cx")).attr("y2",n.config.height).classed("hoverline",true)}).on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",0.2);d3.selectAll(".hoverline").remove()});return l};this.render=function(i,j){this.log(this+".render",arguments);this.log("\t config:",this.config);var g=i[0],k=i[1],h=(i.length>2)?(i[2]):(undefined);g=this.preprocessData(g);k=this.preprocessData(k);this.log("xCol len",g.length,"yCol len",k.length);this.findMinMaxes(g,k,j);this.setUpScales();if(!this.svg){this.svg=d3.select("svg").attr("class","chart")}if(!this.content){this.content=this.svg.append("svg:g").attr("class","content").attr("id",this.config.id)}this.adjustChartDimensions();if(!this.xAxis){this.xAxis=this.content.append("g").attr("class","axis").attr("id","x-axis")}if(!this.xAxisLabel){this.xAxisLabel=this.xAxis.append("text").attr("class","axis-label").attr("id","x-axis-label")}if(!this.yAxis){this.yAxis=this.content.append("g").attr("class","axis").attr("id","y-axis")}if(!this.yAxisLabel){this.yAxisLabel=this.yAxis.append("text").attr("class","axis-label").attr("id","y-axis-label")}this.setUpXAxis();this.setUpYAxis();this.renderGrid();this.renderDatapoints(g,k,h)}};
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/templates/compiled/template-visualization-chartControl.js
--- /dev/null
+++ b/static/scripts/templates/compiled/template-visualization-chartControl.js
@@ -0,0 +1,38 @@
+(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-visualization-chartControl'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ helpers = helpers || Handlebars.helpers;
+ var buffer = "", stack1, foundHelper, functionType="function", escapeExpression=this.escapeExpression, self=this;
+
+function program1(depth0,data) {
+
+
+ return " checked=\"true\"";}
+
+ buffer += "<p class=\"help-text\">\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the 'handle' is in focus, your keyboard's arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n </p>\n\n <div id=\"datapointSize\" class=\"form-input numeric-slider-input\">\n <label for=\"datapointSize\">Size of data point: </label>\n <div class=\"slider-output\">";
+ foundHelper = helpers.datapointSize;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.datapointSize; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n Size of the graphic representation of each data point\n </p>\n </div>\n\n <div id=\"animDuration\" class=\"form-input checkbox-input\">\n <label for=\"animated\">Animate graph transitions?: </label>\n <input type=\"checkbox\" id=\"animated\"\n class=\"checkbox control\"";
+ stack1 = depth0.animDuration;
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(1, program1, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += " />\n <p class=\"form-help help-text-small\">\n Uncheck this to disable the animations used on the graph\n </p>\n </div>\n\n <div id=\"width\" class=\"form-input numeric-slider-input\">\n <label for=\"width\">Graph width: </label>\n <div class=\"slider-output\">";
+ foundHelper = helpers.width;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.width; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id=\"height\" class=\"form-input numeric-slider-input\">\n <label for=\"height\">Graph height: </label>\n <div class=\"slider-output\">";
+ foundHelper = helpers.height;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.height; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id=\"X-axis-label\"class=\"text-input form-input\">\n <label for=\"X-axis-label\">Re-label the X axis: </label>\n <input type=\"text\" name=\"X-axis-label\" id=\"X-axis-label\" value=\"";
+ foundHelper = helpers.xLabel;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.xLabel; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\" />\n <p class=\"form-help help-text-small\"></p>\n </div>\n\n <div id=\"Y-axis-label\" class=\"text-input form-input\">\n <label for=\"Y-axis-label\">Re-label the Y axis: </label>\n <input type=\"text\" name=\"Y-axis-label\" id=\"Y-axis-label\" value=\"";
+ foundHelper = helpers.yLabel;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.yLabel; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\" />\n <p class=\"form-help help-text-small\"></p>\n </div>\n\n <input id=\"render-button\" type=\"button\" value=\"Draw\" />";
+ return buffer;});
+})();
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/templates/compiled/template-visualization-chartDisplay.js
--- /dev/null
+++ b/static/scripts/templates/compiled/template-visualization-chartDisplay.js
@@ -0,0 +1,18 @@
+(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-visualization-chartDisplay'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ helpers = helpers || Handlebars.helpers;
+ var buffer = "", stack1, foundHelper, functionType="function", escapeExpression=this.escapeExpression;
+
+
+ buffer += "<svg width=\"";
+ foundHelper = helpers.width;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.width; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\" height=\"";
+ foundHelper = helpers.height;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.height; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\"></svg>";
+ return buffer;});
+})();
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/templates/compiled/template-visualization-chartSettings.js
--- a/static/scripts/templates/compiled/template-visualization-chartSettings.js
+++ /dev/null
@@ -1,38 +0,0 @@
-(function() {
- var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
-templates['template-visualization-chartSettings'] = template(function (Handlebars,depth0,helpers,partials,data) {
- helpers = helpers || Handlebars.helpers;
- var buffer = "", stack1, foundHelper, functionType="function", escapeExpression=this.escapeExpression, self=this;
-
-function program1(depth0,data) {
-
-
- return " checked=\"true\"";}
-
- buffer += "<p class=\"help-text\">\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the 'handle' is in focus, your keyboard's arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n </p>\n \n <div id=\"datapointSize\" class=\"form-input numeric-slider-input\">\n <label for=\"datapointSize\">Size of data point: </label>\n <div class=\"slider-output\">";
- foundHelper = helpers.datapointSize;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.datapointSize; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n Size of the graphic representation of each data point\n </p>\n </div>\n\n <div id=\"animDuration\" class=\"form-input checkbox-input\">\n <label for=\"animated\">Animate graph transitions?: </label>\n <input type=\"checkbox\" id=\"animated\"\n class=\"checkbox control\"";
- stack1 = depth0.animDuration;
- stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(1, program1, data)});
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += " />\n <p class=\"form-help help-text-small\">\n Uncheck this to disable the animations used on the graph\n </p>\n </div>\n\n <div id=\"width\" class=\"form-input numeric-slider-input\">\n <label for=\"width\">Graph width: </label>\n <div class=\"slider-output\">";
- foundHelper = helpers.width;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.width; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id=\"height\" class=\"form-input numeric-slider-input\">\n <label for=\"height\">Graph height: </label>\n <div class=\"slider-output\">";
- foundHelper = helpers.height;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.height; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</div>\n <div class=\"slider\"></div>\n <p class=\"form-help help-text-small\">\n (not including graph margins and axes)\n </p>\n </div>\n\n <div id=\"X-axis-label\"class=\"text-input form-input\">\n <label for=\"X-axis-label\">Re-label the X axis: </label>\n <input type=\"text\" name=\"X-axis-label\" id=\"X-axis-label\" value=\"";
- foundHelper = helpers.xLabel;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.xLabel; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "\" />\n <p class=\"form-help help-text-small\"></p>\n </div>\n\n <div id=\"Y-axis-label\" class=\"text-input form-input\">\n <label for=\"Y-axis-label\">Re-label the Y axis: </label>\n <input type=\"text\" name=\"Y-axis-label\" id=\"Y-axis-label\" value=\"";
- foundHelper = helpers.yLabel;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.yLabel; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "\" />\n <p class=\"form-help help-text-small\"></p>\n </div>\n\n <input id=\"render-button\" type=\"button\" value=\"Draw\" />";
- return buffer;});
-})();
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/templates/compiled/template-visualization-dataControl.js
--- /dev/null
+++ b/static/scripts/templates/compiled/template-visualization-dataControl.js
@@ -0,0 +1,65 @@
+(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-visualization-dataControl'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ helpers = helpers || Handlebars.helpers;
+ var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
+
+function program1(depth0,data) {
+
+ var buffer = "", stack1, foundHelper;
+ buffer += "\n <option value=\"";
+ foundHelper = helpers.index;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.index; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\">";
+ foundHelper = helpers.name;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</option>\n ";
+ return buffer;}
+
+function program3(depth0,data) {
+
+ var buffer = "", stack1, foundHelper;
+ buffer += "\n <option value=\"";
+ foundHelper = helpers.index;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.index; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\">";
+ foundHelper = helpers.name;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</option>\n ";
+ return buffer;}
+
+function program5(depth0,data) {
+
+ var buffer = "", stack1, foundHelper;
+ buffer += "\n <option value=\"";
+ foundHelper = helpers.index;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.index; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\">";
+ foundHelper = helpers.name;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</option>\n ";
+ return buffer;}
+
+ buffer += "<p class=\"help-text\">\n Use the following controls to change the data used by the chart.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n </p>\n\n ";
+ buffer += "\n <div class=\"column-select\">\n <label for=\"X-select\">Data column for X: </label>\n <select name=\"X\" id=\"X-select\">\n ";
+ stack1 = depth0.numericColumns;
+ stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(1, program1, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n </select>\n </div>\n <div class=\"column-select\">\n <label for=\"Y-select\">Data column for Y: </label>\n <select name=\"Y\" id=\"Y-select\">\n ";
+ stack1 = depth0.numericColumns;
+ stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(3, program3, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n </select>\n </div>\n\n ";
+ buffer += "\n <div id=\"include-id\">\n <label for=\"include-id-checkbox\">Include a third column as data point IDs?</label>\n <input type=\"checkbox\" name=\"include-id\" id=\"include-id-checkbox\" />\n <p class=\"help-text-small\">\n These will be displayed (along with the x and y values) when you hover over\n a data point.\n </p>\n </div>\n <div class=\"column-select\" style=\"display: none\">\n <label for=\"ID-select\">Data column for IDs: </label>\n <select name=\"ID\" id=\"ID-select\">\n ";
+ stack1 = depth0.allColumns;
+ stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(5, program5, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n </select>\n </div>\n\n <input id=\"render-button\" type=\"button\" value=\"Draw\" />\n <div class=\"clear\"></div>";
+ return buffer;});
+})();
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/templates/compiled/template-visualization-scatterplotControlForm.js
--- a/static/scripts/templates/compiled/template-visualization-scatterplotControlForm.js
+++ b/static/scripts/templates/compiled/template-visualization-scatterplotControlForm.js
@@ -2,75 +2,30 @@
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates['template-visualization-scatterplotControlForm'] = template(function (Handlebars,depth0,helpers,partials,data) {
helpers = helpers || Handlebars.helpers;
- var buffer = "", stack1, foundHelper, functionType="function", escapeExpression=this.escapeExpression, self=this;
+ var buffer = "", stack1, foundHelper, functionType="function", escapeExpression=this.escapeExpression;
-function program1(depth0,data) {
-
- var buffer = "", stack1, foundHelper;
- buffer += "\n <option value=\"";
- foundHelper = helpers.index;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.index; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "\">";
- foundHelper = helpers.name;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</option>\n ";
- return buffer;}
-function program3(depth0,data) {
-
- var buffer = "", stack1, foundHelper;
- buffer += "\n <option value=\"";
- foundHelper = helpers.index;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.index; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "\">";
- foundHelper = helpers.name;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</option>\n ";
- return buffer;}
-
-function program5(depth0,data) {
-
- var buffer = "", stack1, foundHelper;
- buffer += "\n <option value=\"";
- foundHelper = helpers.index;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.index; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "\">";
- foundHelper = helpers.name;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</option>\n ";
- return buffer;}
-
- buffer += "\n\n<ul class=\"nav nav-tabs\">\n <li class=\"active\"><a data-toggle=\"tab\" href=\"#data-settings\">Data Controls</a></li>\n <li><a data-toggle=\"tab\" href=\"#chart-settings\">Plot Controls</a></li>\n <li><a data-toggle=\"tab\" href=\"#chart-stats\">Statistics</a></li>\n <li><a data-toggle=\"tab\" href=\"#chart\">Chart</a></li>\n</ul>\n\n";
- buffer += "\n<div class=\"tab-content\">\n<div id=\"data-settings\" class=\"tab-pane active\">\n\n <p class=\"help-text\">\n Use the following controls to change the data used by the chart.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n </p>\n \n ";
- buffer += "\n <div class=\"column-select\">\n <label for=\"X-select\">Data column for X: </label>\n <select name=\"X\" id=\"X-select\">\n ";
- stack1 = depth0.numericColumns;
- stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(1, program1, data)});
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n </select>\n </div>\n <div class=\"column-select\">\n <label for=\"Y-select\">Data column for Y: </label>\n <select name=\"Y\" id=\"Y-select\">\n ";
- stack1 = depth0.numericColumns;
- stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(3, program3, data)});
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n </select>\n </div>\n \n ";
- buffer += "\n <div id=\"include-id\">\n <label for=\"include-id-checkbox\">Include a third column as data point IDs?</label>\n <input type=\"checkbox\" name=\"include-id\" id=\"include-id-checkbox\" />\n <p class=\"help-text-small\">\n These will be displayed (along with the x and y values) when you hover over\n a data point.\n </p>\n </div>\n <div class=\"column-select\" style=\"display: none\">\n <label for=\"ID-select\">Data column for IDs: </label>\n <select name=\"ID\" id=\"ID-select\">\n ";
- stack1 = depth0.allColumns;
- stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(5, program5, data)});
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n </select>\n </div>\n \n <input id=\"render-button\" type=\"button\" value=\"Draw\" />\n <input id=\"save-button\" type=\"button\" value=\"Download Plot as SVG\" style=\"display: none;\" />\n <div class=\"clear\"></div>\n</div>\n\n<div id=\"chart-settings\" class=\"tab-pane\">\n</div>\n\n<div id=\"chart-stats\" class=\"tab-pane\">\n</div>\n</div>";
- buffer += "\n\n";
- buffer += "\n<div id=\"loading-indicator\" style=\"display: none;\">\n <img class=\"loading-img\" src=\"";
+ buffer += "\n\n<div class=\"scatterplot-container chart-container tabbable tabs-left\">\n ";
+ buffer += "\n <ul class=\"nav nav-tabs\">\n ";
+ buffer += "\n <li class=\"active\"><a href=\"#data-control\" data-toggle=\"tab\" class=\"tooltip\"\n title=\"Use this tab to change which data are used\">Data Controls</a></li>\n <li><a href=\"#chart-control\" data-toggle=\"tab\" class=\"tooltip\"\n title=\"Use this tab to change how the chart is drawn\">Chart Controls</a></li>\n <li><a href=\"#stats-display\" data-toggle=\"tab\" class=\"tooltip\"\n title=\"This tab will display overall statistics for your data\">Statistics</a></li>\n <li><a href=\"#chart-display\" data-toggle=\"tab\" class=\"tooltip\"\n title=\"This tab will display the chart\">Chart</a>\n ";
+ buffer += "\n <div id=\"loading-indicator\" style=\"display: none;\">\n <img class=\"loading-img\" src=\"";
foundHelper = helpers.loadingIndicatorImagePath;
if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
else { stack1 = depth0.loadingIndicatorImagePath; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "\" />\n <span class=\"loading-message\">";
+ buffer += escapeExpression(stack1) + "\" />\n <span class=\"loading-message\">";
foundHelper = helpers.message;
if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
else { stack1 = depth0.message; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</span>\n</div>";
+ buffer += escapeExpression(stack1) + "</span>\n </div>\n </li>\n </ul>\n\n ";
+ buffer += "\n <div class=\"tab-content\">\n ";
+ buffer += "\n <div id=\"data-control\" class=\"tab-pane active\">\n ";
+ buffer += "\n </div>\n \n ";
+ buffer += "\n <div id=\"chart-control\" class=\"tab-pane\">\n ";
+ buffer += "\n </div>\n\n ";
+ buffer += "\n <div id=\"stats-display\" class=\"tab-pane\">\n ";
+ buffer += "\n </div>\n\n ";
+ buffer += "\n <div id=\"chart-display\" class=\"tab-pane\">\n ";
+ buffer += "\n </div>\n\n </div>";
+ buffer += "\n</div>";
return buffer;});
})();
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/templates/compiled/template-visualization-statsDisplay.js
--- /dev/null
+++ b/static/scripts/templates/compiled/template-visualization-statsDisplay.js
@@ -0,0 +1,31 @@
+(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-visualization-statsDisplay'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ helpers = helpers || Handlebars.helpers;
+ var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
+
+function program1(depth0,data) {
+
+ var buffer = "", stack1, foundHelper;
+ buffer += "\n <tr><td>";
+ foundHelper = helpers.name;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</td><td>";
+ foundHelper = helpers.xval;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.xval; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</td><td>";
+ foundHelper = helpers.yval;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.yval; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</td></tr>\n </tr>\n ";
+ return buffer;}
+
+ buffer += "<p class=\"help-text\">By column:</p>\n <table id=\"chart-stats-table\">\n <thead><th></th><th>X</th><th>Y</th></thead>\n ";
+ stack1 = depth0.stats;
+ stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(1, program1, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n </table>";
+ return buffer;});
+})();
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/templates/compiled/template-visualization-statsTable.js
--- a/static/scripts/templates/compiled/template-visualization-statsTable.js
+++ /dev/null
@@ -1,31 +0,0 @@
-(function() {
- var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
-templates['template-visualization-statsTable'] = template(function (Handlebars,depth0,helpers,partials,data) {
- helpers = helpers || Handlebars.helpers;
- var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
-
-function program1(depth0,data) {
-
- var buffer = "", stack1, foundHelper;
- buffer += "\n <tr><td>";
- foundHelper = helpers.name;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</td><td>";
- foundHelper = helpers.xval;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.xval; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</td><td>";
- foundHelper = helpers.yval;
- if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
- else { stack1 = depth0.yval; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
- buffer += escapeExpression(stack1) + "</td></tr>\n </tr>\n ";
- return buffer;}
-
- buffer += "<p class=\"help-text\">By column:</p>\n <table id=\"chart-stats-table\">\n <thead><th></th><th>X</th><th>Y</th></thead>\n ";
- stack1 = depth0.stats;
- stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(1, program1, data)});
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n </table>";
- return buffer;});
-})();
\ No newline at end of file
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/templates/visualization-templates.html
--- a/static/scripts/templates/visualization-templates.html
+++ b/static/scripts/templates/visualization-templates.html
@@ -1,22 +1,59 @@
<script type="text/template" class="template-visualization" id="template-visualization-scatterplotControlForm">
-{{! main controls }}
+{{! main layout }}
-<ul class="nav nav-tabs">
- <li class="active"><a data-toggle="tab" href="#data-settings">Data Controls</a></li>
- <li><a data-toggle="tab" href="#chart-settings">Plot Controls</a></li>
- <li><a data-toggle="tab" href="#chart-stats">Statistics</a></li>
- <li><a data-toggle="tab" href="#chart">Chart</a></li>
-</ul>
+<div class="scatterplot-container chart-container tabbable tabs-left">
+ {{! tab buttons/headers using Bootstrap }}
+ <ul class="nav nav-tabs">
+ {{! start with the data controls as the displayed tab }}
+ <li class="active"><a href="#data-control" data-toggle="tab" class="tooltip"
+ title="Use this tab to change which data are used">Data Controls</a></li>
+ <li><a href="#chart-control" data-toggle="tab" class="tooltip"
+ title="Use this tab to change how the chart is drawn">Chart Controls</a></li>
+ <li><a href="#stats-display" data-toggle="tab" class="tooltip"
+ title="This tab will display overall statistics for your data">Statistics</a></li>
+ <li><a href="#chart-display" data-toggle="tab" class="tooltip"
+ title="This tab will display the chart">Chart</a>
+ {{! loading indicator - initially hidden }}
+ <div id="loading-indicator" style="display: none;">
+ <img class="loading-img" src="{{loadingIndicatorImagePath}}" />
+ <span class="loading-message">{{message}}</span>
+ </div>
+ </li>
+ </ul>
-{{! data settings }}
-<div class="tab-content">
-<div id="data-settings" class="tab-pane active">
+ {{! data form, chart config form, stats, and chart all get their own tab }}
+ <div class="tab-content">
+ {{! ---------------------------- tab for data settings form }}
+ <div id="data-control" class="tab-pane active">
+ {{! rendered separately }}
+ </div>
+
+ {{! ---------------------------- tab for chart graphics control form }}
+ <div id="chart-control" class="tab-pane">
+ {{! rendered separately }}
+ </div>
+
+ {{! ---------------------------- tab for data statistics }}
+ <div id="stats-display" class="tab-pane">
+ {{! rendered separately }}
+ </div>
+
+ {{! ---------------------------- tab for actual chart }}
+ <div id="chart-display" class="tab-pane">
+ {{! chart rendered separately }}
+ </div>
+
+ </div>{{! end .tab-content }}
+</div>{{! end .chart-control }}
+</script>
+
+<script type="text/template" class="template-visualization" id="template-visualization-dataControl"><p class="help-text">
Use the following controls to change the data used by the chart.
Use the 'Draw' button to render (or re-render) the chart with the current settings.
</p>
-
+
{{! column selector containers }}
<div class="column-select"><label for="X-select">Data column for X: </label>
@@ -34,7 +71,7 @@
{{/each}}
</select></div>
-
+
{{! optional id column }}
<div id="include-id"><label for="include-id-checkbox">Include a third column as data point IDs?</label>
@@ -52,47 +89,19 @@
{{/each}}
</select></div>
-
+
<input id="render-button" type="button" value="Draw" />
- <input id="save-button" type="button" value="Download Plot as SVG" style="display: none;" /><div class="clear"></div>
-</div>
-
-<div id="chart-settings" class="tab-pane">
-</div>
-
-<div id="chart-stats" class="tab-pane">
-</div>
-</div>{{! class="tab-content" }}
-
-{{! loading indicator - initially hidden }}
-<div id="loading-indicator" style="display: none;">
- <img class="loading-img" src="{{loadingIndicatorImagePath}}" />
- <span class="loading-message">{{message}}</span>
-</div>
-
</script>
-<script type="text/template" class="template-visualization" id="template-visualization-statsTable">
- <p class="help-text">By column:</p>
- <table id="chart-stats-table">
- <thead><th></th><th>X</th><th>Y</th></thead>
- {{#each stats}}
- <tr><td>{{name}}</td><td>{{xval}}</td><td>{{yval}}</td></tr>
- </tr>
- {{/each}}
- </table>
-</script>
-
-<script type="text/template" class="template-visualization" id="template-visualization-chartSettings">
-
+<script type="text/template" class="template-visualization" id="template-visualization-chartControl"><p class="help-text">
Use the following controls to how the chart is displayed.
The slide controls can be moved by the mouse or, if the 'handle' is in focus, your keyboard's arrow keys.
Move the focus between controls by using the tab or shift+tab keys on your keyboard.
Use the 'Draw' button to render (or re-render) the chart with the current settings.
</p>
-
+
<div id="datapointSize" class="form-input numeric-slider-input"><label for="datapointSize">Size of data point: </label><div class="slider-output">{{datapointSize}}</div>
@@ -143,3 +152,18 @@
<input id="render-button" type="button" value="Draw" /></script>
+
+<script type="text/template" class="template-visualization" id="template-visualization-statsDisplay">
+ <p class="help-text">By column:</p>
+ <table id="chart-stats-table">
+ <thead><th></th><th>X</th><th>Y</th></thead>
+ {{#each stats}}
+ <tr><td>{{name}}</td><td>{{xval}}</td><td>{{yval}}</td></tr>
+ </tr>
+ {{/each}}
+ </table>
+</script>
+
+<script type="text/template" class="template-visualization" id="template-visualization-chartDisplay">
+ <svg width="{{width}}" height="{{height}}"></svg>
+</script>
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/utils/LazyDataLoader.js
--- a/static/scripts/utils/LazyDataLoader.js
+++ b/static/scripts/utils/LazyDataLoader.js
@@ -1,3 +1,4 @@
+//==============================================================================
/*
TODO:
?? superclass dataloader, subclass lazydataloader??
@@ -213,3 +214,5 @@
loader.initialize( config );
return loader;
}
+
+//==============================================================================
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff static/scripts/viz/scatterplot.js
--- a/static/scripts/viz/scatterplot.js
+++ b/static/scripts/viz/scatterplot.js
@@ -1,18 +1,3 @@
-define([
- "../libs/underscore",
-
- "../mvc/base-mvc",
- "../utils/LazyDataLoader",
- "../templates/compiled/template-visualization-scatterplotControlForm",
- "../templates/compiled/template-visualization-statsTable",
- "../templates/compiled/template-visualization-chartSettings",
-
- "../libs/d3",
-
- "../libs/bootstrap",
- "../libs/jquery/jquery-ui"
-], function(){
-
/* =============================================================================
todo:
outside this:
@@ -106,8 +91,8 @@
yNumTicks : 10,
xAxisLabelBumpY : 40,
yAxisLabelBumpX : -35,
- width : 500,
- height : 500,
+ width : 400,
+ height : 400,
//TODO: anyway to make this a sub-obj?
marginTop : 50,
marginRight : 50,
@@ -123,11 +108,14 @@
yLabel : "Y"
};
this.config = _.extend( {}, this.defaults, config );
+ this.log( 'intial config:', this.config );
this.updateConfig = function( newConfig, rerender ){
// setter for chart config
//TODO: validate here
_.extend( this.config, newConfig );
+ this.log( this + '.updateConfig:', this.config );
+ //TODO: implement rerender flag
};
// ........................................................ helpers
@@ -143,20 +131,6 @@
};
// ........................................................ initial element creation
- //NOTE: called on new
- this.svg = d3.select( this.config.containerSelector )
- .append( "svg:svg" ).attr( "class", "chart" );
-
- this.content = this.svg.append( "svg:g" ).attr( "class", "content" ).attr( 'id', this.config.id );
-
- this.xAxis = this.content.append( 'g' ).attr( 'class', 'axis' ).attr( 'id', 'x-axis' );
- this.xAxisLabel = this.xAxis.append( 'text' ).attr( 'class', 'axis-label' ).attr( 'id', 'x-axis-label' );
-
- this.yAxis = this.content.append( 'g' ).attr( 'class', 'axis' ).attr( 'id', 'y-axis' );
- this.yAxisLabel = this.yAxis.append( 'text' ).attr( 'class', 'axis-label' ).attr( 'id', 'y-axis-label' );
-
- //this.log( 'built svg:', d3.selectAll( 'svg' ) );
-
this.adjustChartDimensions = function( top, right, bottom, left ){
//this.log( this + '.adjustChartDimensions', arguments );
top = top || 0;
@@ -186,8 +160,8 @@
return ( data.length > this.config.maxDataPoints )? ( data.slice( 0, this.config.maxDataPoints ) ): ( data );
};
- this.setUpDomains = function( xCol, yCol, meta ){
- //this.log( this + '.setUpDomains', arguments );
+ this.findMinMaxes = function( xCol, yCol, meta ){
+ //this.log( this + '.findMinMaxes', arguments );
// configuration takes priority, otherwise meta (from the server) if passed, last-resort: compute it here
this.xMin = this.config.xMin || ( meta )?( meta[0].min ):( d3.min( xCol ) );
this.xMax = this.config.xMax || ( meta )?( meta[0].max ):( d3.max( xCol ) );
@@ -220,22 +194,22 @@
.call( this.xAxisFn );
//this.log( 'xAxis:', this.xAxis );
- this.xLongestLabel = d3.max( _.map( [ this.xMin, this.xMax ],
+ //TODO: adjust ticks when tick labels are long - move odds down and extend tick line
+ // (for now) hide them
+ var xLongestTickLabel = d3.max( _.map( [ this.xMin, this.xMax ],
function( number ){ return ( String( number ) ).length; } ) );
- //this.log( 'xLongestLabel:', this.xLongestLabel );
-
- if( this.xLongestLabel >= X_LABEL_TOO_LONG_AT ){
- //TODO: adjust ticks when tick labels are long - move odds down and extend tick line
- // (for now) hide them
+ //this.log( 'xLongestTickLabel:', xLongestTickLabel );
+ if( xLongestTickLabel >= X_LABEL_TOO_LONG_AT ){
this.xAxis.selectAll( 'g' ).filter( ':nth-child(odd)' ).style( 'display', 'none' );
}
+ this.log( 'this.config.xLabel:', this.config.xLabel );
this.xAxisLabel// = xAxis.select( 'text#x-axis-label' )
.attr( 'x', this.config.width / 2 )
.attr( 'y', this.config.xAxisLabelBumpY )
.attr( 'text-anchor', 'middle' )
.text( this.config.xLabel );
- //this.log( 'xAxisLabel:', this.xAxisLabel );
+ this.log( 'xAxisLabel:', this.xAxisLabel );
};
this.setUpYAxis = function(){
@@ -248,9 +222,10 @@
.call( this.yAxisFn );
//this.log( 'yAxis:', this.yAxis );
+ // a too complicated section for increasing the left margin when tick labels are long
// get the tick labels for the y axis
var yTickLabels = this.yAxis.selectAll( 'text' ).filter( function( e, i ){ return i !== 0; } );
- //this.log( 'yTickLabels:', yTickLabels );
+ this.log( 'yTickLabels:', yTickLabels );
// get the longest label length (or 0 if no labels)
this.yLongestLabel = d3.max(
@@ -412,7 +387,7 @@
}
// titles
- newDatapoints.attr( 'title', function( d, i ){
+ newDatapoints.attr( 'svg:title', function( d, i ){
return (( ids )?( ids[ i ] + ': ' ):( '' )) + newXCol[ i ] + ', ' + newYCol[ i ];
});
@@ -462,6 +437,9 @@
this.render = function( columnData, meta ){
this.log( this + '.render', arguments );
+ this.log( '\t config:', this.config );
+
+ // prepare the data
//pre: columns passed are numeric
//pre: at least two columns are passed
//assume: first column is x, second column is y, any remaining aren't used
@@ -475,12 +453,32 @@
yCol = this.preprocessData( yCol );
this.log( 'xCol len', xCol.length, 'yCol len', yCol.length );
- this.setUpDomains( xCol, yCol, meta );
+ this.findMinMaxes( xCol, yCol, meta );
//this.log( 'xMin, xMax, yMin, yMax:', this.xMin, this.xMax, this.yMin, this.yMax );
+ this.setUpScales();
+
+ // build the svg dom infrastructure
+ if( !this.svg ){ this.svg = d3.select( 'svg' ).attr( "class", "chart" ); }
+ if( !this.content ){
+ this.content = this.svg.append( "svg:g" ).attr( "class", "content" ).attr( 'id', this.config.id );
+ }
+ //this.log( 'svg:', this.svg );
+ //this.log( 'content:', this.content );
+
+ this.adjustChartDimensions();
- this.setUpScales();
- this.adjustChartDimensions();
-
+ if( !this.xAxis ){ this.xAxis = this.content.append( 'g' ).attr( 'class', 'axis' ).attr( 'id', 'x-axis' ); }
+ if( !this.xAxisLabel ){
+ this.xAxisLabel = this.xAxis.append( 'text' ).attr( 'class', 'axis-label' ).attr( 'id', 'x-axis-label' );
+ }
+ //this.log( 'xAxis:', this.xAxis, 'xAxisLabel:', this.xAxisLabel );
+
+ if( !this.yAxis ){ this.yAxis = this.content.append( 'g' ).attr( 'class', 'axis' ).attr( 'id', 'y-axis' ); }
+ if( !this.yAxisLabel ){
+ this.yAxisLabel = this.yAxis.append( 'text' ).attr( 'class', 'axis-label' ).attr( 'id', 'y-axis-label' );
+ }
+ //this.log( 'yAxis:', this.yAxis, 'yAxisLabel:', this.yAxisLabel );
+
this.setUpXAxis();
this.setUpYAxis();
@@ -490,510 +488,3 @@
}
//==============================================================================
-/**
- * Scatterplot control UI as a backbone view
- * handles:
- * getting the desired data
- * configuring the plot display
- * showing (general) statistics
- *
- * initialize attributes REQUIRES a dataset and an apiDatasetsURL
- */
-var ScatterplotControlForm = BaseView.extend( LoggableMixin ).extend({
- //logger : console,
- className : 'scatterplot-settings-form',
-
- dataLoadDelay : 500,
- dataLoadSize : 3001,
-
- loadingIndicatorImage : 'loading_large_white_bg.gif',
-
- initialize : function( attributes ){
- this.log( this + '.initialize, attributes:', attributes );
-
- this.dataset = null;
- this.chartConfig = null;
- this.plot = null;
- this.loader = null;
-
- this.$statsPanel = null;
- this.$chartSettingsPanel = null;
- this.$dataSettingsPanel = null;
- this.dataFetch = null;
-
- this.initializeFromAttributes( attributes );
- this.initializeChart( attributes );
- this.initializeDataLoader( attributes );
- },
-
- initializeFromAttributes : function( attributes ){
- // required settings: ensure certain vars we need are passed in attributes
- if( !attributes || !attributes.dataset ){
- throw( "ScatterplotView requires a dataset" );
- } else {
- this.dataset = attributes.dataset;
- }
- if( jQuery.type( this.dataset.metadata_column_types ) === 'string' ){
- this.dataset.metadata_column_types = this.dataset.metadata_column_types.split( ', ' );
- }
- this.log( 'dataset:', this.dataset );
-
- // passed from mako helper
- //TODO: integrate to galaxyPaths
- if( !attributes.apiDatasetsURL ){
- throw( "ScatterplotView requires a apiDatasetsURL" );
- } else {
- this.dataURL = attributes.apiDatasetsURL + '/' + this.dataset.id + '?';
- }
- this.log( 'this.dataURL:', this.dataURL );
- },
-
- initializeChart : function( attributes ){
- // set up the basic chart infrastructure and config (if any)
- this.chartConfig = attributes.chartConfig || {};
- if( this.logger ){ this.chartConfig.debugging = true; }
- this.log( 'initial chartConfig:', this.chartConfig );
- this.plot = new TwoVarScatterplot( this.chartConfig );
- this.chartConfig = this.plot.config;
- },
-
- initializeDataLoader : function( attributes ){
- // set up data loader
- var view = this;
- this.loader = new LazyDataLoader({
- logger : ( this.logger )?( this.logger ):( null ),
- // we'll generate this when columns are chosen
- url : null,
- start : attributes.start || 0,
- //NOTE: metadata_data_lines can be null (so we won't know the total)
- total : attributes.total || this.dataset.metadata_data_lines,
- delay : this.dataLoadDelay,
- size : this.dataLoadSize,
-
- buildUrl : function( start, size ){
- // currently VERY SPECIFIC to using data_providers.py start_val, max_vals params
- return this.url + '&' + jQuery.param({
- start_val: start,
- max_vals: size
- });
- }
- });
- $( this.loader ).bind( 'error', function( event, status, error ){
- view.log( 'ERROR:', status, error );
- alert( 'ERROR fetching data:\n' + status + '\n' + error );
- view.hideLoadingIndicator();
- });
- },
-
- // ------------------------------------------------------------------------- CONTROLS RENDERING
- render : function(){
- // render the data/chart control forms (but not the chart)
- var view = this,
- formData = {
- config : this.chartConfig,
- allColumns : [],
- numericColumns : [],
- loadingIndicatorImagePath : galaxy_paths.get( 'image_path' ) + '/' + this.loadingIndicatorImage
- };
-
- //TODO: isNumericColumn
- // gather column indeces (from metadata_column_types) and names (from metadata_columnnames)
- _.each( this.dataset.metadata_column_types, function( type, index ){
- // label with the name if available (fall back on 'column <index>')
- var name = 'column ' + ( index + 1 );
- if( view.dataset.metadata_column_names ){
- name = view.dataset.metadata_column_names[ index ];
- }
-
- // filter numeric columns to their own list
- if( type === 'int' || type === 'float' ){
- formData.numericColumns.push({ index: ( index + 1 ), name: name });
- }
- formData.allColumns.push({ index: ( index + 1 ), name: name });
- });
- //TODO: other vals: max_vals, start_val, pagination (plot-settings)
-
- // render template and set up panels, store refs
- this.$el.append( ScatterplotControlForm.templates.form( formData ) );
-
- this.$dataSettingsPanel = this.$el.find( '.tab-pane#data-settings' );
- this.$chartSettingsPanel = this._render_chartSettings();
- this.$statsPanel = this.$el.find( '.tab-pane#chart-stats' );
-
- // preset to column selectors if they were passed in the config
- this.$dataSettingsPanel.find( '#X-select' ).val( this.chartConfig.xColumn );
- this.$dataSettingsPanel.find( '#Y-select' ).val( this.chartConfig.yColumn );
- if( this.chartConfig.idColumn !== undefined ){
- this.$dataSettingsPanel.find( '#include-id-checkbox' )
- .attr( 'checked', true ).trigger( 'change' );
- this.$dataSettingsPanel.find( '#ID-select' ).val( this.chartConfig.idColumn );
- }
-
- //this.$el.find( 'ul.nav' ).find( 'a[href="#chart-settings"]' ).tab( 'show' );
-
- //TODO:?? add autoRender=1 to query maybe?
- if( this.chartConfig.xColumn && this.chartConfig.yColumn ){
- this.renderPlot();
- }
- return this;
- },
-
- //TODO: seems like a function of dataset or metadata
- isColumnNumeric : function( index ){
- if( ( index >= 0 ) && ( index < this.dataset.metadata_column_types.length ) ){
- var columnType = this.dataset.metadata_column_types[ index ];
- return ( columnType === 'int' || columnType === 'float' );
- }
- return false;
- },
-
- _render_chartSettings : function(){
- // chart settings panel
- var chartControl = this,
- $chartSettingsPanel = this.$el.find( '.tab-pane#chart-settings' ),
- // limits for controls (by control/chartConfig id)
- //TODO: move into TwoVarScatterplot
- controlRanges = {
- 'datapointSize' : { min: 2, max: 10, step: 1 },
- 'width' : { min: 200, max: 800, step: 20 },
- 'height' : { min: 200, max: 800, step: 20 }
- };
-
- // render the html
- $chartSettingsPanel.append( ScatterplotControlForm.templates.chartSettings( this.chartConfig ) );
-
- // set up js on sliders
- $chartSettingsPanel.find( '.numeric-slider-input' ).each( function(){
- var $this = $( this ),
- $output = $this.find( '.slider-output' ),
- $slider = $this.find( '.slider' ),
- id = $this.attr( 'id' );
- //chartControl.log( 'slider set up', 'this:', $this, 'slider:', $slider, 'id', id );
-
- // what to do when the slider changes: update display and update chartConfig
- function onSliderChange(){
- var $this = $( this ),
- newValue = $this.slider( 'value' );
- //chartControl.log( 'slider change', 'this:', $this, 'output:', $output, 'value', newValue );
- $output.text( newValue );
- //chartControl.chartConfig[ id ] = newValue;
- }
-
- $slider.slider( _.extend( controlRanges[ id ], {
- value : chartControl.chartConfig[ id ],
- change : onSliderChange,
- slide : onSliderChange
- }));
- });
-
- return $chartSettingsPanel;
- },
-
- // ------------------------------------------------------------------------- EVENTS
- events : {
- 'change #include-id-checkbox' : 'toggleThirdColumnSelector',
- 'click #data-settings #render-button' : 'renderPlot',
- 'click #chart-settings #render-button' : 'changeChartSettings'
- },
-
- toggleThirdColumnSelector : function(){
- // show/hide the id selector on the data settings panel
- this.$el.find( 'select[name="ID"]' ).parent().toggle();
- },
-
- showLoadingIndicator : function( message, callback ){
- // display the loading indicator over the tab panels if hidden, update message (if passed)
- message = message || '';
- var indicator = this.$el.find( 'div#loading-indicator' );
- messageBox = indicator.find( '.loading-message' );
-
- if( indicator.is( ':visible' ) ){
- if( message ){
- messageBox.fadeOut( 'fast', function(){
- messageBox.text( message );
- messageBox.fadeIn( 'fast', callback );
- });
- } else {
- callback();
- }
-
- } else {
- if( message ){ messageBox.text( message ); }
- indicator.fadeIn( 'fast', callback );
- }
- },
-
- hideLoadingIndicator : function( callback ){
- this.$el.find( 'div#loading-indicator' ).fadeOut( 'fast', callback );
- },
-
- // ------------------------------------------------------------------------- GRAPH/STATS RENDERING
- renderPlot : function(){
- // fetch the data, (re-)render the chart
- //TODO: separate data fetch
- var view = this;
-
- // this is a complete re-render, so clear the prev. data
- view.data = null;
- view.meta = null;
-
- // update the chartConfig (here and plot) using graph settings
- //TODO: separate and improve (used in changeChartSettings too)
- _.extend( this.chartConfig, this.getGraphSettings() );
- this.log( 'this.chartConfig:', this.chartConfig );
- this.plot.updateConfig( this.chartConfig, false );
-
- // build the url with the current data settings
- this.loader.url = this.dataURL + '&' + jQuery.param( this.getDataSettings() );
- this.log( 'this.loader, url:', this.loader.url, 'total:', this.loader.total );
-
- // bind the new data event to: aggregate data, update the chart and stats with new data
- $( this.loader ).bind( 'loaded.new', function( event, response ){
- view.log( view + ' loaded.new', response );
-
- // aggregate data and meta
- view.postProcessDataFetchResponse( response );
- view.log( 'postprocessed data:', view.data, 'meta:', view.meta );
-
- // update the graph and stats
- view.showLoadingIndicator( 'Rendering...', function(){
- view.$el.find( 'ul.nav' ).find( 'a[href="#chart-stats"]' ).tab( 'show' );
-
- view.plot.render( view.data, view.meta );
- view.renderStats( view.data, view.meta );
- view.hideLoadingIndicator();
- });
- });
- // when all data loaded - unbind (or we'll start doubling event handlers)
- $( this.loader ).bind( 'complete', function( event, data ){
- view.log( 'complete', data );
- $( view.loader ).unbind();
- });
-
- // begin loading the data
- view.showLoadingIndicator( 'Fetching data...', function(){ view.loader.load(); });
- },
-
- renderStats : function(){
- // render the stats table in the stats panel
- //TODO: there's a better way
- this.$statsPanel.html( ScatterplotControlForm.templates.statsTable({
- stats: [
- { name: 'Count', xval: this.meta[0].count, yval: this.meta[1].count },
- { name: 'Min', xval: this.meta[0].min, yval: this.meta[1].min },
- { name: 'Max', xval: this.meta[0].max, yval: this.meta[1].max },
- { name: 'Sum', xval: this.meta[0].sum, yval: this.meta[1].sum },
- { name: 'Mean', xval: this.meta[0].mean, yval: this.meta[1].mean },
- { name: 'Median', xval: this.meta[0].median, yval: this.meta[1].median }
- ]
- }));
- },
-
- changeChartSettings : function(){
- // re-render the chart with new chart settings and OLD data
- var view = this;
- newGraphSettings = this.getGraphSettings();
-
- // update the chart config from the chartSettings panel controls
- this.log( 'newGraphSettings:', newGraphSettings );
- _.extend( this.chartConfig, newGraphSettings );
- this.log( 'this.chartConfig:', this.chartConfig );
- this.plot.updateConfig( this.chartConfig, false );
-
- // if there's current data, call plot.render with it (no data fetch)
- if( view.data && view.meta ){
- view.showLoadingIndicator( 'Rendering...', function(){
- view.plot.render( view.data, view.meta );
- view.hideLoadingIndicator();
- });
-
- // no current data, call renderPlot instead (which will fetch data)
- } else {
- this.renderPlot();
- }
- },
-
- // ------------------------------------------------------------------------- DATA AGGREGATION
- postProcessDataFetchResponse : function( response ){
- // the loader only returns new data - it's up to this to munge the fetches together properly
- //TODO: we're now storing data in two places: loader and here
- // can't we reduce incoming data into loader.data[0]? are there concurrency problems?
- this.postProcessData( response.data );
- this.postProcessMeta( response.meta );
- },
-
- postProcessData : function( newData ){
- // stack the column data on top of each other into this.data
- var view = this;
-
- // if we already have data: aggregate
- if( view.data ){
- _.each( newData, function( newColData, colIndex ){
- //view.log( colIndex + ' data:', newColData );
- //TODO??: time, space efficiency of this?
- view.data[ colIndex ] = view.data[ colIndex ].concat( newColData );
- });
-
- // otherwise: assign (first load)
- } else {
- view.data = newData;
- }
- },
-
- postProcessMeta : function( newMeta ){
- // munge the meta data (stats) from the server fetches together
- //pre: this.data must be preprocessed (needed for medians)
- var view = this,
- colTypes = this.dataset.metadata_column_types;
-
- // if we already have meta: aggregate
- if( view.meta ){
- _.each( newMeta, function( newColMeta, colIndex ){
- var colMeta = view.meta[ colIndex ],
- colType = colTypes[ colIndex ];
- view.log( colIndex + ' postprocessing meta:', newColMeta );
- //view.log( colIndex + ' old meta:',
- // 'min:', colMeta.min,
- // 'max:', colMeta.max,
- // 'sum:', colMeta.sum,
- // 'mean:', colMeta.mean,
- // 'median:', colMeta.median
- //);
-
- //!TODO: at what point are we getting int/float overflow on these?!
- //??: need to be null safe?
- colMeta.count += ( newColMeta.count )?( newColMeta.count ):( 0 );
- view.log( colIndex, 'count:', colMeta.count );
-
- if( ( colType === 'int' ) || ( colType === 'float' ) ){
- //view.log( colIndex + ' incoming meta:',
- // 'min:', newColMeta.min,
- // 'max:', newColMeta.max,
- // 'sum:', newColMeta.sum,
- // 'mean:', newColMeta.mean,
- // 'median:', newColMeta.median
- //);
-
- colMeta.min = Math.min( newColMeta.min, colMeta.min );
- colMeta.max = Math.max( newColMeta.max, colMeta.max );
- colMeta.sum = newColMeta.sum + colMeta.sum;
- colMeta.mean = ( colMeta.count )?( colMeta.sum / colMeta.count ):( null );
-
- // median's a pain bc of sorting (requires the data as well)
- var sortedCol = view.data[ colIndex ].slice().sort(),
- middleIndex = Math.floor( sortedCol.length / 2 );
-
- if( sortedCol.length % 2 === 0 ){
- colMeta.median = ( ( sortedCol[ middleIndex ] + sortedCol[( middleIndex + 1 )] ) / 2 );
-
- } else {
- colMeta.median = sortedCol[ middleIndex ];
- }
-
- //view.log( colIndex + ' new meta:',
- // 'min:', colMeta.min,
- // 'max:', colMeta.max,
- // 'sum:', colMeta.sum,
- // 'mean:', colMeta.mean,
- // 'median:', colMeta.median
- //);
- }
- });
-
- // otherwise: assign (first load)
- } else {
- view.meta = newMeta;
- view.log( 'initial meta:', view.meta );
- }
- },
-
- // ------------------------------------------------------------------------- GET DATA/GRAPH SETTINGS
- getDataSettings : function(){
- // parse the column values for both indeces (for the data fetch) and names (for the graph)
- var columnSelections = this.getColumnSelections(),
- columns = [];
- this.log( 'columnSelections:', columnSelections );
-
- //TODO: validate columns - minimally: we can assume either set by selectors or via a good query string
-
- // get column indices for params, include the desired ID column (if any)
- columns = [ columnSelections.X.colIndex, columnSelections.Y.colIndex ];
- if( this.$dataSettingsPanel.find( '#include-id-checkbox' ).attr( 'checked' ) ){
- columns.push( columnSelections.ID.colIndex );
- }
-
- //TODO: other vals: max, start, page
-
- var params = {
- data_type : 'raw_data',
- columns : '[' + columns + ']'
- };
- this.log( 'params:', params );
- return params;
- },
-
- getColumnSelections : function(){
- // gets the current user-selected values for which columns to fetch from the data settings panel
- // returns a map: { column-select name (eg. X) : { colIndex : column-selector val,
- // colName : selected option text }, ... }
- var selections = {};
- this.$dataSettingsPanel.find( 'div.column-select select' ).each( function(){
- var $this = $( this ),
- val = parseInt( $this.val(), 10 ) - 1;
- selections[ $this.attr( 'name' ) ] = {
- colIndex : val,
- colName : $this.children( '[value="' + val + '"]' ).text()
- };
- });
- return selections;
- },
-
- getGraphSettings : function(){
- // gets the user-selected chartConfig from the chart settings panel
- var settings = {},
- colSelections = this.getColumnSelections();
- //this.log( 'colSelections:', colSelections );
-
- //TODO: simplify with keys and loop
- settings.datapointSize = this.$chartSettingsPanel.find( '#datapointSize.numeric-slider-input' )
- .find( '.slider' ).slider( 'value' );
- settings.width = this.$chartSettingsPanel.find( '#width.numeric-slider-input' )
- .find( '.slider' ).slider( 'value' );
- settings.height = this.$chartSettingsPanel.find( '#height.numeric-slider-input' )
- .find( '.slider' ).slider( 'value' );
-
- // update axes labels using chartSettings inputs (if not at defaults), otherwise the selects' colName
- //TODO: a little confusing
- var chartSettingsXLabel = this.$chartSettingsPanel.find( 'input#X-axis-label' ).val(),
- chartSettingsYLabel = this.$chartSettingsPanel.find( 'input#Y-axis-label' ).val();
- settings.xLabel = ( chartSettingsXLabel === 'X' )?
- ( colSelections.X.colName ):( chartSettingsXLabel );
- settings.yLabel = ( chartSettingsYLabel === 'Y' )?
- ( colSelections.Y.colName ):( chartSettingsYLabel );
-
- settings.animDuration = 10;
- if( this.$chartSettingsPanel.find( '#animDuration.checkbox-input' ).is( ':checked' ) ){
- settings.animDuration = 500;
- }
-
- this.log( 'graphSettings:', settings );
- return settings;
- },
-
- toString : function(){
- return 'ScatterplotControlForm(' + (( this.dataset )?( this.dataset.id ):( '' )) + ')';
- }
-});
-
-ScatterplotControlForm.templates = {
- form : Handlebars.templates[ 'template-visualization-scatterplotControlForm' ],
- statsTable : Handlebars.templates[ 'template-visualization-statsTable' ],
- chartSettings : Handlebars.templates[ 'template-visualization-chartSettings' ]
-};
-
-//==============================================================================
-return {
- LazyDataLoader : LazyDataLoader,
- TwoVarScatterplot : TwoVarScatterplot,
- ScatterplotControlForm : ScatterplotControlForm
-};});
diff -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 -r 9bf411ee2476c21c4c3fce6a5bbabf508f7505ff templates/visualization/scatterplot.mako
--- a/templates/visualization/scatterplot.mako
+++ b/templates/visualization/scatterplot.mako
@@ -12,21 +12,7 @@
/*TODO: use/move into base.less*/
* { margin: 0px; padding: 0px; }
-/* -------------------------------------------- layout */
-.column {
- position:relative;
- overflow: auto;
-}
-
-.left-column {
- float: left;
- width: 40%;
-}
-
-.right-column {
- margin-left: 41%;
-}
-
+/* -------------------------------------------- general layout */
div.tab-pane {
padding: 8px;
}
@@ -39,6 +25,7 @@
#chart-header {
padding : 8px;
background-color: #ebd9b2;
+ margin-bottom: 16px;
}
#chart-header .subtitle {
@@ -48,90 +35,98 @@
font-size: small;
}
-/* -------------------------------------------- all controls */
-#chart-settings-form {
+/* -------------------------------------------- main layout */
+#scatterplot {
/*from width + margin of chart?*/
- padding-top: 1em;
}
-#chart-settings-form input[type=button],
-#chart-settings-form select {
+.scatterplot-container .tab-pane {
+}
+
+/* -------------------------------------------- all controls */
+
+#scatterplot input[type=button],
+#scatterplot select {
width: 100%;
max-width: 256px;
margin-bottom: 8px;
}
-#chart-settings-form .help-text,
-#chart-settings-form .help-text-small {
+#scatterplot .help-text,
+#scatterplot .help-text-small {
color: grey;
}
-#chart-settings-form .help-text {
+#scatterplot .help-text {
padding-bottom: 16px;
}
-#chart-settings-form .help-text-small {
+#scatterplot .help-text-small {
padding: 4px;
font-size: smaller;
}
-#chart-settings-form > * {
+#scatterplot > * {
}
-#chart-settings-form input[value=Draw] {
+#scatterplot input[value=Draw] {
display: block;
margin-top: 16px;
}
+#scatterplot .numeric-slider-input {
+ max-width: 70%;
+}
+
/* -------------------------------------------- data controls */
/* -------------------------------------------- chart controls */
-#chart-settings .form-input {
+#chart-control .form-input {
/*display: table-row;*/
}
-#chart-settings label {
+#chart-control label {
/*text-align: right;*/
margin-bottom: 8px;
/*display: table-cell;*/
}
-#chart-settings .slider {
+#chart-control .slider {
/*display: table-cell;*/
height: 8px;
display: block;
margin: 8px 0px 0px 8px;
}
-#chart-settings .slider-output {
+#chart-control .slider-output {
/*display: table-cell;*/
float: right;
}
-#chart-settings input[type="text"] {
+#chart-control input[type="text"] {
border: 1px solid lightgrey;
}
/* -------------------------------------------- statistics */
-#chart-stats table#chart-stats-table {
+#stats-display table#chart-stats-table {
width: 100%;
}
-#chart-stats #chart-stats-table th {
+#stats-display #chart-stats-table th {
width: 30%;
padding: 4px;
font-weight: bold;
color: grey;
}
-#chart-stats #chart-stats-table td {
+#stats-display #chart-stats-table td {
border: solid lightgrey;
border-width: 1px 0px 0px 1px;
padding: 4px;
}
-#chart-stats #chart-stats-table td:nth-child(1) {
+#stats-display #chart-stats-table td:nth-child(1) {
border-width: 1px 0px 0px 0px;
padding-right: 1em;
text-align: right;
@@ -141,19 +136,12 @@
/* -------------------------------------------- load indicators */
#loading-indicator {
- z-index: 2;
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- background-color: white;
- padding: 32px 0 0 32px;
+ margin: 12px 0px 0px 8px;
}
-#chart-settings-form #loading-indicator .loading-message {
- margin-left: 10px;
+#scatterplot #loading-indicator .loading-message {
font-style: italic;
+ font-size: smaller;
color: grey;
}
@@ -208,18 +196,37 @@
<%def name="javascripts()">
${parent.javascripts()}
-${h.js( "libs/require" )}
+${h.js(
+
+ "libs/underscore",
+ "libs/jquery/jquery-ui",
+ "libs/d3",
+
+ "mvc/base-mvc",
+ "utils/LazyDataLoader",
+ "viz/scatterplot"
+)}
+
+${h.templates(
+ "../../templates/compiled/template-visualization-scatterplotControlForm",
+ "../../templates/compiled/template-visualization-dataControl",
+ "../../templates/compiled/template-visualization-chartControl",
+ "../../templates/compiled/template-visualization-chartDisplay",
+ "../../templates/compiled/template-visualization-statsDisplay"
+)}
+
+${h.js(
+ "mvc/visualizations/scatterplotControlForm",
+)}
<script type="text/javascript">
-require.config({ baseUrl : "${h.url_for( '/static/scripts' )}", });
+$(function(){
-require([ "viz/scatterplot" ], function( scatterplot ){
-
var hda = ${h.to_json_string( hda )},
historyID = '${historyID}',
querySettings = ${h.to_json_string( kwargs )},
chartConfig = _.extend( querySettings, {
- containerSelector : '#chart-holder',
+ containerSelector : '#chart',
//TODO: move to ScatterplotControlForm.initialize
marginTop : ( querySettings.marginTop > 20 )?( querySettings.marginTop ):( 20 ),
@@ -229,18 +236,29 @@
});
//console.debug( querySettings );
- var settingsForm = new scatterplot.ScatterplotControlForm({
+ var settingsForm = new ScatterplotControlForm({
dataset : hda,
apiDatasetsURL : "${h.url_for( controller='/api/datasets' )}",
- el : $( '#chart-settings-form' ),
+ el : $( '#scatterplot' ),
chartConfig : chartConfig
}).render();
});
+</script>
+</%def>
+<%def name="body()">
+ <!--dataset info-->
+ <div id="chart-header" class="header">
+ <h2 class="title">Scatterplot of '${hda['name']}'</h2>
+ <p class="subtitle">${hda['misc_info']}</p>
+ </div>
+ <div id="scatterplot" class="scatterplot-control-form"></div>
+</%def>
+
+<script type="text/javascript">
function make_abs_box( top, left, x, y, id ){
- console.debug( top, left, x, y, id );
var ARROW_SIZE = 8,
ARROW_COLOR = 'grey',
DIST_TO_POINT = 4,
@@ -296,26 +314,4 @@
//console.debug( boxContainer );
return boxContainer;
}
-
-
</script>
-</%def>
-
-<%def name="body()">
- <!--dataset info-->
- <div id="chart-header" class="header">
- <h2 class="title">Scatterplot of '${hda['name']}'</h2>
- <p class="subtitle">${hda['misc_info']}</p>
- </div>
- <div class="outer-container">
- <!--plot controls-->
- <div id="chart-settings-form" class="column left-column"></div>
- <!--plot-->
- <div class="column right-column">
- <div id="chart-holder" class="inner-container"></div>
- </div>
- <div style="clear: both;"></div>
- </div>
- <div id="test"></div>
-
-</%def>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/8143d7f7b944/
changeset: 8143d7f7b944
user: jgoecks
date: 2012-11-27 00:26:42
summary: Remove debugging statement.
affected #: 1 file
diff -r 1c5aa1094c417760f0c48225f5a3dd8b71245b0e -r 8143d7f7b94449889c1d590a3d72a3d20de291e3 templates/grid_base.mako
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -564,7 +564,6 @@
// HACK: use a simple string to separate the elements in the
// response: (1) table body; (2) number of pages in table; and (3) message.
var parsed_response_text = response_text.split("*****");
- console.log(parsed_response_text);
// Update grid body and footer.
$('#grid-table-body').html(parsed_response_text[0]);
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/1c5aa1094c41/
changeset: 1c5aa1094c41
user: jgoecks
date: 2012-11-27 00:25:52
summary: Some grid framework fixes.
affected #: 5 files
diff -r 8420c9a0f7308bbbd79c0a0db29dab063ff9070a -r 1c5aa1094c417760f0c48225f5a3dd8b71245b0e lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py
+++ b/lib/galaxy/web/framework/helpers/grids.py
@@ -20,6 +20,7 @@
title = ""
exposed = True
model_class = None
+ show_item_checkboxes = False
template = "grid_base.mako"
async_template = "grid_base_async.mako"
use_async = False
@@ -273,7 +274,8 @@
message = message,
use_panels=self.use_panels,
webapp=webapp,
- show_item_checkboxes = ( kwargs.get( 'show_item_checkboxes', '' ) in ['True', 'true'] ),
+ show_item_checkboxes = ( self.show_item_checkboxes or
+ kwargs.get( 'show_item_checkboxes', '' ) in [ 'True', 'true' ] ),
# Pass back kwargs so that grid template can set and use args without
# grid explicitly having to pass them.
kwargs=kwargs )
diff -r 8420c9a0f7308bbbd79c0a0db29dab063ff9070a -r 1c5aa1094c417760f0c48225f5a3dd8b71245b0e lib/galaxy/webapps/galaxy/controllers/page.py
--- a/lib/galaxy/webapps/galaxy/controllers/page.py
+++ b/lib/galaxy/webapps/galaxy/controllers/page.py
@@ -91,6 +91,7 @@
return item.name
# Grid definition.
+ show_item_checkboxes = True
template = "/page/select_items_grid.mako"
async_template = "/page/select_items_grid_async.mako"
default_filter = { "deleted" : "False" , "sharing" : "All" }
diff -r 8420c9a0f7308bbbd79c0a0db29dab063ff9070a -r 1c5aa1094c417760f0c48225f5a3dd8b71245b0e templates/grid_base.mako
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -564,10 +564,13 @@
// HACK: use a simple string to separate the elements in the
// response: (1) table body; (2) number of pages in table; and (3) message.
var parsed_response_text = response_text.split("*****");
+ console.log(parsed_response_text);
// Update grid body and footer.
$('#grid-table-body').html(parsed_response_text[0]);
- $('#grid-table-footer').html(parsed_response_text[1]);
+ // FIXME: this does not work at all; what's needed is a function
+ // that updates page links when number of pages changes.
+ //$('#grid-table-footer').html(parsed_response_text[1]);
// Trigger custom event to indicate grid body has changed.
$('#grid-table-body').trigger('update');
@@ -906,7 +909,7 @@
%>
Page:
% if min_page > 1:
- <span class='page-link'><a href="${url( page=1 )}" page_num="1">1</a></span> ...
+ <span class='page-link' id="page-link-1"><a href="${url( page=1 )}" page_num="1">1</a></span> ...
% endif
%for page_index in range(min_page, max_page + 1):
%if page_index == cur_page_num:
@@ -918,7 +921,7 @@
%endfor
%if max_page < num_pages:
...
- <span class='page-link'><a href="${url( page=num_pages )}" page_num="${num_pages}">${num_pages}</a></span>
+ <span class='page-link' id="page-link-${num_pages}"><a href="${url( page=num_pages )}" page_num="${num_pages}">${num_pages}</a></span>
%endif
</span>
diff -r 8420c9a0f7308bbbd79c0a0db29dab063ff9070a -r 1c5aa1094c417760f0c48225f5a3dd8b71245b0e templates/page/select_items_grid.mako
--- a/templates/page/select_items_grid.mako
+++ b/templates/page/select_items_grid.mako
@@ -1,7 +1,7 @@
## Template generates a grid that enables user to select items.
<%namespace file="../grid_base.mako" import="*" />
-${javascripts()}
+${grid_javascripts()}
${stylesheets()}
${render_grid_header( grid, False )}
${render_grid_table( grid, show_item_checkboxes=True )}
diff -r 8420c9a0f7308bbbd79c0a0db29dab063ff9070a -r 1c5aa1094c417760f0c48225f5a3dd8b71245b0e templates/page/select_items_grid_async.mako
--- a/templates/page/select_items_grid_async.mako
+++ b/templates/page/select_items_grid_async.mako
@@ -2,7 +2,7 @@
<%namespace file="/display_common.mako" import="render_message" />
## Always show item checkboxes so that users can select items.
-${render_grid_table_body_contents( grid, show_item_checkboxes=True )}
+${render_grid_table_body_contents( grid, show_item_checkboxes=show_item_checkboxes )}
*****
${num_pages}
*****
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: natefoo: Explicitly import galaxy.util.shed_util in galaxy.tool_shed that was causing a failure to start on test.
by Bitbucket 26 Nov '12
by Bitbucket 26 Nov '12
26 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/8420c9a0f730/
changeset: 8420c9a0f730
user: natefoo
date: 2012-11-26 19:45:14
summary: Explicitly import galaxy.util.shed_util in galaxy.tool_shed that was causing a failure to start on test.
affected #: 1 file
diff -r f03725b8272bd13aa3fc693b9fcb20d2f0f4c28f -r 8420c9a0f7308bbbd79c0a0db29dab063ff9070a lib/galaxy/tool_shed/__init__.py
--- a/lib/galaxy/tool_shed/__init__.py
+++ b/lib/galaxy/tool_shed/__init__.py
@@ -2,7 +2,7 @@
Classes encapsulating the management of repositories installed from Galaxy tool sheds.
"""
import os
-from galaxy.util.shed_util import *
+import galaxy.util.shed_util
from galaxy.model.orm import *
from galaxy import eggs
@@ -27,7 +27,7 @@
ElementInclude.include( root )
tool_path = root.get( 'tool_path', None )
if tool_path:
- tool_shed = clean_tool_shed_url( tool_shed_repository.tool_shed )
+ tool_shed = galaxy.util.shed_util.clean_tool_shed_url( tool_shed_repository.tool_shed )
relative_path = os.path.join( tool_path,
tool_shed,
'repos',
@@ -44,13 +44,13 @@
.order_by( self.model.ToolShedRepository.table.c.id ):
relative_install_dir = self.get_repository_install_dir( tool_shed_repository )
if relative_install_dir:
- installed_repository_dict = load_installed_datatypes( self.app, tool_shed_repository, relative_install_dir )
+ installed_repository_dict = galaxy.util.shed_util.load_installed_datatypes( self.app, tool_shed_repository, relative_install_dir )
if installed_repository_dict:
self.installed_repository_dicts.append( installed_repository_dict )
def load_proprietary_converters_and_display_applications( self, deactivate=False ):
for installed_repository_dict in self.installed_repository_dicts:
if installed_repository_dict[ 'converter_path' ]:
- load_installed_datatype_converters( self.app, installed_repository_dict, deactivate=deactivate )
+ galaxy.util.shed_util.load_installed_datatype_converters( self.app, installed_repository_dict, deactivate=deactivate )
if installed_repository_dict[ 'display_path' ]:
- load_installed_display_applications( self.app, installed_repository_dict, deactivate=deactivate )
-
\ No newline at end of file
+ galaxy.util.shed_util.load_installed_display_applications( self.app, installed_repository_dict, deactivate=deactivate )
+
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/ec3b5956130f/
changeset: ec3b5956130f
user: jmchilton
date: 2012-10-31 18:18:55
summary: Fix small bug in exception handler in lib/galaxy/jobs/handler.py. The bug was preventing exceptions in dynamic job runner methods from being percolated up into the logs.
affected #: 1 file
diff -r 4ceab232dc02a5a99eca0b24c0038c490d49a392 -r ec3b5956130f767d6f5d9c07c6a64dafd128351f lib/galaxy/jobs/handler.py
--- a/lib/galaxy/jobs/handler.py
+++ b/lib/galaxy/jobs/handler.py
@@ -397,6 +397,11 @@
def put( self, job_wrapper ):
try:
runner_name = self.__get_runner_name( job_wrapper )
+ except Exception:
+ log.exception( 'Failed to generate job runner name' )
+ job_wrapper.fail( 'Unable to run job due to a misconfiguration of the Galaxy job running system. Please contact a site administrator.' )
+ return
+ try:
if self.app.config.use_tasked_jobs and job_wrapper.tool.parallelism is not None and isinstance(job_wrapper, TaskWrapper):
#DBTODO Refactor
log.debug( "dispatching task %s, of job %d, to %s runner" %( job_wrapper.task_id, job_wrapper.job_id, runner_name ) )
https://bitbucket.org/galaxy/galaxy-central/changeset/0206d3a8a4d0/
changeset: 0206d3a8a4d0
user: jmchilton
date: 2012-10-31 21:04:33
summary: Allow dynamic job runner rules to pass error messages back to users via job failure message. To do this simply raise galaxy.jobs.mapper.JobMappingException(user_messsage) from a rule method where user_message is the desired message.
affected #: 2 files
diff -r ec3b5956130f767d6f5d9c07c6a64dafd128351f -r 0206d3a8a4d03b2447ab1ef09cd2a2015ce4c3ae lib/galaxy/jobs/handler.py
--- a/lib/galaxy/jobs/handler.py
+++ b/lib/galaxy/jobs/handler.py
@@ -17,6 +17,7 @@
# States for running a job. These are NOT the same as data states
JOB_WAIT, JOB_ERROR, JOB_INPUT_ERROR, JOB_INPUT_DELETED, JOB_READY, JOB_DELETED, JOB_ADMIN_DELETED = 'wait', 'error', 'input_error', 'input_deleted', 'ready', 'deleted', 'admin_deleted'
+DEFAULT_JOB_PUT_FAILURE_MESSAGE = 'Unable to run job due to a misconfiguration of the Galaxy job running system. Please contact a site administrator.'
class JobHandler( object ):
"""
@@ -41,6 +42,7 @@
a JobRunner.
"""
STOP_SIGNAL = object()
+
def __init__( self, app, dispatcher ):
"""Start the job manager"""
self.app = app
@@ -397,9 +399,13 @@
def put( self, job_wrapper ):
try:
runner_name = self.__get_runner_name( job_wrapper )
- except Exception:
- log.exception( 'Failed to generate job runner name' )
- job_wrapper.fail( 'Unable to run job due to a misconfiguration of the Galaxy job running system. Please contact a site administrator.' )
+ except Exception, e:
+ failure_message = getattr(e, 'failure_message', DEFAULT_JOB_PUT_FAILURE_MESSAGE )
+ if failure_message == DEFAULT_JOB_PUT_FAILURE_MESSAGE:
+ log.exception( 'Failed to generate job runner name' )
+ else:
+ log.debug( "Intentionally failing job with message (%s)" % failure_message )
+ job_wrapper.fail( failure_message )
return
try:
if self.app.config.use_tasked_jobs and job_wrapper.tool.parallelism is not None and isinstance(job_wrapper, TaskWrapper):
@@ -410,7 +416,7 @@
self.job_runners[runner_name].put( job_wrapper )
except KeyError:
log.error( 'put(): (%s) Invalid job runner: %s' % ( job_wrapper.job_id, runner_name ) )
- job_wrapper.fail( 'Unable to run job due to a misconfiguration of the Galaxy job running system. Please contact a site administrator.' )
+ job_wrapper.fail( DEFAULT_JOB_PUT_FAILURE_MESSAGE )
def stop( self, job ):
"""
@@ -452,7 +458,7 @@
self.job_runners[runner_name].recover( job, job_wrapper )
except KeyError:
log.error( 'recover(): (%s) Invalid job runner: %s' % ( job_wrapper.job_id, runner_name ) )
- job_wrapper.fail( 'Unable to run job due to a misconfiguration of the Galaxy job running system. Please contact a site administrator.' )
+ job_wrapper.fail( DEFAULT_JOB_PUT_FAILURE_MESSAGE )
def shutdown( self ):
for runner in self.job_runners.itervalues():
diff -r ec3b5956130f767d6f5d9c07c6a64dafd128351f -r 0206d3a8a4d03b2447ab1ef09cd2a2015ce4c3ae lib/galaxy/jobs/mapper.py
--- a/lib/galaxy/jobs/mapper.py
+++ b/lib/galaxy/jobs/mapper.py
@@ -8,6 +8,12 @@
DYNAMIC_RUNNER_PREFIX = "dynamic:///"
+class JobMappingException( Exception ):
+
+ def __init__( self, failure_message ):
+ self.failure_message = failure_message
+
+
class JobRunnerMapper( object ):
"""
This class is responsible to managing the mapping of jobs
https://bitbucket.org/galaxy/galaxy-central/changeset/6f3b4e88fc21/
changeset: 6f3b4e88fc21
user: jmchilton
date: 2012-11-15 05:23:19
summary: Merge latest galaxy-central to resolve conflict introdcued with 73e05bc.
affected #: 227 files
Diff too large to display.
https://bitbucket.org/galaxy/galaxy-central/changeset/f03725b8272b/
changeset: f03725b8272b
user: natefoo
date: 2012-11-26 19:36:09
summary: Merged in jmchilton/galaxy-central-dynamic-job-runner-enhancements (pull request #82)
affected #: 2 files
diff -r d0e7bd064cf9a2b991f793c1dc7720430906174f -r f03725b8272bd13aa3fc693b9fcb20d2f0f4c28f lib/galaxy/jobs/handler.py
--- a/lib/galaxy/jobs/handler.py
+++ b/lib/galaxy/jobs/handler.py
@@ -17,6 +17,7 @@
# States for running a job. These are NOT the same as data states
JOB_WAIT, JOB_ERROR, JOB_INPUT_ERROR, JOB_INPUT_DELETED, JOB_READY, JOB_DELETED, JOB_ADMIN_DELETED, JOB_USER_OVER_QUOTA = 'wait', 'error', 'input_error', 'input_deleted', 'ready', 'deleted', 'admin_deleted', 'user_over_quota'
+DEFAULT_JOB_PUT_FAILURE_MESSAGE = 'Unable to run job due to a misconfiguration of the Galaxy job running system. Please contact a site administrator.'
class JobHandler( object ):
"""
@@ -41,6 +42,7 @@
a JobRunner.
"""
STOP_SIGNAL = object()
+
def __init__( self, app, dispatcher ):
"""Start the job manager"""
self.app = app
@@ -462,6 +464,15 @@
def put( self, job_wrapper ):
try:
runner_name = self.__get_runner_name( job_wrapper )
+ except Exception, e:
+ failure_message = getattr(e, 'failure_message', DEFAULT_JOB_PUT_FAILURE_MESSAGE )
+ if failure_message == DEFAULT_JOB_PUT_FAILURE_MESSAGE:
+ log.exception( 'Failed to generate job runner name' )
+ else:
+ log.debug( "Intentionally failing job with message (%s)" % failure_message )
+ job_wrapper.fail( failure_message )
+ return
+ try:
if self.app.config.use_tasked_jobs and job_wrapper.tool.parallelism is not None and isinstance(job_wrapper, TaskWrapper):
#DBTODO Refactor
log.debug( "dispatching task %s, of job %d, to %s runner" %( job_wrapper.task_id, job_wrapper.job_id, runner_name ) )
@@ -470,7 +481,7 @@
self.job_runners[runner_name].put( job_wrapper )
except KeyError:
log.error( 'put(): (%s) Invalid job runner: %s' % ( job_wrapper.job_id, runner_name ) )
- job_wrapper.fail( 'Unable to run job due to a misconfiguration of the Galaxy job running system. Please contact a site administrator.' )
+ job_wrapper.fail( DEFAULT_JOB_PUT_FAILURE_MESSAGE )
def stop( self, job ):
"""
@@ -512,7 +523,7 @@
self.job_runners[runner_name].recover( job, job_wrapper )
except KeyError:
log.error( 'recover(): (%s) Invalid job runner: %s' % ( job_wrapper.job_id, runner_name ) )
- job_wrapper.fail( 'Unable to run job due to a misconfiguration of the Galaxy job running system. Please contact a site administrator.' )
+ job_wrapper.fail( DEFAULT_JOB_PUT_FAILURE_MESSAGE )
def shutdown( self ):
for runner in self.job_runners.itervalues():
diff -r d0e7bd064cf9a2b991f793c1dc7720430906174f -r f03725b8272bd13aa3fc693b9fcb20d2f0f4c28f lib/galaxy/jobs/mapper.py
--- a/lib/galaxy/jobs/mapper.py
+++ b/lib/galaxy/jobs/mapper.py
@@ -8,6 +8,12 @@
DYNAMIC_RUNNER_PREFIX = "dynamic:///"
+class JobMappingException( Exception ):
+
+ def __init__( self, failure_message ):
+ self.failure_message = failure_message
+
+
class JobRunnerMapper( object ):
"""
This class is responsible to managing the mapping of jobs
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: Don't allow reviewing empty repositories in the tool shed.
by Bitbucket 26 Nov '12
by Bitbucket 26 Nov '12
26 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/d0e7bd064cf9/
changeset: d0e7bd064cf9
user: greg
date: 2012-11-26 18:09:23
summary: Don't allow reviewing empty repositories in the tool shed.
affected #: 3 files
diff -r 593c0d6c3d447aaa79c61fea2688b6091b44dda5 -r d0e7bd064cf9a2b991f793c1dc7720430906174f templates/webapps/community/repository/manage_repository.mako
--- a/templates/webapps/community/repository/manage_repository.mako
+++ b/templates/webapps/community/repository/manage_repository.mako
@@ -21,7 +21,7 @@
can_undeprecate = trans.user and ( is_admin or repository.user == trans.user ) and is_deprecated
can_reset_all_metadata = not is_deprecated and is_admin and len( repo ) > 0
has_readme = metadata and 'readme' in metadata
- can_review_repository = not is_deprecated and trans.app.security_agent.user_can_review_repositories( trans.user )
+ can_review_repository = not is_new and not is_deprecated and trans.app.security_agent.user_can_review_repositories( trans.user )
reviewing_repository = cntrller and cntrller == 'repository_review'
if can_push:
diff -r 593c0d6c3d447aaa79c61fea2688b6091b44dda5 -r d0e7bd064cf9a2b991f793c1dc7720430906174f templates/webapps/community/repository/view_repository.mako
--- a/templates/webapps/community/repository/view_repository.mako
+++ b/templates/webapps/community/repository/view_repository.mako
@@ -20,7 +20,7 @@
browse_label = 'Browse repository tip files'
has_readme = metadata and 'readme' in metadata
reviewing_repository = cntrller and cntrller == 'repository_review'
- can_review_repository = not is_deprecated and trans.app.security_agent.user_can_review_repositories( trans.user )
+ can_review_repository = not is_new and not is_deprecated and trans.app.security_agent.user_can_review_repositories( trans.user )
%><%!
diff -r 593c0d6c3d447aaa79c61fea2688b6091b44dda5 -r d0e7bd064cf9a2b991f793c1dc7720430906174f templates/webapps/community/repository/view_tool_metadata.mako
--- a/templates/webapps/community/repository/view_tool_metadata.mako
+++ b/templates/webapps/community/repository/view_tool_metadata.mako
@@ -8,6 +8,7 @@
from urllib import quote_plus
is_admin = trans.user_is_admin()
is_new = repository.is_new( trans.app )
+ is_deprecated = repository.deprecated
can_contact_owner = trans.user and trans.user != repository.user
can_push = trans.app.security_agent.can_push( trans.app, trans.user, repository )
can_upload = can_push
@@ -21,7 +22,7 @@
else:
browse_label = 'Browse repository tip files'
has_readme = metadata and 'readme' in metadata
- can_review_repository = trans.app.security_agent.user_can_review_repositories( trans.user )
+ can_review_repository = not is_new and not is_deprecated and trans.app.security_agent.user_can_review_repositories( trans.user )
%><%!
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

26 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/593c0d6c3d44/
changeset: 593c0d6c3d44
user: greg
date: 2012-11-26 17:43:38
summary: Fixes for tool shed functional tests.
affected #: 3 files
diff -r 770580cf77ccd49f2a8c2aab2b559c45a318db51 -r 593c0d6c3d447aaa79c61fea2688b6091b44dda5 test/tool_shed/base/test_db_util.py
--- a/test/tool_shed/base/test_db_util.py
+++ b/test/tool_shed/base/test_db_util.py
@@ -1,9 +1,6 @@
import galaxy.webapps.community.model as model
from galaxy.model.orm import *
from galaxy.webapps.community.model.mapping import context as sa_session
-from base.twilltestcase import *
-from sqlalchemy import desc
-import sys
def delete_obj( obj ):
sa_session.delete( obj )
diff -r 770580cf77ccd49f2a8c2aab2b559c45a318db51 -r 593c0d6c3d447aaa79c61fea2688b6091b44dda5 test/tool_shed/base/twilltestcase.py
--- a/test/tool_shed/base/twilltestcase.py
+++ b/test/tool_shed/base/twilltestcase.py
@@ -1,5 +1,4 @@
from base.twilltestcase import *
-from tool_shed.base.test_db_util import *
class ShedTwillTestCase( TwillTestCase ):
def setUp( self ):
@@ -37,7 +36,7 @@
self.check_for_strings( strings_displayed, strings_not_displayed )
def check_for_valid_tools( self, repository ):
self.manage_repository( repository )
- self.check_page_for_string( '<b>Valid tools</b><i> - click the name to preview the tool' )
+ self.check_page_for_string( 'Valid tools' )
def check_repository_changelog( self, repository, strings_displayed=[], strings_not_displayed=[] ):
url = '/repository/view_changelog?id=%s' % self.security.encode_id( repository.id )
self.visit_url( url )
@@ -149,6 +148,6 @@
self.visit_url( '/upload/upload?repository_id=%s' % self.security.encode_id( repository.id ) )
for key in kwargs:
tc.fv( "1", key, kwargs[ key ] )
- tc.formfile( "1", "file_data", filename )
+ tc.formfile( "1", "file_data", self.get_filename( filename ) )
tc.submit( "upload_button" )
self.check_for_strings( strings_displayed, strings_not_displayed )
diff -r 770580cf77ccd49f2a8c2aab2b559c45a318db51 -r 593c0d6c3d447aaa79c61fea2688b6091b44dda5 test/tool_shed/functional/test_0000_basic_repository_features.py
--- a/test/tool_shed/functional/test_0000_basic_repository_features.py
+++ b/test/tool_shed/functional/test_0000_basic_repository_features.py
@@ -1,8 +1,3 @@
-import tempfile, time, re, tempfile, os, shutil
-import galaxy.webapps.community.model
-from galaxy.util import parse_xml, string_as_bool
-from galaxy.util.shed_util import clean_tool_shed_url
-from galaxy.model.orm import *
from tool_shed.base.twilltestcase import *
from tool_shed.base.test_db_util import *
@@ -19,8 +14,6 @@
repository_name = 'filter'
repository_description = "Galaxy's filter tool"
repository_long_description = "Long description of Galaxy's filter tool"
-files_path = os.path.abspath( os.path.join( "test", "tool_shed", "test_data" ) )
-filter_filename = os.path.join( files_path, "filtering_1.1.0.tar" )
class TestCreateRepository( ShedTwillTestCase ):
@@ -65,8 +58,9 @@
def test_0030_upload_tarball( self ):
"""Upload filtering_1.1.0.tar to the repository"""
repository = get_repository_by_name( repository_name, admin_username )
- self.upload( repository, filter_filename, \
- strings_displayed=[ "The file '%s' has been successfully uploaded to the repository." % filter_filename ], \
+ self.upload( repository,
+ 'filtering_1.1.0.tar',
+ strings_displayed=[ "has been successfully uploaded to the repository." ],
commit_message="Uploaded filtering 1.1.0" )
self.check_for_valid_tools( repository )
latest_repository_metadata = self.get_latest_repository_metadata_for_repository( repository )
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: Change name of first tool shed functional test file.
by Bitbucket 26 Nov '12
by Bitbucket 26 Nov '12
26 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/770580cf77cc/
changeset: 770580cf77cc
user: greg
date: 2012-11-26 17:24:39
summary: Change name of first tool shed functional test file.
affected #: 2 files
diff -r a27a0f7d3ac90dfabd1050e26b9216eeb9ebc46b -r 770580cf77ccd49f2a8c2aab2b559c45a318db51 test/tool_shed/functional/test_0000_basic_repository_features.py
--- /dev/null
+++ b/test/tool_shed/functional/test_0000_basic_repository_features.py
@@ -0,0 +1,94 @@
+import tempfile, time, re, tempfile, os, shutil
+import galaxy.webapps.community.model
+from galaxy.util import parse_xml, string_as_bool
+from galaxy.util.shed_util import clean_tool_shed_url
+from galaxy.model.orm import *
+from tool_shed.base.twilltestcase import *
+from tool_shed.base.test_db_util import *
+
+admin_user = None
+admin_user_private_role = None
+admin_email = 'test(a)bx.psu.edu'
+admin_username = 'admin-user'
+
+regular_user = None
+regular_user_private_role = None
+regular_email = 'test-1(a)bx.psu.edu'
+regular_username = 'user1'
+
+repository_name = 'filter'
+repository_description = "Galaxy's filter tool"
+repository_long_description = "Long description of Galaxy's filter tool"
+files_path = os.path.abspath( os.path.join( "test", "tool_shed", "test_data" ) )
+filter_filename = os.path.join( files_path, "filtering_1.1.0.tar" )
+
+class TestCreateRepository( ShedTwillTestCase ):
+
+ def test_0000_initiate_users( self ):
+ """Create necessary users and login as an admin user."""
+ self.login( email=regular_email, username=regular_username )
+ regular_user = get_user( regular_email )
+ assert regular_user is not None, 'Problem retrieving user with email %s from the database' % regular_email
+ regular_user_private_role = get_private_role( regular_user )
+ self.logout()
+ self.login( email=admin_email, username=admin_username )
+ admin_user = get_user( admin_email )
+ assert admin_user is not None, 'Problem retrieving user with email %s from the database' % admin_email
+ admin_user_private_role = get_private_role( admin_user )
+ def test_0005_create_categories( self ):
+ """Create a category"""
+ self.create_category( 'Text Manipulation', 'Tools for manipulating text' )
+ self.create_category( 'Text Analysis', 'Tools for analyzing text' )
+ def test_0010_create_repository( self ):
+ """Create a repository"""
+ strings_displayed = [ '<div class="toolFormTitle">Repository %s</div>' % "'%s'" % repository_name, \
+ 'Repository %s has been created' % "'%s'" % repository_name ]
+ self.create_repository( repository_name, repository_description, \
+ repository_long_description=repository_long_description, \
+ categories=[ 'Text Manipulation' ], \
+ strings_displayed=strings_displayed )
+ def test_0015_edit_repository( self ):
+ """Edit the repository name, description, and long description"""
+ repository = get_repository_by_name( repository_name, admin_username )
+ new_name = "renamed_filter"
+ new_description = "Edited filter tool"
+ new_long_description = "Edited long description"
+ self.edit_repository_information( repository, repo_name=new_name, description=new_description, long_description=new_long_description )
+ def test_0020_change_repository_category( self ):
+ """Change the category of a repository"""
+ repository = get_repository_by_name( repository_name, admin_username )
+ self.edit_repository_categories( repository, categories_to_add=[ "Text Analysis" ], categories_to_remove=[ "Text Manipulation" ] )
+# def test_0025_grant_write_access( self ):
+# '''Grant write access to another user'''
+# repository = get_repository_by_name( repository_name, admin_username )
+# self.grant_write_access( repository, usernames=[ regular_username ] )
+ def test_0030_upload_tarball( self ):
+ """Upload filtering_1.1.0.tar to the repository"""
+ repository = get_repository_by_name( repository_name, admin_username )
+ self.upload( repository, filter_filename, \
+ strings_displayed=[ "The file '%s' has been successfully uploaded to the repository." % filter_filename ], \
+ commit_message="Uploaded filtering 1.1.0" )
+ self.check_for_valid_tools( repository )
+ latest_repository_metadata = self.get_latest_repository_metadata_for_repository( repository )
+ changeset_revision = latest_repository_metadata.changeset_revision
+ self.check_repository_changelog( repository, strings_displayed=[ 'Repository metadata is associated with this change set.' ] )
+ self.set_repository_malicious( repository, strings_displayed=[ 'The repository tip has been defined as malicious.' ] )
+ self.unset_repository_malicious( repository, strings_displayed=[ 'The repository tip has been defined as <b>not</b> malicious.' ] )
+ self.load_display_tool_page( repository, tool_xml_filename='filtering.xml', \
+ changeset_revision=changeset_revision, \
+ strings_displayed=[ 'Filter (version 1.1.0)', "c1=='chr1'" ], \
+ strings_not_displayed=[] )
+ tool = latest_repository_metadata.metadata[ 'tools' ][0]
+ metadata_strings_displayed = [ tool[ 'guid' ], tool[ 'version' ], tool[ 'id' ], tool[ 'name' ], tool[ 'description' ], changeset_revision ]
+ self.check_for_tool_metadata( repository, changeset_revision, 'Filter1', strings_displayed=metadata_strings_displayed )
+ def test_0035_repository_browse_page( self ):
+ '''Visit the repository browse page'''
+ repository = get_repository_by_name( repository_name, admin_username )
+ self.browse_repository( repository, strings_displayed=[ 'Browse %s revision' % repository.name, '(repository tip)' ] )
+ def test_0040_visit_clone_url_via_hgweb( self ):
+ '''Visit the repository clone URL via hgweb'''
+ repository = get_repository_by_name( repository_name, admin_username )
+ latest_changeset_revision = self.get_latest_repository_metadata_for_repository( repository )
+ self.display_repository_clone_page( admin_username, \
+ repository_name, \
+ strings_displayed=[ 'Uploaded filtering 1.1.0', latest_changeset_revision.changeset_revision ] )
diff -r a27a0f7d3ac90dfabd1050e26b9216eeb9ebc46b -r 770580cf77ccd49f2a8c2aab2b559c45a318db51 test/tool_shed/functional/test_0000_create_repository.py
--- a/test/tool_shed/functional/test_0000_create_repository.py
+++ /dev/null
@@ -1,94 +0,0 @@
-import tempfile, time, re, tempfile, os, shutil
-import galaxy.webapps.community.model
-from galaxy.util import parse_xml, string_as_bool
-from galaxy.util.shed_util import clean_tool_shed_url
-from galaxy.model.orm import *
-from tool_shed.base.twilltestcase import *
-from tool_shed.base.test_db_util import *
-
-admin_user = None
-admin_user_private_role = None
-admin_email = 'test(a)bx.psu.edu'
-admin_username = 'admin-user'
-
-regular_user = None
-regular_user_private_role = None
-regular_email = 'test-1(a)bx.psu.edu'
-regular_username = 'user1'
-
-repository_name = 'filter'
-repository_description = "Galaxy's filter tool"
-repository_long_description = "Long description of Galaxy's filter tool"
-files_path = os.path.abspath( os.path.join( "test", "tool_shed", "test_data" ) )
-filter_filename = os.path.join( files_path, "filtering_1.1.0.tar" )
-
-class TestCreateRepository( ShedTwillTestCase ):
-
- def test_0000_initiate_users( self ):
- """Create necessary users and login as an admin user."""
- self.login( email=regular_email, username=regular_username )
- regular_user = get_user( regular_email )
- assert regular_user is not None, 'Problem retrieving user with email %s from the database' % regular_email
- regular_user_private_role = get_private_role( regular_user )
- self.logout()
- self.login( email=admin_email, username=admin_username )
- admin_user = get_user( admin_email )
- assert admin_user is not None, 'Problem retrieving user with email %s from the database' % admin_email
- admin_user_private_role = get_private_role( admin_user )
- def test_0005_create_categories( self ):
- """Create a category"""
- self.create_category( 'Text Manipulation', 'Tools for manipulating text' )
- self.create_category( 'Text Analysis', 'Tools for analyzing text' )
- def test_0010_create_repository( self ):
- """Create a repository"""
- strings_displayed = [ '<div class="toolFormTitle">Repository %s</div>' % "'%s'" % repository_name, \
- 'Repository %s has been created' % "'%s'" % repository_name ]
- self.create_repository( repository_name, repository_description, \
- repository_long_description=repository_long_description, \
- categories=[ 'Text Manipulation' ], \
- strings_displayed=strings_displayed )
- def test_0015_edit_repository( self ):
- """Edit the repository name, description, and long description"""
- repository = get_repository_by_name( repository_name, admin_username )
- new_name = "renamed_filter"
- new_description = "Edited filter tool"
- new_long_description = "Edited long description"
- self.edit_repository_information( repository, repo_name=new_name, description=new_description, long_description=new_long_description )
- def test_0020_change_repository_category( self ):
- """Change the category of a repository"""
- repository = get_repository_by_name( repository_name, admin_username )
- self.edit_repository_categories( repository, categories_to_add=[ "Text Analysis" ], categories_to_remove=[ "Text Manipulation" ] )
-# def test_0025_grant_write_access( self ):
-# '''Grant write access to another user'''
-# repository = get_repository_by_name( repository_name, admin_username )
-# self.grant_write_access( repository, usernames=[ regular_username ] )
- def test_0030_upload_tarball( self ):
- """Upload filtering_1.1.0.tar to the repository"""
- repository = get_repository_by_name( repository_name, admin_username )
- self.upload( repository, filter_filename, \
- strings_displayed=[ "The file '%s' has been successfully uploaded to the repository." % filter_filename ], \
- commit_message="Uploaded filtering 1.1.0" )
- self.check_for_valid_tools( repository )
- latest_repository_metadata = self.get_latest_repository_metadata_for_repository( repository )
- changeset_revision = latest_repository_metadata.changeset_revision
- self.check_repository_changelog( repository, strings_displayed=[ 'Repository metadata is associated with this change set.' ] )
- self.set_repository_malicious( repository, strings_displayed=[ 'The repository tip has been defined as malicious.' ] )
- self.unset_repository_malicious( repository, strings_displayed=[ 'The repository tip has been defined as <b>not</b> malicious.' ] )
- self.load_display_tool_page( repository, tool_xml_filename='filtering.xml', \
- changeset_revision=changeset_revision, \
- strings_displayed=[ 'Filter (version 1.1.0)', "c1=='chr1'" ], \
- strings_not_displayed=[] )
- tool = latest_repository_metadata.metadata[ 'tools' ][0]
- metadata_strings_displayed = [ tool[ 'guid' ], tool[ 'version' ], tool[ 'id' ], tool[ 'name' ], tool[ 'description' ], changeset_revision ]
- self.check_for_tool_metadata( repository, changeset_revision, 'Filter1', strings_displayed=metadata_strings_displayed )
- def test_0035_repository_browse_page( self ):
- '''Visit the repository browse page'''
- repository = get_repository_by_name( repository_name, admin_username )
- self.browse_repository( repository, strings_displayed=[ 'Browse %s revision' % repository.name, '(repository tip)' ] )
- def test_0040_visit_clone_url_via_hgweb( self ):
- '''Visit the repository clone URL via hgweb'''
- repository = get_repository_by_name( repository_name, admin_username )
- latest_changeset_revision = self.get_latest_repository_metadata_for_repository( repository )
- self.display_repository_clone_page( admin_username, \
- repository_name, \
- strings_displayed=[ 'Uploaded filtering 1.1.0', latest_changeset_revision.changeset_revision ] )
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: Style fix for rendering tool dependency information when installing tool shed repositories.
by Bitbucket 26 Nov '12
by Bitbucket 26 Nov '12
26 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/a27a0f7d3ac9/
changeset: a27a0f7d3ac9
user: greg
date: 2012-11-26 16:44:32
summary: Style fix for rendering tool dependency information when installing tool shed repositories.
affected #: 2 files
diff -r a627f341d01ae4a9741164981ff2e5300353354f -r a27a0f7d3ac90dfabd1050e26b9216eeb9ebc46b lib/galaxy/util/shed_util.py
--- a/lib/galaxy/util/shed_util.py
+++ b/lib/galaxy/util/shed_util.py
@@ -484,18 +484,6 @@
if ctx_file_name == config_file:
return get_named_tmpfile_from_ctx( changeset_ctx, ctx_file, dir )
return None
-def get_headers( fname, sep, count=60, is_multi_byte=False ):
- """Returns a list with the first 'count' lines split by 'sep'."""
- headers = []
- for idx, line in enumerate( file( fname ) ):
- line = line.rstrip( '\n\r' )
- if is_multi_byte:
- line = unicode( line, 'utf-8' )
- sep = sep.encode( 'utf-8' )
- headers.append( line.split( sep ) )
- if idx == count:
- break
- return headers
def get_converter_and_display_paths( registration_elem, relative_install_dir ):
"""Find the relative path to data type converters and display applications included in installed tool shed repositories."""
converter_path = None
@@ -544,6 +532,18 @@
ctx_rev = response.read()
response.close()
return ctx_rev
+def get_headers( fname, sep, count=60, is_multi_byte=False ):
+ """Returns a list with the first 'count' lines split by 'sep'."""
+ headers = []
+ for idx, line in enumerate( file( fname ) ):
+ line = line.rstrip( '\n\r' )
+ if is_multi_byte:
+ line = unicode( line, 'utf-8' )
+ sep = sep.encode( 'utf-8' )
+ headers.append( line.split( sep ) )
+ if idx == count:
+ break
+ return headers
def get_installed_tool_shed_repository( trans, id ):
"""Get a repository on the Galaxy side from the database via id"""
return trans.sa_session.query( trans.model.ToolShedRepository ).get( trans.security.decode_id( id ) )
diff -r a627f341d01ae4a9741164981ff2e5300353354f -r a27a0f7d3ac90dfabd1050e26b9216eeb9ebc46b templates/admin/tool_shed_repository/common.mako
--- a/templates/admin/tool_shed_repository/common.mako
+++ b/templates/admin/tool_shed_repository/common.mako
@@ -90,8 +90,18 @@
</div></div><div style="clear: both"></div>
- <div class="form-row">
- <table class="grid">
+ <div class="form-row">
+ <style type="text/css">
+ #dependency_table{ table-layout:fixed;
+ width:100%;
+ overflow-wrap:normal;
+ overflow:hidden;
+ border:0px;
+ word-break:keep-all;
+ word-wrap:break-word;
+ line-break:strict; }
+ </style>
+ <table class="grid" id="dependency_table"><tr><td colspan="4" bgcolor="#D8D8D8"><b>Tool dependencies</b></td></tr><%
env_settings_heaader_row_displayed = False
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: inithello: Removed lastz and bowtie from tool_conf.xml.main
by Bitbucket 26 Nov '12
by Bitbucket 26 Nov '12
26 Nov '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/a627f341d01a/
changeset: a627f341d01a
user: inithello
date: 2012-11-26 16:20:38
summary: Removed lastz and bowtie from tool_conf.xml.main
affected #: 1 file
diff -r 87828175dfe7475aa846f383a5038e5ca4f1e312 -r a627f341d01ae4a9741164981ff2e5300353354f tool_conf.xml.main
--- a/tool_conf.xml.main
+++ b/tool_conf.xml.main
@@ -259,13 +259,10 @@
</section><section name="NGS: Mapping" id="ngs_mapping"><label text="Illumina" id="illumina"/>
- <tool file="sr_mapping/bowtie_wrapper.xml" /><label text="Roche-454" id="roche_454"/>
- <tool file="sr_mapping/lastz_wrapper.xml" /><tool file="metag_tools/megablast_wrapper.xml" /><tool file="metag_tools/megablast_xml_parser.xml" /><label text="AB-SOLiD" id="ab_solid"/>
- <tool file="sr_mapping/bowtie_color_wrapper.xml" /></section><section name="NGS: SAM Tools" id="samtools"><tool file="samtools/sam_bitwise_flag_filter.xml" />
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