galaxy-commits
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- 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: dannon: Update to PR285, use datetime.isoformat() instead of str(). Left in stringifying bit (commented) for future reference.
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/968389346b34/
Changeset: 968389346b34
User: dannon
Date: 2014-01-07 20:41:53
Summary: Update to PR285, use datetime.isoformat() instead of str(). Left in stringifying bit (commented) for future reference.
Affected #: 1 file
diff -r d05b1d649290a8563558e8721f4a1ca8336e8180 -r 968389346b3491bf1a90cf359ff258075816b1e8 lib/galaxy/model/item_attrs.py
--- a/lib/galaxy/model/item_attrs.py
+++ b/lib/galaxy/model/item_attrs.py
@@ -184,9 +184,13 @@
except:
if key in value_mapper:
return value_mapper.get( key )( item )
- #If the item is of a class that needs to be 'stringified' before being put into a JSON data structure
- if type(item) in [datetime.datetime]:
- return str(item)
+ if type(item) == datetime.datetime:
+ return item.isoformat()
+ # Leaving this for future reference, though we may want a more
+ # generic way to handle special type mappings going forward.
+ # If the item is of a class that needs to be 'stringified' before being put into a JSON data structure
+ # elif type(item) in []:
+ # return str(item)
return item
# Create dict to represent item.
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
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/8388f17e522a/
Changeset: 8388f17e522a
Branch: search
User: Kyle Ellrott
Date: 2014-01-02 20:37:37
Summary: Default merge
Affected #: 467 files
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -60,6 +60,7 @@
job_conf.xml
data_manager_conf.xml
shed_data_manager_conf.xml
+object_store_conf.xml
config/*
static/welcome.html.*
static/welcome.html
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 config/plugins/visualizations/scatterplot/Gruntfile.js
--- a/config/plugins/visualizations/scatterplot/Gruntfile.js
+++ b/config/plugins/visualizations/scatterplot/Gruntfile.js
@@ -9,7 +9,7 @@
// compile all hb templates into a single file in the build dir
compile: {
options: {
- namespace: 'Templates',
+ namespace: 'scatterplot',
processName : function( filepath ){
return filepath.match( /\w*\.handlebars/ )[0].replace( '.handlebars', '' );
}
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 config/plugins/visualizations/scatterplot/src/scatterplot-config-editor.js
--- a/config/plugins/visualizations/scatterplot/src/scatterplot-config-editor.js
+++ b/config/plugins/visualizations/scatterplot/src/scatterplot-config-editor.js
@@ -37,14 +37,19 @@
/** initialize requires a configuration Object containing a dataset Object */
initialize : function( attributes ){
//console.log( this + '.initialize, attributes:', attributes );
- if( !attributes || !attributes.config || !attributes.config.dataset ){
+ if( !attributes || !attributes.config || !attributes.dataset ){
throw new Error( "ScatterplotView requires a configuration and dataset" );
}
- this.dataset = attributes.config.dataset;
+ //console.log( 'config:', attributes.config );
+
+ this.dataset = attributes.dataset;
//console.log( 'dataset:', this.dataset );
+//TODO: ScatterplotView -> ScatterplotDisplay, this.plotView -> this.display
this.plotView = new ScatterplotView({
+ dataset : attributes.dataset,
config : attributes.config
+//TODO: if data
});
},
@@ -197,8 +202,8 @@
// parse the column values for both indeces (for the data fetch) and names (for the chart)
var $dataControls = this.$el.find( '#data-control' );
var settings = {
- xColumn : $dataControls.find( '[name="xColumn"]' ).val(),
- yColumn : $dataControls.find( '[name="yColumn"]' ).val()
+ xColumn : Number( $dataControls.find( '[name="xColumn"]' ).val() ),
+ yColumn : Number( $dataControls.find( '[name="yColumn"]' ).val() )
};
if( $dataControls.find( '#include-id-checkbox' ).prop( 'checked' ) ){
settings.idColumn = $dataControls.find( '[name="idColumn"]' ).val();
@@ -229,9 +234,9 @@
});
ScatterplotConfigEditor.templates = {
- mainLayout : Templates.editor,
- dataControl : Templates.datacontrol,
- chartControl : Templates.chartcontrol
+ mainLayout : scatterplot.editor,
+ dataControl : scatterplot.datacontrol,
+ chartControl : scatterplot.chartcontrol
};
//==============================================================================
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 config/plugins/visualizations/scatterplot/src/scatterplot-display.js
--- a/config/plugins/visualizations/scatterplot/src/scatterplot-display.js
+++ b/config/plugins/visualizations/scatterplot/src/scatterplot-display.js
@@ -10,14 +10,10 @@
//TODO: should be a view on visualization(revision) model
defaults : {
- dataset : {
- },
metadata : {
dataLines : undefined
},
- ajaxFn : null,
-
pagination : {
currPage : 0,
perPage : 3000
@@ -48,6 +44,7 @@
initialize : function( attributes ){
this.config = _.extend( _.clone( this.defaults ), attributes.config || {});
+ this.dataset = attributes.dataset;
//console.debug( this + '.config:', this.config );
},
@@ -65,7 +62,7 @@
//console.debug( 'currPage', this.config.pagination.currPage );
var view = this;
//TODO: very tied to datasets - should be generalized eventually
- xhr = jQuery.getJSON( '/api/datasets/' + this.config.dataset.id, {
+ xhr = jQuery.getJSON( '/api/datasets/' + this.dataset.id, {
data_type : 'raw_data',
provider : 'dataset-column',
limit : this.config.pagination.perPage,
@@ -151,7 +148,7 @@
},
renderLineInfo : function( data ){
- var totalLines = this.config.dataset.metadata_data_lines || 'an unknown number of',
+ var totalLines = this.dataset.metadata_data_lines || 'an unknown number of',
lineStart = ( this.config.pagination.currPage * this.config.pagination.perPage ),
lineEnd = lineStart + data.length;
return $( '<p/>' ).addClass( 'scatterplot-data-info' )
@@ -168,9 +165,9 @@
}
//TODO: cache numPages/numLines in config
var view = this,
- dataLines = this.config.dataset.metadata_data_lines,
+ dataLines = this.dataset.metadata_data_lines,
numPages = ( dataLines )?( Math.ceil( dataLines / this.config.pagination.perPage ) ):( undefined );
- //console.debug( 'data:', this.config.dataset.metadata_data_lines, 'numPages:', numPages );
+ //console.debug( 'data:', this.dataset.metadata_data_lines, 'numPages:', numPages );
// prev next buttons
var $prev = makePage$Li( 'Prev' ).click( function(){
@@ -207,9 +204,9 @@
}
//TODO: cache numPages/numLines in config
var view = this,
- dataLines = this.config.dataset.metadata_data_lines,
+ dataLines = this.dataset.metadata_data_lines,
numPages = ( dataLines )?( Math.ceil( dataLines / this.config.pagination.perPage ) ):( undefined );
- //console.debug( 'data:', this.config.dataset.metadata_data_lines, 'numPages:', numPages );
+ //console.debug( 'data:', this.dataset.metadata_data_lines, 'numPages:', numPages );
// page numbers (as separate control)
//var $paginationContainer = $( '<div/>' ).addClass( 'pagination-container' ),
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 config/plugins/visualizations/scatterplot/src/visualization-templates.html
--- a/config/plugins/visualizations/scatterplot/src/visualization-templates.html
+++ /dev/null
@@ -1,197 +0,0 @@
-<script type="text/template" class="template-visualization" id="template-visualization-scatterplotControlForm">
-{{! main layout }}
-
-<h1>WHAAAAA?</h1>
-<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 title="Use this tab to change which data are used"
- href="#data-control" data-toggle="tab">Data Controls</a>
- </li>
- <li>
- <a title="Use this tab to change how the chart is drawn"
- href="#chart-control" data-toggle="tab" >Chart Controls</a>
- </li>
- <li>
- <a title="This tab will display overall statistics for your data"
- href="#stats-display" data-toggle="tab">Statistics</a>
- </li>
- <li>
- <a title="This tab will display the chart"
- href="#chart-display" data-toggle="tab">Chart</a>
-
- <div id="loading-indicator" style="display: none;">
- <img class="loading-img" src="{{loadingIndicatorImagePath}}" />
- <span class="loading-message">{{message}}</span>
- </div>
- </li>
- </ul>
-
- {{! 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">
- <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>
- </div>
-
- {{! ---------------------------- tab for actual chart }}
- <div id="chart-display" class="tab-pane">
- <svg width="{{width}}" height="{{height}}"></svg>
- </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>
- <select name="X" id="X-select">
- {{#each numericColumns}}
- <option value="{{index}}">{{name}}</option>
- {{/each}}
- </select>
- </div>
- <div class="column-select">
- <label for="Y-select">Data column for Y: </label>
- <select name="Y" id="Y-select">
- {{#each numericColumns}}
- <option value="{{index}}">{{name}}</option>
- {{/each}}
- </select>
- </div>
-
- {{! optional id column }}
- <div id="include-id">
- <label for="include-id-checkbox">Include a third column as data point IDs?</label>
- <input type="checkbox" name="include-id" id="include-id-checkbox" />
- <p class="help-text-small">
- These will be displayed (along with the x and y values) when you hover over
- a data point.
- </p>
- </div>
- <div class="column-select" style="display: none">
- <label for="ID-select">Data column for IDs: </label>
- <select name="ID" id="ID-select">
- {{#each allColumns}}
- <option value="{{index}}">{{name}}</option>
- {{/each}}
- </select>
- </div>
-
- {{! if we're using generic column selection names ('column 1') - allow the user to use the first line }}
- <div id="first-line-header" style="display: none;">
- <p>Possible headers: {{ possibleHeaders }}
- </p>
- <label for="first-line-header-checkbox">Use the above as column headers?</label>
- <input type="checkbox" name="include-id" id="first-line-header-checkbox"
- {{#if usePossibleHeaders }}checked="true"{{/if}}/>
- <p class="help-text-small">
- It looks like Galaxy couldn't get proper column headers for this data.
- Would you like to use the column headers above as column names to select columns?
- </p>
- </div>
-
- <input id="render-button" type="button" value="Draw" />
- <div class="clear"></div>
-</script>
-
-<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>
- <div class="slider"></div>
- <p class="form-help help-text-small">
- Size of the graphic representation of each data point
- </p>
- </div>
-
- <div id="animDuration" class="form-input checkbox-input">
- <label for="animate-chart">Animate chart transitions?: </label>
- <input type="checkbox" id="animate-chart"
- class="checkbox control"{{#if animDuration}} checked="true"{{/if}} />
- <p class="form-help help-text-small">
- Uncheck this to disable the animations used on the chart
- </p>
- </div>
-
- <div id="width" class="form-input numeric-slider-input">
- <label for="width">Chart width: </label>
- <div class="slider-output">{{width}}</div>
- <div class="slider"></div>
- <p class="form-help help-text-small">
- (not including chart margins and axes)
- </p>
- </div>
-
- <div id="height" class="form-input numeric-slider-input">
- <label for="height">Chart height: </label>
- <div class="slider-output">{{height}}</div>
- <div class="slider"></div>
- <p class="form-help help-text-small">
- (not including chart margins and axes)
- </p>
- </div>
-
- <div id="X-axis-label"class="text-input form-input">
- <label for="X-axis-label">Re-label the X axis: </label>
- <input type="text" name="X-axis-label" id="X-axis-label" value="{{xLabel}}" />
- <p class="form-help help-text-small"></p>
- </div>
-
- <div id="Y-axis-label" class="text-input form-input">
- <label for="Y-axis-label">Re-label the Y axis: </label>
- <input type="text" name="Y-axis-label" id="Y-axis-label" value="{{yLabel}}" />
- <p class="form-help help-text-small"></p>
- </div>
-
- <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 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 config/plugins/visualizations/scatterplot/static/scatterplot-edit.js
--- a/config/plugins/visualizations/scatterplot/static/scatterplot-edit.js
+++ b/config/plugins/visualizations/scatterplot/static/scatterplot-edit.js
@@ -1,1 +1,1 @@
-function scatterplot(a,b,c){function d(){var a={v:{},h:{}};return a.v.lines=p.selectAll("line.v-grid-line").data(m.x.ticks(q.x.fn.ticks()[0])),a.v.lines.enter().append("svg:line").classed("grid-line v-grid-line",!0),a.v.lines.attr("x1",m.x).attr("x2",m.x).attr("y1",0).attr("y2",b.height),a.v.lines.exit().remove(),a.h.lines=p.selectAll("line.h-grid-line").data(m.y.ticks(q.y.fn.ticks()[0])),a.h.lines.enter().append("svg:line").classed("grid-line h-grid-line",!0),a.h.lines.attr("x1",0).attr("x2",b.width).attr("y1",m.y).attr("y2",m.y),a.h.lines.exit().remove(),a}function e(){return t.attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",function(a,b){return m.y(k(a,b))}).style("display","block").filter(function(){var a=d3.select(this).attr("cx"),c=d3.select(this).attr("cy");return 0>a||a>b.width?!0:0>c||c>b.height?!0:!1}).style("display","none")}function f(){q.redraw(),e(),s=d(),$(".chart-info-box").remove(),$(o.node()).trigger("zoom.scatterplot",[])}function g(a,c,d){return c+=8,$(['<div class="chart-info-box" style="position: absolute">',b.idColumn?"<div>"+d[b.idColumn]+"</div>":"","<div>",j(d),"</div>","<div>",k(d),"</div>","</div>"].join("")).css({top:a,left:c,"z-index":2})}var h=function(a,b){return"translate("+a+","+b+")"},i=function(a,b,c){return"rotate("+a+","+b+","+c+")"},j=function(a){return a[b.xColumn]},k=function(a){return a[b.yColumn]},l={x:{extent:d3.extent(c,j)},y:{extent:d3.extent(c,k)}},m={x:d3.scale.linear().domain(l.x.extent).range([0,b.width]),y:d3.scale.linear().domain(l.y.extent).range([b.height,0])},n=d3.behavior.zoom().x(m.x).y(m.y).scaleExtent([1,10]),o=d3.select(a).attr("class","scatterplot").attr("width","100%").attr("height",b.height+(b.margin.top+b.margin.bottom)),p=o.append("g").attr("class","content").attr("transform",h(b.margin.left,b.margin.top)).call(n);p.append("rect").attr("class","zoom-rect").attr("width",b.width).attr("height",b.height).style("fill","transparent");var q={x:{},y:{}};q.x.fn=d3.svg.axis().orient("bottom").scale(m.x).ticks(b.x.ticks).tickFormat(d3.format("s")),q.y.fn=d3.svg.axis().orient("left").scale(m.y).ticks(b.y.ticks).tickFormat(d3.format("s")),q.x.g=p.append("g").attr("class","x axis").attr("transform",h(0,b.height)).call(q.x.fn),q.y.g=p.append("g").attr("class","y axis").call(q.y.fn);var r=4;q.x.label=o.append("text").attr("class","axis-label").text(b.x.label).attr("text-anchor","middle").attr("dominant-baseline","text-after-edge").attr("x",b.width/2+b.margin.left).attr("y",b.height+b.margin.bottom+b.margin.top-r),q.y.label=o.append("text").attr("class","axis-label").text(b.y.label).attr("text-anchor","middle").attr("dominant-baseline","text-before-edge").attr("x",r).attr("y",b.height/2).attr("transform",i(-90,r,b.height/2)),q.redraw=function(){o.select(".x.axis").call(q.x.fn),o.select(".y.axis").call(q.y.fn)};var s=d(),t=p.selectAll(".glyph").data(c).enter().append("svg:circle").classed("glyph",!0).attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",b.height).attr("r",0);t.transition().duration(b.animDuration).attr("cy",function(a,b){return m.y(k(a,b))}).attr("r",b.datapointSize),n.on("zoom",f),t.on("mouseover",function(a,c){var d=d3.select(this);d.style("fill","red").style("fill-opacity",1),p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")-b.datapointSize).attr("y1",d.attr("cy")).attr("x2",0).attr("y2",d.attr("cy")).classed("hoverline",!0),d.attr("cy")<b.height&&p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")).attr("y1",+d.attr("cy")+b.datapointSize).attr("x2",d.attr("cx")).attr("y2",b.height).classed("hoverline",!0);var e=this.getBoundingClientRect();$("body").append(g(e.top,e.right,a)),$(o.node()).trigger("mouseover-datapoint.scatterplot",[this,a,c])}),t.on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",.2),p.selectAll(".hoverline").remove(),$(".chart-info-box").remove()})}this.Templates=this.Templates||{},this.Templates.chartcontrol=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f,g="",h="function",i=this.escapeExpression;return g+='<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 data-config-key="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">',(f=c.datapointSize)?f=f.call(b,{hash:{},data:e}):(f=b.datapointSize,f=typeof f===h?f.apply(b):f),g+=i(f)+'</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 data-config-key="width" class="form-input numeric-slider-input">\n <label for="width">Chart width: </label>\n <div class="slider-output">',(f=c.width)?f=f.call(b,{hash:{},data:e}):(f=b.width,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n</div>\n\n<div data-config-key="height" class="form-input numeric-slider-input">\n <label for="height">Chart height: </label>\n <div class="slider-output">',(f=c.height)?f=f.call(b,{hash:{},data:e}):(f=b.height,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n</div>\n\n<div data-config-key="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="'+i((f=b.x,f=null==f||f===!1?f:f.label,typeof f===h?f.apply(b):f))+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<div data-config-key="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="'+i((f=b.y,f=null==f||f===!1?f:f.label,typeof f===h?f.apply(b):f))+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.Templates.datacontrol=Handlebars.template(function(a,b,c,d,e){function f(a,b){var d,e="";return e+='\n <option value="',(d=c.index)?d=d.call(a,{hash:{},data:b}):(d=a.index,d=typeof d===j?d.apply(a):d),e+=k(d)+'">',(d=c.name)?d=d.call(a,{hash:{},data:b}):(d=a.name,d=typeof d===j?d.apply(a):d),e+=k(d)+"</option>\n "}function g(){return'checked="true"'}this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var h,i="",j="function",k=this.escapeExpression,l=this;return 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\n<div class="column-select">\n <label>Data column for X: </label>\n <select name="xColumn">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n<div class="column-select">\n <label>Data column for Y: </label>\n <select name="yColumn">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n\n\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="idColumn">\n ',h=c.each.call(b,b.allColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n\n\n<div id="first-line-header" style="display: none;">\n <p>Possible headers: ',(h=c.possibleHeaders)?h=h.call(b,{hash:{},data:e}):(h=b.possibleHeaders,h=typeof h===j?h.apply(b):h),i+=k(h)+'\n </p>\n <label for="first-line-header-checkbox">Use the above as column headers?</label>\n <input type="checkbox" name="include-id" id="first-line-header-checkbox"\n ',h=c["if"].call(b,b.usePossibleHeaders,{hash:{},inverse:l.noop,fn:l.program(3,g,e),data:e}),(h||0===h)&&(i+=h),i+='/>\n <p class="help-text-small">\n It looks like Galaxy couldn\'t get proper column headers for this data.\n Would you like to use the column headers above as column names to select columns?\n </p>\n</div>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.Templates.editor=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f="";return f+='<div class="scatterplot-editor tabbable tabs-left">\n \n <ul class="nav nav-tabs">\n \n <li class="active">\n <a title="Use this tab to change which data are used"\n href="#data-control" data-toggle="tab">Data Controls</a>\n </li>\n <li>\n <a title="Use this tab to change how the chart is drawn"\n href="#chart-control" data-toggle="tab" >Chart Controls</a>\n </li>\n \n <li class="disabled">\n <a title="This tab will display the chart"\n href="#chart-display" data-toggle="tab">Chart</a>\n </li>\n </ul>\n\n \n <div class="tab-content">\n \n <div id="data-control" class="scatterplot-config-control tab-pane active">\n \n </div>\n \n \n <div id="chart-control" class="scatterplot-config-control tab-pane">\n \n </div>\n\n \n <div id="chart-display" class="scatterplot-display tab-pane"></div>\n\n </div>\n</div>\n'});var ScatterplotConfigEditor=BaseView.extend(LoggableMixin).extend({className:"scatterplot-control-form",initialize:function(a){if(!a||!a.config||!a.config.dataset)throw new Error("ScatterplotView requires a configuration and dataset");this.dataset=a.config.dataset,this.plotView=new ScatterplotView({config:a.config})},render:function(){return this.$el.append(ScatterplotConfigEditor.templates.mainLayout({})),this.$el.find("#data-control").append(this._render_dataControl()),this._render_chartControls(this.$el.find("#chart-control")),this._render_chartDisplay(),this.$el.find("[title]").tooltip(),this},_render_dataControl:function(){var a=this.dataset,b=_.map(a.metadata_column_types,function(b,c){var d={index:c,type:b,name:"column "+(c+1)};return a.metadata_column_names&&a.metadata_column_names[c]&&(d.name=a.metadata_column_names[c]),d}),c=_.filter(b,function(a){return"int"===a.type||"float"===a.type});2>c&&(c=b);var d=this.$el.find(".tab-pane#data-control");return d.html(ScatterplotConfigEditor.templates.dataControl({allColumns:b,numericColumns:c})),d.find('[name="xColumn"]').val(this.plotView.config.xColumn||c[0].index),d.find('[name="yColumn"]').val(this.plotView.config.yColumn||c[1].index),void 0!==this.plotView.config.idColumn&&(d.find("#include-id-checkbox").prop("checked",!0).trigger("change"),d.find('select[name="idColumn"]').val(this.plotView.config.idColumn)),d},_render_chartControls:function(a){function b(){var a=$(this);a.siblings(".slider-output").text(a.slider("value"))}a.html(ScatterplotConfigEditor.templates.chartControl(this.plotView.config));var c=this,d={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};return a.find(".numeric-slider-input").each(function(){var a=$(this),e=a.attr("data-config-key"),f=_.extend(d[e],{value:c.plotView.config[e],change:b,slide:b});a.find(".slider").slider(f)}),this.dataset.metadata_column_names,a},_render_chartDisplay:function(){var a=this.$el.find(".tab-pane#chart-display");return this.plotView.setElement(a),this.plotView.render(),a},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","click #data-control .render-button":"renderChart","click #chart-control .render-button":"renderChart"},toggleThirdColumnSelector:function(){this.$el.find('select[name="idColumn"]').parent().toggle()},renderChart:function(){this.$el.find(".nav li.disabled").removeClass("disabled"),this.updateConfigWithDataSettings(),this.updateConfigWithChartSettings(),this.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show"),this.plotView.fetchData()},updateConfigWithDataSettings:function(){var a=this.$el.find("#data-control"),b={xColumn:a.find('[name="xColumn"]').val(),yColumn:a.find('[name="yColumn"]').val()};return a.find("#include-id-checkbox").prop("checked")&&(b.idColumn=a.find('[name="idColumn"]').val()),_.extend(this.plotView.config,b)},updateConfigWithChartSettings:function(){var a=this.plotView,b=this.$el.find("#chart-control");return["datapointSize","width","height"].forEach(function(c){a.config[c]=b.find('.numeric-slider-input[data-config-key="'+c+'"]').find(".slider").slider("value")}),a.config.x.label=b.find('input[name="X-axis-label"]').val(),a.config.y.label=b.find('input[name="Y-axis-label"]').val(),a.config},toString:function(){return"ScatterplotConfigEditor("+(this.dataset?this.dataset.id:"")+")"}});ScatterplotConfigEditor.templates={mainLayout:Templates.editor,dataControl:Templates.datacontrol,chartControl:Templates.chartcontrol};var ScatterplotView=Backbone.View.extend({defaults:{dataset:{},metadata:{dataLines:void 0},ajaxFn:null,pagination:{currPage:0,perPage:3e3},width:400,height:400,margin:{top:16,right:16,bottom:40,left:54},x:{ticks:10,label:"X"},y:{ticks:10,label:"Y"},datapointSize:4,animDuration:500},initialize:function(a){this.config=_.extend(_.clone(this.defaults),a.config||{})},updateConfig:function(a){this.config=this.config||{},_.extend(this.config,a)},fetchData:function(){this.showLoadingIndicator("getting data");var a=this;return xhr=jQuery.getJSON("/api/datasets/"+this.config.dataset.id,{data_type:"raw_data",provider:"dataset-column",limit:this.config.pagination.perPage,offset:this.config.pagination.currPage*this.config.pagination.perPage}),xhr.done(function(b){a.renderData(b.data)}),xhr.fail(function(a,b,c){alert("Error loading data:\n"+a.responseText),console.error(a,b,c)}),xhr.always(function(){a.hideLoadingIndicator()}),xhr},render:function(a){return this.$el.addClass("scatterplot-display").html(['<div class="controls clear"></div>','<div class="loading-indicator">','<span class="fa fa-spinner fa-spin"></span>','<span class="loading-indicator-message"></span>',"</div>","<svg/>",'<div class="stats-display"></div>'].join("")),this.$el.children().hide(),a&&this.renderData(a),this},showLoadingIndicator:function(a,b){a=a||"",b=b||"fast";var c=this.$el.find(".loading-indicator");a&&c.find(".loading-indicator-message").text(a),c.is(":visible")||(this.toggleStats(!1),c.css({left:this.config.width/2,top:this.config.height/2}).show())},hideLoadingIndicator:function(a){a=a||"fast",this.$el.find(".loading-indicator").hide()},renderData:function(a){this.$el.find(".controls").empty().append(this.renderControls(a)).show(),this.renderPlot(a),this.getStats(a)},renderControls:function(a){var b=this,c=$('<div class="left"></div>'),d=$('<div class="right"></div>');return c.append([this.renderPrevNext(a),this.renderPagination(a)]),d.append([this.renderLineInfo(a),$("<button>Stats</button>").addClass("stats-toggle-btn").click(function(){b.toggleStats()}),$("<button>Redraw</button>").addClass("rerender-btn").click(function(){b.renderPlot(a)})]),[c,d]},renderLineInfo:function(a){var b=this.config.dataset.metadata_data_lines||"an unknown number of",c=this.config.pagination.currPage*this.config.pagination.perPage,d=c+a.length;return $("<p/>").addClass("scatterplot-data-info").text(["Displaying lines",c+1,"to",d,"of",b,"lines"].join(" "))},renderPrevNext:function(a){function b(a){return $(['<li><a href="javascript:void(0);">',a,"</a></li>"].join(""))}if(!a||0===this.config.pagination.currPage&&a.length<this.config.pagination.perPage)return null;var c=this,d=this.config.dataset.metadata_data_lines,e=d?Math.ceil(d/this.config.pagination.perPage):void 0,f=b("Prev").click(function(){c.config.pagination.currPage>0&&(c.config.pagination.currPage-=1,c.fetchData())}),g=b("Next").click(function(){(!e||c.config.pagination.currPage<e-1)&&(c.config.pagination.currPage+=1,c.fetchData())}),h=$("<ul/>").addClass("pagination data-prev-next").append([f,g]);return 0===c.config.pagination.currPage&&f.addClass("disabled"),e&&c.config.pagination.currPage===e-1&&g.addClass("disabled"),h},renderPagination:function(a){function b(a){return $(['<li><a href="javascript:void(0);">',a,"</a></li>"].join(""))}function c(){d.config.pagination.currPage=$(this).data("page"),d.fetchData()}if(!a||0===this.config.pagination.currPage&&a.length<this.config.pagination.perPage)return null;for(var d=this,e=this.config.dataset.metadata_data_lines,f=e?Math.ceil(e/this.config.pagination.perPage):void 0,g=$("<ul/>").addClass("pagination data-pages"),h=0;f>h;h+=1){var i=b(h+1).attr("data-page",h).click(c);h===this.config.pagination.currPage&&i.addClass("active"),g.append(i)}return g},renderPlot:function(a){this.toggleStats(!1);var b=this.$el.find("svg");b.off().empty().show(),scatterplot(b.get(0),this.config,a)},getStats:function(a){var b=this;meanWorker=new Worker("/plugins/visualizations/scatterplot/static/worker-stats.js"),meanWorker.postMessage({data:a,keys:[this.config.xColumn,this.config.yColumn]}),meanWorker.onerror=function(){meanWorker.terminate()},meanWorker.onmessage=function(a){b.renderStats(a.data)}},renderStats:function(a){var b=this.$el.find(".stats-display"),c=this.config.x.label,d=this.config.y.label,e=$("<table/>").addClass("table").append(["<thead><th></th><th>",c,"</th><th>",d,"</th></thead>"].join("")).append(_.map(a,function(a,b){return $(["<tr><td>",b,"</td><td>",a[0],"</td><td>",a[1],"</td></tr>"].join(""))}));b.empty().append(e)},toggleStats:function(a){var b=this.$el.find(".stats-display");a=void 0===a?b.is(":hidden"):a,a?(this.$el.find("svg").hide(),b.show(),this.$el.find(".controls .stats-toggle-btn").text("Plot")):(b.hide(),this.$el.find("svg").show(),this.$el.find(".controls .stats-toggle-btn").text("Stats"))},toString:function(){return"ScatterplotView()"}});
\ No newline at end of file
+function scatterplot(a,b,c){function d(){var a={v:{},h:{}};return a.v.lines=p.selectAll("line.v-grid-line").data(m.x.ticks(q.x.fn.ticks()[0])),a.v.lines.enter().append("svg:line").classed("grid-line v-grid-line",!0),a.v.lines.attr("x1",m.x).attr("x2",m.x).attr("y1",0).attr("y2",b.height),a.v.lines.exit().remove(),a.h.lines=p.selectAll("line.h-grid-line").data(m.y.ticks(q.y.fn.ticks()[0])),a.h.lines.enter().append("svg:line").classed("grid-line h-grid-line",!0),a.h.lines.attr("x1",0).attr("x2",b.width).attr("y1",m.y).attr("y2",m.y),a.h.lines.exit().remove(),a}function e(){return t.attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",function(a,b){return m.y(k(a,b))}).style("display","block").filter(function(){var a=d3.select(this).attr("cx"),c=d3.select(this).attr("cy");return 0>a||a>b.width?!0:0>c||c>b.height?!0:!1}).style("display","none")}function f(){q.redraw(),e(),s=d(),$(".chart-info-box").remove(),$(o.node()).trigger("zoom.scatterplot",[])}function g(a,c,d){return c+=8,$(['<div class="chart-info-box" style="position: absolute">',b.idColumn?"<div>"+d[b.idColumn]+"</div>":"","<div>",j(d),"</div>","<div>",k(d),"</div>","</div>"].join("")).css({top:a,left:c,"z-index":2})}var h=function(a,b){return"translate("+a+","+b+")"},i=function(a,b,c){return"rotate("+a+","+b+","+c+")"},j=function(a){return a[b.xColumn]},k=function(a){return a[b.yColumn]},l={x:{extent:d3.extent(c,j)},y:{extent:d3.extent(c,k)}},m={x:d3.scale.linear().domain(l.x.extent).range([0,b.width]),y:d3.scale.linear().domain(l.y.extent).range([b.height,0])},n=d3.behavior.zoom().x(m.x).y(m.y).scaleExtent([1,10]),o=d3.select(a).attr("class","scatterplot").attr("width","100%").attr("height",b.height+(b.margin.top+b.margin.bottom)),p=o.append("g").attr("class","content").attr("transform",h(b.margin.left,b.margin.top)).call(n);p.append("rect").attr("class","zoom-rect").attr("width",b.width).attr("height",b.height).style("fill","transparent");var q={x:{},y:{}};q.x.fn=d3.svg.axis().orient("bottom").scale(m.x).ticks(b.x.ticks).tickFormat(d3.format("s")),q.y.fn=d3.svg.axis().orient("left").scale(m.y).ticks(b.y.ticks).tickFormat(d3.format("s")),q.x.g=p.append("g").attr("class","x axis").attr("transform",h(0,b.height)).call(q.x.fn),q.y.g=p.append("g").attr("class","y axis").call(q.y.fn);var r=4;q.x.label=o.append("text").attr("class","axis-label").text(b.x.label).attr("text-anchor","middle").attr("dominant-baseline","text-after-edge").attr("x",b.width/2+b.margin.left).attr("y",b.height+b.margin.bottom+b.margin.top-r),q.y.label=o.append("text").attr("class","axis-label").text(b.y.label).attr("text-anchor","middle").attr("dominant-baseline","text-before-edge").attr("x",r).attr("y",b.height/2).attr("transform",i(-90,r,b.height/2)),q.redraw=function(){o.select(".x.axis").call(q.x.fn),o.select(".y.axis").call(q.y.fn)};var s=d(),t=p.selectAll(".glyph").data(c).enter().append("svg:circle").classed("glyph",!0).attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",b.height).attr("r",0);t.transition().duration(b.animDuration).attr("cy",function(a,b){return m.y(k(a,b))}).attr("r",b.datapointSize),n.on("zoom",f),t.on("mouseover",function(a,c){var d=d3.select(this);d.style("fill","red").style("fill-opacity",1),p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")-b.datapointSize).attr("y1",d.attr("cy")).attr("x2",0).attr("y2",d.attr("cy")).classed("hoverline",!0),d.attr("cy")<b.height&&p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")).attr("y1",+d.attr("cy")+b.datapointSize).attr("x2",d.attr("cx")).attr("y2",b.height).classed("hoverline",!0);var e=this.getBoundingClientRect();$("body").append(g(e.top,e.right,a)),$(o.node()).trigger("mouseover-datapoint.scatterplot",[this,a,c])}),t.on("mouseout",function(){d3.select(this).style("fill","black").style("fill-opacity",.2),p.selectAll(".hoverline").remove(),$(".chart-info-box").remove()})}this.Templates=this.Templates||{},this.Templates.chartcontrol=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f,g="",h="function",i=this.escapeExpression;return g+='<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 data-config-key="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">',(f=c.datapointSize)?f=f.call(b,{hash:{},data:e}):(f=b.datapointSize,f=typeof f===h?f.apply(b):f),g+=i(f)+'</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 data-config-key="width" class="form-input numeric-slider-input">\n <label for="width">Chart width: </label>\n <div class="slider-output">',(f=c.width)?f=f.call(b,{hash:{},data:e}):(f=b.width,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n</div>\n\n<div data-config-key="height" class="form-input numeric-slider-input">\n <label for="height">Chart height: </label>\n <div class="slider-output">',(f=c.height)?f=f.call(b,{hash:{},data:e}):(f=b.height,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n</div>\n\n<div data-config-key="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="'+i((f=b.x,f=null==f||f===!1?f:f.label,typeof f===h?f.apply(b):f))+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<div data-config-key="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="'+i((f=b.y,f=null==f||f===!1?f:f.label,typeof f===h?f.apply(b):f))+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.Templates.datacontrol=Handlebars.template(function(a,b,c,d,e){function f(a,b){var d,e="";return e+='\n <option value="',(d=c.index)?d=d.call(a,{hash:{},data:b}):(d=a.index,d=typeof d===j?d.apply(a):d),e+=k(d)+'">',(d=c.name)?d=d.call(a,{hash:{},data:b}):(d=a.name,d=typeof d===j?d.apply(a):d),e+=k(d)+"</option>\n "}function g(){return'checked="true"'}this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var h,i="",j="function",k=this.escapeExpression,l=this;return 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\n<div class="column-select">\n <label>Data column for X: </label>\n <select name="xColumn">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n<div class="column-select">\n <label>Data column for Y: </label>\n <select name="yColumn">\n ',h=c.each.call(b,b.numericColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n\n\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="idColumn">\n ',h=c.each.call(b,b.allColumns,{hash:{},inverse:l.noop,fn:l.program(1,f,e),data:e}),(h||0===h)&&(i+=h),i+='\n </select>\n</div>\n\n\n<div id="first-line-header" style="display: none;">\n <p>Possible headers: ',(h=c.possibleHeaders)?h=h.call(b,{hash:{},data:e}):(h=b.possibleHeaders,h=typeof h===j?h.apply(b):h),i+=k(h)+'\n </p>\n <label for="first-line-header-checkbox">Use the above as column headers?</label>\n <input type="checkbox" name="include-id" id="first-line-header-checkbox"\n ',h=c["if"].call(b,b.usePossibleHeaders,{hash:{},inverse:l.noop,fn:l.program(3,g,e),data:e}),(h||0===h)&&(i+=h),i+='/>\n <p class="help-text-small">\n It looks like Galaxy couldn\'t get proper column headers for this data.\n Would you like to use the column headers above as column names to select columns?\n </p>\n</div>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.Templates.editor=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f="";return f+='<div class="scatterplot-editor tabbable tabs-left">\n \n <ul class="nav nav-tabs">\n \n <li class="active">\n <a title="Use this tab to change which data are used"\n href="#data-control" data-toggle="tab">Data Controls</a>\n </li>\n <li>\n <a title="Use this tab to change how the chart is drawn"\n href="#chart-control" data-toggle="tab" >Chart Controls</a>\n </li>\n \n <li class="disabled">\n <a title="This tab will display the chart"\n href="#chart-display" data-toggle="tab">Chart</a>\n </li>\n </ul>\n\n \n <div class="tab-content">\n \n <div id="data-control" class="scatterplot-config-control tab-pane active">\n \n </div>\n \n \n <div id="chart-control" class="scatterplot-config-control tab-pane">\n \n </div>\n\n \n <div id="chart-display" class="scatterplot-display tab-pane"></div>\n\n </div>\n</div>\n'});var ScatterplotConfigEditor=BaseView.extend(LoggableMixin).extend({className:"scatterplot-control-form",initialize:function(a){if(!a||!a.config||!a.dataset)throw new Error("ScatterplotView requires a configuration and dataset");this.dataset=a.dataset,this.plotView=new ScatterplotView({dataset:a.dataset,config:a.config})},render:function(){return this.$el.append(ScatterplotConfigEditor.templates.mainLayout({})),this.$el.find("#data-control").append(this._render_dataControl()),this._render_chartControls(this.$el.find("#chart-control")),this._render_chartDisplay(),this.$el.find("[title]").tooltip(),this},_render_dataControl:function(){var a=this.dataset,b=_.map(a.metadata_column_types,function(b,c){var d={index:c,type:b,name:"column "+(c+1)};return a.metadata_column_names&&a.metadata_column_names[c]&&(d.name=a.metadata_column_names[c]),d}),c=_.filter(b,function(a){return"int"===a.type||"float"===a.type});2>c&&(c=b);var d=this.$el.find(".tab-pane#data-control");return d.html(ScatterplotConfigEditor.templates.dataControl({allColumns:b,numericColumns:c})),d.find('[name="xColumn"]').val(this.plotView.config.xColumn||c[0].index),d.find('[name="yColumn"]').val(this.plotView.config.yColumn||c[1].index),void 0!==this.plotView.config.idColumn&&(d.find("#include-id-checkbox").prop("checked",!0).trigger("change"),d.find('select[name="idColumn"]').val(this.plotView.config.idColumn)),d},_render_chartControls:function(a){function b(){var a=$(this);a.siblings(".slider-output").text(a.slider("value"))}a.html(ScatterplotConfigEditor.templates.chartControl(this.plotView.config));var c=this,d={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};return a.find(".numeric-slider-input").each(function(){var a=$(this),e=a.attr("data-config-key"),f=_.extend(d[e],{value:c.plotView.config[e],change:b,slide:b});a.find(".slider").slider(f)}),this.dataset.metadata_column_names,a},_render_chartDisplay:function(){var a=this.$el.find(".tab-pane#chart-display");return this.plotView.setElement(a),this.plotView.render(),a},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","click #data-control .render-button":"renderChart","click #chart-control .render-button":"renderChart"},toggleThirdColumnSelector:function(){this.$el.find('select[name="idColumn"]').parent().toggle()},renderChart:function(){this.$el.find(".nav li.disabled").removeClass("disabled"),this.updateConfigWithDataSettings(),this.updateConfigWithChartSettings(),this.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show"),this.plotView.fetchData()},updateConfigWithDataSettings:function(){var a=this.$el.find("#data-control"),b={xColumn:Number(a.find('[name="xColumn"]').val()),yColumn:Number(a.find('[name="yColumn"]').val())};return a.find("#include-id-checkbox").prop("checked")&&(b.idColumn=a.find('[name="idColumn"]').val()),_.extend(this.plotView.config,b)},updateConfigWithChartSettings:function(){var a=this.plotView,b=this.$el.find("#chart-control");return["datapointSize","width","height"].forEach(function(c){a.config[c]=b.find('.numeric-slider-input[data-config-key="'+c+'"]').find(".slider").slider("value")}),a.config.x.label=b.find('input[name="X-axis-label"]').val(),a.config.y.label=b.find('input[name="Y-axis-label"]').val(),a.config},toString:function(){return"ScatterplotConfigEditor("+(this.dataset?this.dataset.id:"")+")"}});ScatterplotConfigEditor.templates={mainLayout:Templates.editor,dataControl:Templates.datacontrol,chartControl:Templates.chartcontrol};var ScatterplotView=Backbone.View.extend({defaults:{metadata:{dataLines:void 0},pagination:{currPage:0,perPage:3e3},width:400,height:400,margin:{top:16,right:16,bottom:40,left:54},x:{ticks:10,label:"X"},y:{ticks:10,label:"Y"},datapointSize:4,animDuration:500},initialize:function(a){this.config=_.extend(_.clone(this.defaults),a.config||{}),this.dataset=a.dataset},updateConfig:function(a){this.config=this.config||{},_.extend(this.config,a)},fetchData:function(){this.showLoadingIndicator("getting data");var a=this;return xhr=jQuery.getJSON("/api/datasets/"+this.dataset.id,{data_type:"raw_data",provider:"dataset-column",limit:this.config.pagination.perPage,offset:this.config.pagination.currPage*this.config.pagination.perPage}),xhr.done(function(b){a.renderData(b.data)}),xhr.fail(function(a,b,c){alert("Error loading data:\n"+a.responseText),console.error(a,b,c)}),xhr.always(function(){a.hideLoadingIndicator()}),xhr},render:function(a){return this.$el.addClass("scatterplot-display").html(['<div class="controls clear"></div>','<div class="loading-indicator">','<span class="fa fa-spinner fa-spin"></span>','<span class="loading-indicator-message"></span>',"</div>","<svg/>",'<div class="stats-display"></div>'].join("")),this.$el.children().hide(),a&&this.renderData(a),this},showLoadingIndicator:function(a,b){a=a||"",b=b||"fast";var c=this.$el.find(".loading-indicator");a&&c.find(".loading-indicator-message").text(a),c.is(":visible")||(this.toggleStats(!1),c.css({left:this.config.width/2,top:this.config.height/2}).show())},hideLoadingIndicator:function(a){a=a||"fast",this.$el.find(".loading-indicator").hide()},renderData:function(a){this.$el.find(".controls").empty().append(this.renderControls(a)).show(),this.renderPlot(a),this.getStats(a)},renderControls:function(a){var b=this,c=$('<div class="left"></div>'),d=$('<div class="right"></div>');return c.append([this.renderPrevNext(a),this.renderPagination(a)]),d.append([this.renderLineInfo(a),$("<button>Stats</button>").addClass("stats-toggle-btn").click(function(){b.toggleStats()}),$("<button>Redraw</button>").addClass("rerender-btn").click(function(){b.renderPlot(a)})]),[c,d]},renderLineInfo:function(a){var b=this.dataset.metadata_data_lines||"an unknown number of",c=this.config.pagination.currPage*this.config.pagination.perPage,d=c+a.length;return $("<p/>").addClass("scatterplot-data-info").text(["Displaying lines",c+1,"to",d,"of",b,"lines"].join(" "))},renderPrevNext:function(a){function b(a){return $(['<li><a href="javascript:void(0);">',a,"</a></li>"].join(""))}if(!a||0===this.config.pagination.currPage&&a.length<this.config.pagination.perPage)return null;var c=this,d=this.dataset.metadata_data_lines,e=d?Math.ceil(d/this.config.pagination.perPage):void 0,f=b("Prev").click(function(){c.config.pagination.currPage>0&&(c.config.pagination.currPage-=1,c.fetchData())}),g=b("Next").click(function(){(!e||c.config.pagination.currPage<e-1)&&(c.config.pagination.currPage+=1,c.fetchData())}),h=$("<ul/>").addClass("pagination data-prev-next").append([f,g]);return 0===c.config.pagination.currPage&&f.addClass("disabled"),e&&c.config.pagination.currPage===e-1&&g.addClass("disabled"),h},renderPagination:function(a){function b(a){return $(['<li><a href="javascript:void(0);">',a,"</a></li>"].join(""))}function c(){d.config.pagination.currPage=$(this).data("page"),d.fetchData()}if(!a||0===this.config.pagination.currPage&&a.length<this.config.pagination.perPage)return null;for(var d=this,e=this.dataset.metadata_data_lines,f=e?Math.ceil(e/this.config.pagination.perPage):void 0,g=$("<ul/>").addClass("pagination data-pages"),h=0;f>h;h+=1){var i=b(h+1).attr("data-page",h).click(c);h===this.config.pagination.currPage&&i.addClass("active"),g.append(i)}return g},renderPlot:function(a){this.toggleStats(!1);var b=this.$el.find("svg");b.off().empty().show(),scatterplot(b.get(0),this.config,a)},getStats:function(a){var b=this;meanWorker=new Worker("/plugins/visualizations/scatterplot/static/worker-stats.js"),meanWorker.postMessage({data:a,keys:[this.config.xColumn,this.config.yColumn]}),meanWorker.onerror=function(){meanWorker.terminate()},meanWorker.onmessage=function(a){b.renderStats(a.data)}},renderStats:function(a){var b=this.$el.find(".stats-display"),c=this.config.x.label,d=this.config.y.label,e=$("<table/>").addClass("table").append(["<thead><th></th><th>",c,"</th><th>",d,"</th></thead>"].join("")).append(_.map(a,function(a,b){return $(["<tr><td>",b,"</td><td>",a[0],"</td><td>",a[1],"</td></tr>"].join(""))}));b.empty().append(e)},toggleStats:function(a){var b=this.$el.find(".stats-display");a=void 0===a?b.is(":hidden"):a,a?(this.$el.find("svg").hide(),b.show(),this.$el.find(".controls .stats-toggle-btn").text("Plot")):(b.hide(),this.$el.find("svg").show(),this.$el.find(".controls .stats-toggle-btn").text("Stats"))},toString:function(){return"ScatterplotView()"}});
\ No newline at end of file
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 config/plugins/visualizations/scatterplot/templates/scatterplot.mako
--- a/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
+++ b/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
@@ -15,7 +15,6 @@
<script type="text/javascript" src="/static/scripts/libs/jquery/jquery.migrate.js"></script><script type="text/javascript" src="/static/scripts/libs/underscore.js"></script><script type="text/javascript" src="/static/scripts/libs/backbone/backbone.js"></script>
-<script type="text/javascript" src="/static/scripts/libs/backbone/backbone-relational.js"></script><script type="text/javascript" src="/static/scripts/libs/handlebars.runtime.js"></script><script type="text/javascript" src="/static/scripts/libs/d3.js"></script><script type="text/javascript" src="/static/scripts/libs/bootstrap.js"></script>
@@ -43,22 +42,18 @@
data = None
##data = list( hda.datatype.dataset_column_dataprovider( hda, limit=10000 ) )
%>
- var hda = ${h.to_json_string( trans.security.encode_dict_ids( hda.to_dict() ) )},
- data = ${h.to_json_string( data )},
- querySettings = ${h.to_json_string( query_args )},
- config = _.extend( querySettings, {
- containerSelector : '#chart',
- dataset : hda,
- });
- //console.debug( querySettings );
+ var hda = ${h.to_json_string( trans.security.encode_dict_ids( hda.to_dict() ) )};
var editor = new ScatterplotConfigEditor({
el : $( '.scatterplot-editor' ).attr( 'id', 'scatterplot-editor-hda-' + hda.id ),
- config : config
+ config : ${h.to_json_string( query_args )},
+ dataset : ${h.to_json_string( trans.security.encode_dict_ids( hda.to_dict() ) )}
}).render();
+ window.editor = editor;
// uncomment to auto render for development
//$( '.render-button:visible' ).click();
});
+
</script>
%endif
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 doc/source/lib/galaxy.webapps.galaxy.api.rst
--- a/doc/source/lib/galaxy.webapps.galaxy.api.rst
+++ b/doc/source/lib/galaxy.webapps.galaxy.api.rst
@@ -205,6 +205,11 @@
The request and response format should be considered alpha and are subject to change.
+API Return Codes and Formats
+==================
+
+A set of error codes for API requests is being established and will be
+documented here. This is a long-term project however so stayed tuned.
API Controllers
===============
@@ -297,6 +302,14 @@
:undoc-members:
:show-inheritance:
+:mod:`lda_datasets` Module
+--------------------------
+
+.. automodule:: galaxy.webapps.galaxy.api.lda_datasets
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
:mod:`libraries` Module
-----------------------
@@ -393,3 +406,67 @@
:undoc-members:
:show-inheritance:
+
+API Design Guidelines
+=====================
+
+The following section outlines guidelines related to extending and/or modifing
+the Galaxy API. The Galaxy API has grown in an ad-hoc fashion over time by
+many contributors and so clients SHOULD NOT expect the API will conform to
+these guidelines - but developers contributing to the Galaxy API SHOULD follow
+these guidelines.
+
+ - API functionality should include docstring documentation for consumption
+ by readthedocs.org.
+ - Developers should familarize themselves with the HTTP status code definitions
+ http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html. The API responses
+ should properly set the status code according to the result - in particular
+ 2XX responses should be used for successful requests, 4XX for various
+ kinds of client errors, and 5XX for errors on the server side.
+ - If there is an error processing some part of request (one item in a list
+ for instance), the status code should be set to reflect the error and the
+ partial result may or may not be returned depending on the controller -
+ this behavior should be documented.
+ - (TODO) API methods should throw a finite number of exceptions (defined in)
+ `galaxy.exceptions` and these should subclass `MessageException` and not
+ paste/wsgi HTTP exceptions. When possible, the framework itself should be
+ responsible catching these exceptions, setting the status code, and
+ building an error response.
+ - Error responses should not consist of plain text strings - they should be
+ dictionaries describing the error and containing the following keys (TODO:
+ spell out nature of this.) Various error conditions (once a format has
+ been chosen and framework to enforce it in place) should be spelled out
+ in this document.
+ - Backward compatibility is important and should maintained when possible.
+ If changing behavior in a non-backward compatibile way please ensure one
+ of the following holds - there is a strong reason to believe no consumers
+ depend on a behavior, the behavior is effectively broken, or the API
+ method being modified has not been part of a tagged dist release.
+
+The following bullet points represent good practices more than guidelines, please
+consider them when modifying the API.
+
+ - Functionality should not be copied and pasted between controllers -
+ consider refactoring functionality into associated classes or short of
+ that into Mixins (http://en.wikipedia.org/wiki/Composition_over_inheritance)
+ - API additions are more permanent changes to Galaxy than many other potential
+ changes and so a second opinion on API changes should be sought. (Consider a
+ pull request!)
+ - New API functionality should include functional tests. These functional
+ tests should be implemented in Python and placed in
+ `test/functional/api`. (Once such a framework is in place - it is not
+ right now).
+ - Changes to reflect modifications to the API should be pushed upstream to
+ the BioBlend project possible.
+
+Longer term goals/notes.
+
+ - It would be advantageous to have a clearer separation of anonymous and
+ admin handling functionality.
+ - If at some point in the future, functionality needs to be added that
+ breaks backward compatibility in a significant way to a compontent used by
+ the community should be alerted - a "dev" variant of the API will be
+ established and the community should be alerted and given a timeframe
+ for when the old behavior will be replaced with the new behavior.
+ - Consistent standards for range-based requests, batch requests, filtered
+ requests, etc... should be established and documented here.
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 install_and_test_tool_shed_repositories.sh
--- a/install_and_test_tool_shed_repositories.sh
+++ b/install_and_test_tool_shed_repositories.sh
@@ -2,14 +2,20 @@
# A good place to look for nose info: http://somethingaboutorange.com/mrl/projects/nose/
-# The test/install_and_test_tool_shed_repositories/functional_tests.py can not be executed directly, because it must have certain functional test definitions
-# in sys.argv. Running it through this shell script is the best way to ensure that it has the required definitions.
+# The test/install_and_test_tool_shed_repositories/functional_tests.py cannot be executed directly because it must
+# have certain functional test definitions in sys.argv. Running it through this shell script is the best way to
+# ensure that it has the required definitions.
-# This script requires the following environment variables:
+# This script requires setting of the following environment variables:
# GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY - must be set to the API key for the tool shed that is being checked.
# GALAXY_INSTALL_TEST_TOOL_SHED_URL - must be set to a URL that the tool shed is listening on.
-# If the tool shed url is not specified in tool_sheds_conf.xml, GALAXY_INSTALL_TEST_TOOL_SHEDS_CONF must be set to a tool sheds configuration file
-# that does specify that url, otherwise repository installation will fail.
+
+# If the tool shed url is not specified in tool_sheds_conf.xml, GALAXY_INSTALL_TEST_TOOL_SHEDS_CONF must be set to
+# a tool sheds configuration file that does specify that url or repository installation will fail.
+
+# This script accepts the command line option -w to select which set of tests to run. The default behavior is to test
+# first tool_dependency_definition repositories and then repositories with tools. Provide the value 'dependencies'
+# to test only tool_dependency_definition repositories or 'tools' to test only repositories with tools.
if [ -z $GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY ] ; then
echo "This script requires the GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY environment variable to be set and non-empty."
@@ -37,7 +43,45 @@
fi
fi
-python test/install_and_test_tool_shed_repositories/functional_tests.py $* -v --with-nosehtml --html-report-file \
- test/install_and_test_tool_shed_repositories/run_functional_tests.html \
- test/install_and_test_tool_shed_repositories/functional/test_install_repositories.py \
- test/functional/test_toolbox.py
+test_tool_dependency_definitions () {
+ # Test installation of repositories of type tool_dependency_definition.
+ python test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py $* -v --with-nosehtml --html-report-file \
+ test/install_and_test_tool_shed_repositories/tool_dependency_definitions/run_functional_tests.html \
+ test/install_and_test_tool_shed_repositories/functional/test_install_repositories.py \
+ test/functional/test_toolbox.py
+}
+
+test_repositories_with_tools () {
+ # Test installation of repositories that contain valid tools with defined functional tests and a test-data directory containing test files.
+ python test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py $* -v --with-nosehtml --html-report-file \
+ test/install_and_test_tool_shed_repositories/repositories_with_tools/run_functional_tests.html \
+ test/install_and_test_tool_shed_repositories/functional/test_install_repositories.py \
+ test/functional/test_toolbox.py
+}
+
+which='both'
+
+while getopts "w:" arg; do
+ case $arg in
+ w)
+ which=$OPTARG
+ ;;
+ esac
+done
+
+case $which in
+ # Use "-w tool_dependency_definitions" when you want to test repositories of type tool_dependency_definition.
+ tool_dependency_definitions)
+ test_tool_dependency_definitions
+ ;;
+ # Use "-w repositories_with_tools" parameter when you want to test repositories that contain tools.
+ repositories_with_tools)
+ test_repositories_with_tools
+ ;;
+ # No received parameters or any received parameter not in [ tool_dependency_definitions, repositories_with_tools ]
+ # will execute both scripts.
+ *)
+ test_tool_dependency_definitions
+ test_repositories_with_tools
+ ;;
+esac
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 job_conf.xml.sample_advanced
--- a/job_conf.xml.sample_advanced
+++ b/job_conf.xml.sample_advanced
@@ -52,15 +52,44 @@
<param id="private_token">123456789changeme</param><!-- Uncomment the following statement to disable file staging (e.g.
if there is a shared file system between Galaxy and the LWR
- server). -->
+ server). Alternatively action can be set to 'copy' - to replace
+ http transfers with file system copies. --><!-- <param id="default_file_action">none</param> -->
+ <!-- The above option is just the default, the transfer behavior
+ none|copy|http can be configured on a per path basis via the
+ following file. See lib/galaxy/jobs/runners/lwr_client/action_mapper.py
+ for examples of how to configure this file. This is very beta
+ and nature of file will likely change.
+ -->
+ <!-- <param id="file_action_config">file_actions.json</param> -->
+ <!-- Uncomment following option to disable Galaxy tool dependency
+ resolution and utilize remote LWR's configuraiton of tool
+ dependency resolution instead (same options as Galaxy for
+ dependency resolution are available in LWR).
+ -->
+ <!-- <param id="dependency_resolution">remote</params> -->
+ <!-- Uncomment following option to enable setting metadata on remote
+ LWR server. The 'use_remote_datatypes' option is available for
+ determining whether to use remotely configured datatypes or local
+ ones (both alternatives are a little brittle). -->
+ <!-- <param id="remote_metadata">true</param> -->
+ <!-- <param id="use_remote_datatypes">false</param> -->
+ <!-- If remote LWR server is configured to run jobs as the real user,
+ uncomment the following line to pass the current Galaxy user
+ along. -->
+ <!-- <param id="submit_user">$__user_name__</param> -->
+ <!-- Various other submission parameters can be passed along to the LWR
+ whose use will depend on the remote LWR's configured job manager.
+ For instance:
+ -->
+ <!-- <param id="submit_native_specification">-P bignodes -R y -pe threads 8</param> --></destination><destination id="ssh_torque" runner="cli"><param id="shell_plugin">SecureShell</param><param id="job_plugin">Torque</param><param id="shell_username">foo</param><param id="shell_hostname">foo.example.org</param>
- <param id="Job_Execution_Time">24:00:00</param>
+ <param id="job_Resource_List">walltime=24:00:00,ncpus=4</param></destination><destination id="condor" runner="condor"><!-- With no params, jobs are submitted to the 'vanilla' universe with:
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/app.py
--- a/lib/galaxy/app.py
+++ b/lib/galaxy/app.py
@@ -1,16 +1,10 @@
from __future__ import absolute_import
-import sys, os, atexit
+import sys
+import os
-from galaxy import config, jobs, util, tools, web
-import galaxy.tools.search
-import galaxy.tools.data
-import tool_shed.galaxy_install
-import tool_shed.tool_shed_registry
-from galaxy.web import security
+from galaxy import config, jobs
import galaxy.model
-import galaxy.datatypes.registry
import galaxy.security
-from galaxy.objectstore import build_object_store_from_config
import galaxy.quota
from galaxy.tags.tag_handler import GalaxyTagHandler
from galaxy.visualization.genomes import Genomes
@@ -27,7 +21,8 @@
import logging
log = logging.getLogger( __name__ )
-class UniverseApplication( object ):
+
+class UniverseApplication( object, config.ConfiguresGalaxyMixin ):
"""Encapsulates the state of a Universe application"""
def __init__( self, **kwargs ):
print >> sys.stderr, "python path is: " + ", ".join( sys.path )
@@ -38,72 +33,38 @@
self.config.check()
config.configure_logging( self.config )
self.configure_fluent_log()
- # Determine the database url
- if self.config.database_connection:
- db_url = self.config.database_connection
- else:
- db_url = "sqlite:///%s?isolation_level=IMMEDIATE" % self.config.database
- # Set up the tool sheds registry
- if os.path.isfile( self.config.tool_sheds_config ):
- self.tool_shed_registry = tool_shed.tool_shed_registry.Registry( self.config.root, self.config.tool_sheds_config )
- else:
- self.tool_shed_registry = None
- # Initialize database / check for appropriate schema version. # If this
- # is a new installation, we'll restrict the tool migration messaging.
- from galaxy.model.migrate.check import create_or_verify_database
- create_or_verify_database( db_url, kwargs.get( 'global_conf', {} ).get( '__file__', None ), self.config.database_engine_options, app=self )
- # Alert the Galaxy admin to tools that have been moved from the distribution to the tool shed.
- from tool_shed.galaxy_install.migrate.check import verify_tools
- verify_tools( self, db_url, kwargs.get( 'global_conf', {} ).get( '__file__', None ), self.config.database_engine_options )
- # Object store manager
- self.object_store = build_object_store_from_config(self.config, fsmon=True)
+
+ self._configure_tool_shed_registry()
+
+ self._configure_object_store( fsmon=True )
+
# Setup the database engine and ORM
- from galaxy.model import mapping
- self.model = mapping.init( self.config.file_path,
- db_url,
- self.config.database_engine_options,
- database_query_profiling_proxy = self.config.database_query_profiling_proxy,
- object_store = self.object_store,
- trace_logger=self.trace_logger,
- use_pbkdf2=self.config.get_bool( 'use_pbkdf2', True ) )
+ config_file = kwargs.get( 'global_conf', {} ).get( '__file__', None )
+ self._configure_models( check_migrate_databases=True, check_migrate_tools=True, config_file=config_file )
+
# Manage installed tool shed repositories.
- self.installed_repository_manager = tool_shed.galaxy_install.InstalledRepositoryManager( self )
- # Create an empty datatypes registry.
- self.datatypes_registry = galaxy.datatypes.registry.Registry()
- # Load proprietary datatypes defined in datatypes_conf.xml files in all installed tool shed repositories. We
- # load proprietary datatypes before datatypes in the distribution because Galaxy's default sniffers include some
- # generic sniffers (eg text,xml) which catch anything, so it's impossible for proprietary sniffers to be used.
- # However, if there is a conflict (2 datatypes with the same extension) between a proprietary datatype and a datatype
- # in the Galaxy distribution, the datatype in the Galaxy distribution will take precedence. If there is a conflict
- # between 2 proprietary datatypes, the datatype from the repository that was installed earliest will take precedence.
- self.installed_repository_manager.load_proprietary_datatypes()
- # Load the data types in the Galaxy distribution, which are defined in self.config.datatypes_config.
- self.datatypes_registry.load_datatypes( self.config.root, self.config.datatypes_config )
+ from tool_shed.galaxy_install import installed_repository_manager
+ self.installed_repository_manager = installed_repository_manager.InstalledRepositoryManager( self )
+
+ self._configure_datatypes_registry( self.installed_repository_manager )
galaxy.model.set_datatypes_registry( self.datatypes_registry )
+
# Security helper
- self.security = security.SecurityHelper( id_secret=self.config.id_secret )
+ self._configure_security()
# Tag handler
self.tag_handler = GalaxyTagHandler()
# Genomes
self.genomes = Genomes( self )
# Data providers registry.
self.data_provider_registry = DataProviderRegistry()
- # Initialize tool data tables using the config defined by self.config.tool_data_table_config_path.
- self.tool_data_tables = galaxy.tools.data.ToolDataTableManager( tool_data_path=self.config.tool_data_path,
- config_filename=self.config.tool_data_table_config_path )
- # Load additional entries defined by self.config.shed_tool_data_table_config into tool data tables.
- self.tool_data_tables.load_from_config_file( config_filename=self.config.shed_tool_data_table_config,
- tool_data_path=self.tool_data_tables.tool_data_path,
- from_shed_config=False )
+
+ self._configure_tool_data_tables( from_shed_config=False )
+
# Initialize the job management configuration
self.job_config = jobs.JobConfiguration(self)
- # Initialize the tools, making sure the list of tool configs includes the reserved migrated_tools_conf.xml file.
- tool_configs = self.config.tool_configs
- if self.config.migrated_tools_config not in tool_configs:
- tool_configs.append( self.config.migrated_tools_config )
- self.toolbox = tools.ToolBox( tool_configs, self.config.tool_path, self )
- # Search support for tools
- self.toolbox_search = galaxy.tools.search.ToolBoxSearch( self.toolbox )
+
+ self._configure_toolbox()
+
# Load Data Manager
self.data_managers = DataManagers( self )
# If enabled, poll respective tool sheds to see if updates are available for any installed tool shed repositories.
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -1,6 +1,8 @@
"""
Universe configuration builder.
"""
+# absolute_import needed for tool_shed package.
+from __future__ import absolute_import
import sys, os, tempfile, re
import logging, logging.config
@@ -33,15 +35,22 @@
self.umask = os.umask( 077 ) # get the current umask
os.umask( self.umask ) # can't get w/o set, so set it back
self.gid = os.getgid() # if running under newgrp(1) we'll need to fix the group of data created on the cluster
+
# Database related configuration
self.database = resolve_path( kwargs.get( "database_file", "database/universe.sqlite" ), self.root )
self.database_connection = kwargs.get( "database_connection", False )
self.database_engine_options = get_database_engine_options( kwargs )
self.database_create_tables = string_as_bool( kwargs.get( "database_create_tables", "True" ) )
self.database_query_profiling_proxy = string_as_bool( kwargs.get( "database_query_profiling_proxy", "False" ) )
+
# Don't set this to true for production databases, but probably should
# default to True for sqlite databases.
self.database_auto_migrate = string_as_bool( kwargs.get( "database_auto_migrate", "False" ) )
+
+ # Install database related configuration (if different).
+ self.install_database_connection = kwargs.get( "install_database_connection", None )
+ self.install_database_engine_options = get_database_engine_options( kwargs, model_prefix="install_" )
+
# Where dataset files are stored
self.file_path = resolve_path( kwargs.get( "file_path", "database/files" ), self.root )
self.new_file_path = resolve_path( kwargs.get( "new_file_path", "database/tmp" ), self.root )
@@ -439,7 +448,7 @@
admin_users = [ x.strip() for x in self.get( "admin_users", "" ).split( "," ) ]
return ( user is not None and user.email in admin_users )
-def get_database_engine_options( kwargs ):
+def get_database_engine_options( kwargs, model_prefix='' ):
"""
Allow options for the SQLAlchemy database engine to be passed by using
the prefix "database_engine_option".
@@ -455,7 +464,7 @@
'pool_threadlocal': string_as_bool,
'server_side_cursors': string_as_bool
}
- prefix = "database_engine_option_"
+ prefix = "%sdatabase_engine_option_" % model_prefix
prefix_len = len( prefix )
rval = {}
for key, value in kwargs.iteritems():
@@ -466,6 +475,7 @@
rval[ key ] = value
return rval
+
def configure_logging( config ):
"""
Allow some basic logging configuration to be read from ini file.
@@ -506,3 +516,110 @@
sentry_handler.setLevel( logging.WARN )
root.addHandler( sentry_handler )
+
+class ConfiguresGalaxyMixin:
+ """ Shared code for configuring Galaxy-like app objects.
+ """
+
+ def _configure_toolbox( self ):
+ # Initialize the tools, making sure the list of tool configs includes the reserved migrated_tools_conf.xml file.
+ tool_configs = self.config.tool_configs
+ if self.config.migrated_tools_config not in tool_configs:
+ tool_configs.append( self.config.migrated_tools_config )
+ from galaxy import tools
+ self.toolbox = tools.ToolBox( tool_configs, self.config.tool_path, self )
+ # Search support for tools
+ import galaxy.tools.search
+ self.toolbox_search = galaxy.tools.search.ToolBoxSearch( self.toolbox )
+
+ def _configure_tool_data_tables( self, from_shed_config ):
+ from galaxy.tools.data import ToolDataTableManager
+
+ # Initialize tool data tables using the config defined by self.config.tool_data_table_config_path.
+ self.tool_data_tables = ToolDataTableManager( tool_data_path=self.config.tool_data_path,
+ config_filename=self.config.tool_data_table_config_path )
+ # Load additional entries defined by self.config.shed_tool_data_table_config into tool data tables.
+ self.tool_data_tables.load_from_config_file( config_filename=self.config.shed_tool_data_table_config,
+ tool_data_path=self.tool_data_tables.tool_data_path,
+ from_shed_config=from_shed_config )
+
+ def _configure_datatypes_registry( self, installed_repository_manager=None ):
+ from galaxy.datatypes import registry
+ # Create an empty datatypes registry.
+ self.datatypes_registry = registry.Registry()
+ if installed_repository_manager:
+ # Load proprietary datatypes defined in datatypes_conf.xml files in all installed tool shed repositories. We
+ # load proprietary datatypes before datatypes in the distribution because Galaxy's default sniffers include some
+ # generic sniffers (eg text,xml) which catch anything, so it's impossible for proprietary sniffers to be used.
+ # However, if there is a conflict (2 datatypes with the same extension) between a proprietary datatype and a datatype
+ # in the Galaxy distribution, the datatype in the Galaxy distribution will take precedence. If there is a conflict
+ # between 2 proprietary datatypes, the datatype from the repository that was installed earliest will take precedence.
+ installed_repository_manager.load_proprietary_datatypes()
+ # Load the data types in the Galaxy distribution, which are defined in self.config.datatypes_config.
+ self.datatypes_registry.load_datatypes( self.config.root, self.config.datatypes_config )
+
+ def _configure_object_store( self, **kwds ):
+ from galaxy.objectstore import build_object_store_from_config
+ self.object_store = build_object_store_from_config( self.config, **kwds )
+
+ def _configure_security( self ):
+ from galaxy.web import security
+ self.security = security.SecurityHelper( id_secret=self.config.id_secret )
+
+ def _configure_tool_shed_registry( self ):
+ import tool_shed.tool_shed_registry
+
+ # Set up the tool sheds registry
+ if os.path.isfile( self.config.tool_sheds_config ):
+ self.tool_shed_registry = tool_shed.tool_shed_registry.Registry( self.config.root, self.config.tool_sheds_config )
+ else:
+ self.tool_shed_registry = None
+
+ def _configure_models( self, check_migrate_databases=False, check_migrate_tools=False, config_file=None ):
+ """
+ Preconditions: object_store must be set on self.
+ """
+ if self.config.database_connection:
+ db_url = self.config.database_connection
+ else:
+ db_url = "sqlite:///%s?isolation_level=IMMEDIATE" % self.config.database
+ install_db_url = self.config.install_database_connection
+ # TODO: Consider more aggressive check here that this is not the same
+ # database file under the hood.
+ combined_install_database = not( install_db_url and install_db_url != db_url )
+ install_db_url = install_db_url or db_url
+
+ if check_migrate_databases:
+ # Initialize database / check for appropriate schema version. # If this
+ # is a new installation, we'll restrict the tool migration messaging.
+ from galaxy.model.migrate.check import create_or_verify_database
+ create_or_verify_database( db_url, config_file, self.config.database_engine_options, app=self )
+ if not combined_install_database:
+ from galaxy.model.tool_shed_install.migrate.check import create_or_verify_database as tsi_create_or_verify_database
+ tsi_create_or_verify_database( install_db_url, self.config.install_database_engine_options, app=self )
+
+ if check_migrate_tools:
+ # Alert the Galaxy admin to tools that have been moved from the distribution to the tool shed.
+ from tool_shed.galaxy_install.migrate.check import verify_tools
+ verify_tools( self, install_db_url, config_file, self.config.database_engine_options )
+
+ from galaxy.model import mapping
+ self.model = mapping.init( self.config.file_path,
+ db_url,
+ self.config.database_engine_options,
+ map_install_models=combined_install_database,
+ database_query_profiling_proxy=self.config.database_query_profiling_proxy,
+ object_store=self.object_store,
+ trace_logger=getattr(self, "trace_logger", None),
+ use_pbkdf2=self.config.get_bool( 'use_pbkdf2', True ) )
+
+ if combined_install_database:
+ log.info("Install database targetting Galaxy's database configuration.")
+ self.install_model = self.model
+ else:
+ from galaxy.model.tool_shed_install import mapping as install_mapping
+ install_db_url = self.config.install_database_connection
+ log.info("Install database using its own connection %s" % install_db_url)
+ install_db_engine_options = self.config.install_database_engine_options
+ self.install_model = install_mapping.init( install_db_url,
+ install_db_engine_options )
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/datatypes/checkers.py
--- a/lib/galaxy/datatypes/checkers.py
+++ b/lib/galaxy/datatypes/checkers.py
@@ -2,6 +2,8 @@
from galaxy import util
from StringIO import StringIO
+HTML_CHECK_LINES = 100
+
try:
import Image as PIL
except ImportError:
@@ -32,9 +34,11 @@
regexp1 = re.compile( "<A\s+[^>]*HREF[^>]+>", re.I )
regexp2 = re.compile( "<IFRAME[^>]*>", re.I )
regexp3 = re.compile( "<FRAMESET[^>]*>", re.I )
- regexp4 = re.compile( "<META[^>]*>", re.I )
+ regexp4 = re.compile( "<META[\W][^>]*>", re.I )
regexp5 = re.compile( "<SCRIPT[^>]*>", re.I )
lineno = 0
+ # TODO: Potentially reading huge lines into string here, this should be
+ # reworked.
for line in temp:
lineno += 1
matches = regexp1.search( line ) or regexp2.search( line ) or regexp3.search( line ) or regexp4.search( line ) or regexp5.search( line )
@@ -42,7 +46,7 @@
if chunk is None:
temp.close()
return True
- if lineno > 100:
+ if HTML_CHECK_LINES and (lineno > HTML_CHECK_LINES):
break
if chunk is None:
temp.close()
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/datatypes/data.py
--- a/lib/galaxy/datatypes/data.py
+++ b/lib/galaxy/datatypes/data.py
@@ -199,6 +199,26 @@
out = "Can't create peek %s" % str( exc )
return out
+ def _archive_main_file(self, archive, display_name, data_filename):
+ """Called from _archive_composite_dataset to add central file to archive.
+
+ Unless subclassed, this will add the main dataset file (argument data_filename)
+ to the archive, as an HTML file with its filename derived from the dataset name
+ (argument outfname).
+
+ Returns a tuple of boolean, string, string: (error, msg, messagetype)
+ """
+ error, msg, messagetype = False, "", ""
+ archname = '%s.html' % display_name # fake the real nature of the html file
+ try:
+ archive.add(data_filename, archname)
+ except IOError:
+ error = True
+ log.exception("Unable to add composite parent %s to temporary library download archive" % data_filename)
+ msg = "Unable to create archive for download, please report this error"
+ messagetype = "error"
+ return error, msg, messagetype
+
def _archive_composite_dataset( self, trans, data=None, **kwd ):
# save a composite object into a compressed archive for downloading
params = util.Params( kwd )
@@ -237,29 +257,27 @@
path = data.file_name
fname = os.path.split(path)[-1]
efp = data.extra_files_path
- htmlname = os.path.splitext(outfname)[0]
- if not htmlname.endswith(ext):
- htmlname = '%s_%s' % (htmlname,ext)
- archname = '%s.html' % htmlname # fake the real nature of the html file
- try:
- archive.add(data.file_name,archname)
- except IOError:
- error = True
- log.exception( "Unable to add composite parent %s to temporary library download archive" % data.file_name)
- msg = "Unable to create archive for download, please report this error"
- messagetype = 'error'
- for root, dirs, files in os.walk(efp):
- for fname in files:
- fpath = os.path.join(root,fname)
- rpath = os.path.relpath(fpath,efp)
- try:
- archive.add( fpath,rpath )
- except IOError:
- error = True
- log.exception( "Unable to add %s to temporary library download archive" % rpath)
- msg = "Unable to create archive for download, please report this error"
- messagetype = 'error'
- continue
+ #Add any central file to the archive,
+
+ display_name = os.path.splitext(outfname)[0]
+ if not display_name.endswith(ext):
+ display_name = '%s_%s' % (display_name, ext)
+
+ error, msg, messagetype = self._archive_main_file(archive, display_name, path)
+ if not error:
+ #Add any child files to the archive,
+ for root, dirs, files in os.walk(efp):
+ for fname in files:
+ fpath = os.path.join(root,fname)
+ rpath = os.path.relpath(fpath,efp)
+ try:
+ archive.add( fpath,rpath )
+ except IOError:
+ error = True
+ log.exception( "Unable to add %s to temporary library download archive" % rpath)
+ msg = "Unable to create archive for download, please report this error"
+ messagetype = 'error'
+ continue
if not error:
if params.do_action == 'zip':
archive.close()
@@ -288,7 +306,14 @@
return open( dataset.file_name )
def display_data(self, trans, data, preview=False, filename=None, to_ext=None, size=None, offset=None, **kwd):
- """ Old display method, for transition """
+ """ Old display method, for transition - though still used by API and
+ test framework. Datatypes should be very careful if overridding this
+ method and this interface between datatypes and Galaxy will likely
+ change.
+
+ TOOD: Document alternatives to overridding this method (data
+ providers?).
+ """
#Relocate all composite datatype display to a common location.
composite_extensions = trans.app.datatypes_registry.get_composite_extensions( )
composite_extensions.append('html') # for archiving composite datatypes
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/datatypes/interval.py
--- a/lib/galaxy/datatypes/interval.py
+++ b/lib/galaxy/datatypes/interval.py
@@ -389,7 +389,7 @@
MetadataElement( name="endCol", default=3, desc="End column", param=metadata.ColumnParameter )
MetadataElement( name="strandCol", desc="Strand column (click box & select)", param=metadata.ColumnParameter, optional=True, no_value=0 )
MetadataElement( name="columns", default=3, desc="Number of columns", readonly=True, visible=False )
- MetadataElement( name="viz_filter_cols", desc="Score column for visualization", default=[4], param=metadata.ColumnParameter, multiple=True )
+ MetadataElement( name="viz_filter_cols", desc="Score column for visualization", default=[4], param=metadata.ColumnParameter, optional=True, multiple=True )
###do we need to repeat these? they are the same as should be inherited from interval type
def set_meta( self, dataset, overwrite = True, **kwd ):
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/datatypes/registry.py
--- a/lib/galaxy/datatypes/registry.py
+++ b/lib/galaxy/datatypes/registry.py
@@ -348,7 +348,7 @@
try:
aclass = getattr( module, datatype_class_name )()
except Exception, e:
- self.log.exception( 'Error calling method %s from class %s: %s' ( str( datatype_class_name ), str( module ), str( e ) ) )
+ self.log.exception( 'Error calling method %s from class %s: %s', str( datatype_class_name ), str( module ), str( e ) )
ok = False
if ok:
if deactivate:
@@ -598,6 +598,9 @@
tool_xml_text = """
<tool id="__SET_METADATA__" name="Set External Metadata" version="1.0.1" tool_type="set_metadata"><type class="SetMetadataTool" module="galaxy.tools"/>
+ <requirements>
+ <requirement type="package">samtools</requirement>
+ </requirements><action module="galaxy.tools.actions.metadata" class="SetMetadataToolAction"/><command>$__SET_EXTERNAL_METADATA_COMMAND_LINE__</command><inputs>
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/datatypes/tabular.py
--- a/lib/galaxy/datatypes/tabular.py
+++ b/lib/galaxy/datatypes/tabular.py
@@ -652,7 +652,7 @@
MetadataElement( name="columns", default=10, desc="Number of columns", readonly=True, visible=False )
MetadataElement( name="column_types", default=['str','int','str','str','str','int','str','list','str','str'], param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False )
- MetadataElement( name="viz_filter_cols", desc="Score column for visualization", default=[5], param=metadata.ColumnParameter, multiple=True, visible=False )
+ MetadataElement( name="viz_filter_cols", desc="Score column for visualization", default=[5], param=metadata.ColumnParameter, optional=True, multiple=True, visible=False )
MetadataElement( name="sample_names", default=[], desc="Sample names", readonly=True, visible=False, optional=True, no_value=[] )
def sniff( self, filename ):
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/exceptions/__init__.py
--- a/lib/galaxy/exceptions/__init__.py
+++ b/lib/galaxy/exceptions/__init__.py
@@ -1,6 +1,10 @@
"""
Custom exceptions for Galaxy
"""
+
+from galaxy import eggs
+eggs.require( "Paste" )
+
from paste import httpexceptions
class MessageException( Exception ):
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -14,7 +14,6 @@
import shutil
import subprocess
import sys
-import threading
import traceback
from galaxy import model, util
from galaxy.datatypes import metadata
@@ -39,21 +38,6 @@
# and should eventually become API'd
TOOL_PROVIDED_JOB_METADATA_FILE = 'galaxy.json'
-class Sleeper( object ):
- """
- Provides a 'sleep' method that sleeps for a number of seconds *unless*
- the notify method is called (from a different thread).
- """
- def __init__( self ):
- self.condition = threading.Condition()
- def sleep( self, seconds ):
- self.condition.acquire()
- self.condition.wait( seconds )
- self.condition.release()
- def wake( self ):
- self.condition.acquire()
- self.condition.notify()
- self.condition.release()
class JobDestination( Bunch ):
"""
@@ -704,10 +688,7 @@
if self.command_line and self.command_line.startswith( 'python' ):
self.galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root
# Shell fragment to inject dependencies
- if self.app.config.use_tool_dependencies:
- self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
- else:
- self.dependency_shell_commands = None
+ self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
# We need command_line persisted to the db in order for Galaxy to re-queue the job
# if the server was stopped and restarted before the job finished
job.command_line = self.command_line
@@ -1451,10 +1432,7 @@
if self.command_line and self.command_line.startswith( 'python' ):
self.galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root
# Shell fragment to inject dependencies
- if self.app.config.use_tool_dependencies:
- self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
- else:
- self.dependency_shell_commands = None
+ self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
# We need command_line persisted to the db in order for Galaxy to re-queue the job
# if the server was stopped and restarted before the job finished
task.command_line = self.command_line
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/actions/post.py
--- a/lib/galaxy/jobs/actions/post.py
+++ b/lib/galaxy/jobs/actions/post.py
@@ -12,7 +12,7 @@
form = """
if (pja.action_type == "%s"){
p_str = "<div class='pjaForm toolForm'><span class='action_tag' style='display:none'>"+ pja.action_type + pja.output_name + "</span><div class='toolFormTitle'> %s <br/> on " + pja.output_name + "\
- <div style='float: right;' class='buttons'><img src='/static/images/delete_icon.png'></div></div><div class='toolFormBody'>";
+ <div style='float: right;' class='buttons'><img src='/static/images/history-buttons/delete_icon.png'></div></div><div class='toolFormBody'>";
%s
p_str += "</div><div class='toolParamHelp'>%s</div></div>";
}""" % (action_type, title, content, help)
@@ -20,7 +20,7 @@
form = """
if (pja.action_type == "%s"){
p_str = "<div class='pjaForm toolForm'><span class='action_tag' style='display:none'>"+ pja.action_type + "</span><div class='toolFormTitle'> %s \
- <div style='float: right;' class='buttons'><img src='/static/images/delete_icon.png'></div></div><div class='toolFormBody'>";
+ <div style='float: right;' class='buttons'><img src='/static/images/history-buttons/delete_icon.png'></div></div><div class='toolFormBody'>";
%s
p_str += "</div><div class='toolParamHelp'>%s</div></div>";
}""" % (action_type, title, content, help)
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/command_factory.py
--- a/lib/galaxy/jobs/command_factory.py
+++ b/lib/galaxy/jobs/command_factory.py
@@ -1,8 +1,11 @@
from os import getcwd
from os.path import abspath
+CAPTURE_RETURN_CODE = "return_code=$?"
+YIELD_CAPTURED_CODE = 'sh -c "exit $return_code"'
-def build_command( job, job_wrapper, include_metadata=False, include_work_dir_outputs=True ):
+
+def build_command( runner, job_wrapper, include_metadata=False, include_work_dir_outputs=True, remote_command_params={} ):
"""
Compose the sequence of commands necessary to execute a job. This will
currently include:
@@ -13,64 +16,125 @@
- commands to set metadata (if include_metadata is True)
"""
- commands = job_wrapper.get_command_line()
+ commands_builder = CommandsBuilder(job_wrapper.get_command_line())
# All job runners currently handle this case which should never occur
- if not commands:
+ if not commands_builder.commands:
return None
- # Remove trailing semi-colon so we can start hacking up this command.
- # TODO: Refactor to compose a list and join with ';', would be more clean.
- commands = commands.rstrip("; ")
+ __handle_version_command(commands_builder, job_wrapper)
+ __handle_task_splitting(commands_builder, job_wrapper)
+ __handle_dependency_resolution(commands_builder, job_wrapper, remote_command_params)
+ if include_work_dir_outputs:
+ __handle_work_dir_outputs(commands_builder, job_wrapper, runner, remote_command_params)
+
+ if include_metadata and job_wrapper.requires_setting_metadata:
+ __handle_metadata(commands_builder, job_wrapper, runner, remote_command_params)
+
+ return commands_builder.build()
+
+
+def __handle_version_command(commands_builder, job_wrapper):
# Prepend version string
if job_wrapper.version_string_cmd:
- commands = "%s &> %s; " % ( job_wrapper.version_string_cmd, job_wrapper.get_version_string_path() ) + commands
+ version_command = "%s &> %s" % ( job_wrapper.version_string_cmd, job_wrapper.get_version_string_path() )
+ commands_builder.prepend_command(version_command)
+
+def __handle_task_splitting(commands_builder, job_wrapper):
# prepend getting input files (if defined)
- if hasattr(job_wrapper, 'prepare_input_files_cmds') and job_wrapper.prepare_input_files_cmds is not None:
- commands = "; ".join( job_wrapper.prepare_input_files_cmds + [ commands ] )
+ if getattr(job_wrapper, 'prepare_input_files_cmds', None):
+ commands_builder.prepend_commands(job_wrapper.prepare_input_files_cmds)
+
+
+def __handle_dependency_resolution(commands_builder, job_wrapper, remote_command_params):
+ local_dependency_resolution = remote_command_params.get("dependency_resolution", "local") == "local"
# Prepend dependency injection
- if job_wrapper.dependency_shell_commands:
- commands = "; ".join( job_wrapper.dependency_shell_commands + [ commands ] )
+ if job_wrapper.dependency_shell_commands and local_dependency_resolution:
+ commands_builder.prepend_commands(job_wrapper.dependency_shell_commands)
- # Coping work dir outputs or setting metadata will mask return code of
- # tool command. If these are used capture the return code and ensure
- # the last thing that happens is an exit with return code.
- capture_return_code_command = "; return_code=$?"
- captured_return_code = False
+def __handle_work_dir_outputs(commands_builder, job_wrapper, runner, remote_command_params):
# Append commands to copy job outputs based on from_work_dir attribute.
- if include_work_dir_outputs:
- work_dir_outputs = job.get_work_dir_outputs( job_wrapper )
- if work_dir_outputs:
- if not captured_return_code:
- commands += capture_return_code_command
- captured_return_code = True
+ work_dir_outputs_kwds = {}
+ if 'working_directory' in remote_command_params:
+ work_dir_outputs_kwds['job_working_directory'] = remote_command_params['working_directory']
+ work_dir_outputs = runner.get_work_dir_outputs( job_wrapper, **work_dir_outputs_kwds )
+ if work_dir_outputs:
+ commands_builder.capture_return_code()
+ copy_commands = map(__copy_if_exists_command, work_dir_outputs)
+ commands_builder.append_commands(copy_commands)
- commands += "; " + "; ".join( [ "if [ -f %s ] ; then cp %s %s ; fi" %
- ( source_file, source_file, destination ) for ( source_file, destination ) in work_dir_outputs ] )
+def __handle_metadata(commands_builder, job_wrapper, runner, remote_command_params):
# Append metadata setting commands, we don't want to overwrite metadata
# that was copied over in init_meta(), as per established behavior
- if include_metadata and job_wrapper.requires_setting_metadata:
- metadata_command = job_wrapper.setup_external_metadata(
- exec_dir=abspath( getcwd() ),
- tmp_dir=job_wrapper.working_directory,
- dataset_files_path=job.app.model.Dataset.file_path,
- output_fnames=job_wrapper.get_output_fnames(),
- set_extension=False,
- kwds={ 'overwrite' : False }
- ) or ''
- metadata_command = metadata_command.strip()
- if metadata_command:
- if not captured_return_code:
- commands += capture_return_code_command
- captured_return_code = True
- commands += "; cd %s; %s" % (abspath( getcwd() ), metadata_command)
+ metadata_kwds = remote_command_params.get('metadata_kwds', {})
+ exec_dir = metadata_kwds.get( 'exec_dir', abspath( getcwd() ) )
+ tmp_dir = metadata_kwds.get( 'tmp_dir', job_wrapper.working_directory )
+ dataset_files_path = metadata_kwds.get( 'dataset_files_path', runner.app.model.Dataset.file_path )
+ output_fnames = metadata_kwds.get( 'output_fnames', job_wrapper.get_output_fnames() )
+ config_root = metadata_kwds.get( 'config_root', None )
+ config_file = metadata_kwds.get( 'config_file', None )
+ datatypes_config = metadata_kwds.get( 'datatypes_config', None )
+ metadata_command = job_wrapper.setup_external_metadata(
+ exec_dir=exec_dir,
+ tmp_dir=tmp_dir,
+ dataset_files_path=dataset_files_path,
+ output_fnames=output_fnames,
+ set_extension=False,
+ config_root=config_root,
+ config_file=config_file,
+ datatypes_config=datatypes_config,
+ kwds={ 'overwrite' : False }
+ ) or ''
+ metadata_command = metadata_command.strip()
+ if metadata_command:
+ commands_builder.capture_return_code()
+ commands_builder.append_command("cd %s; %s" % (exec_dir, metadata_command))
- if captured_return_code:
- commands += '; sh -c "exit $return_code"'
- return commands
+def __copy_if_exists_command(work_dir_output):
+ source_file, destination = work_dir_output
+ return "if [ -f %s ] ; then cp %s %s ; fi" % ( source_file, source_file, destination )
+
+
+class CommandsBuilder(object):
+
+ def __init__(self, initial_command):
+ # Remove trailing semi-colon so we can start hacking up this command.
+ # TODO: Refactor to compose a list and join with ';', would be more clean.
+ commands = initial_command.rstrip("; ")
+ self.commands = commands
+
+ # Coping work dir outputs or setting metadata will mask return code of
+ # tool command. If these are used capture the return code and ensure
+ # the last thing that happens is an exit with return code.
+ self.return_code_captured = False
+
+ def prepend_command(self, command):
+ self.commands = "%s; %s" % (command, self.commands)
+ return self
+
+ def prepend_commands(self, commands):
+ return self.prepend_command("; ".join(commands))
+
+ def append_command(self, command):
+ self.commands = "%s; %s" % (self.commands, command)
+
+ def append_commands(self, commands):
+ self.append_command("; ".join(commands))
+
+ def capture_return_code(self):
+ if not self.return_code_captured:
+ self.return_code_captured = True
+ self.append_command(CAPTURE_RETURN_CODE)
+
+ def build(self):
+ if self.return_code_captured:
+ self.append_command(YIELD_CAPTURED_CODE)
+ return self.commands
+
+__all__ = [build_command]
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/handler.py
--- a/lib/galaxy/jobs/handler.py
+++ b/lib/galaxy/jobs/handler.py
@@ -11,7 +11,8 @@
from sqlalchemy.sql.expression import and_, or_, select, func
from galaxy import model
-from galaxy.jobs import Sleeper, JobWrapper, TaskWrapper, JobDestination
+from galaxy.util.sleeper import Sleeper
+from galaxy.jobs import JobWrapper, TaskWrapper, JobDestination
log = logging.getLogger( __name__ )
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/manager.py
--- a/lib/galaxy/jobs/manager.py
+++ b/lib/galaxy/jobs/manager.py
@@ -11,7 +11,8 @@
from Queue import Empty, Queue
from galaxy import model
-from galaxy.jobs import handler, JobWrapper, NoopQueue, Sleeper
+from galaxy.util.sleeper import Sleeper
+from galaxy.jobs import handler, JobWrapper, NoopQueue
from galaxy.util.json import from_json_string
log = logging.getLogger( __name__ )
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/__init__.py
--- a/lib/galaxy/jobs/runners/__init__.py
+++ b/lib/galaxy/jobs/runners/__init__.py
@@ -146,11 +146,13 @@
def build_command_line( self, job_wrapper, include_metadata=False, include_work_dir_outputs=True ):
return build_command( self, job_wrapper, include_metadata=include_metadata, include_work_dir_outputs=include_work_dir_outputs )
- def get_work_dir_outputs( self, job_wrapper ):
+ def get_work_dir_outputs( self, job_wrapper, job_working_directory=None ):
"""
Returns list of pairs (source_file, destination) describing path
to work_dir output file and ultimate destination.
"""
+ if not job_working_directory:
+ job_working_directory = os.path.abspath( job_wrapper.working_directory )
def in_directory( file, directory ):
"""
@@ -186,7 +188,7 @@
if hda_tool_output and hda_tool_output.from_work_dir:
# Copy from working dir to HDA.
# TODO: move instead of copy to save time?
- source_file = os.path.join( os.path.abspath( job_wrapper.working_directory ), hda_tool_output.from_work_dir )
+ source_file = os.path.join( job_working_directory, hda_tool_output.from_work_dir )
destination = job_wrapper.get_output_destination( output_paths[ dataset.dataset_id ] )
if in_directory( source_file, job_wrapper.working_directory ):
output_pairs.append( ( source_file, destination ) )
@@ -196,7 +198,7 @@
log.exception( "from_work_dir specified a location not in the working directory: %s, %s" % ( source_file, job_wrapper.working_directory ) )
return output_pairs
- def _handle_metadata_externally(self, job_wrapper):
+ def _handle_metadata_externally( self, job_wrapper, resolve_requirements=False ):
"""
Set metadata externally. Used by the local and lwr job runners where this
shouldn't be attached to command-line to execute.
@@ -210,6 +212,12 @@
tmp_dir=job_wrapper.working_directory,
#we don't want to overwrite metadata that was copied over in init_meta(), as per established behavior
kwds={ 'overwrite' : False } )
+ if resolve_requirements:
+ dependency_shell_commands = self.app.datatypes_registry.set_external_metadata_tool.build_dependency_shell_commands()
+ if dependency_shell_commands:
+ if isinstance( dependency_shell_commands, list ):
+ dependency_shell_commands = "&&".join( dependency_shell_commands )
+ external_metadata_script = "%s&&%s" % ( dependency_shell_commands, external_metadata_script )
log.debug( 'executing external set_meta script for job %d: %s' % ( job_wrapper.job_id, external_metadata_script ) )
external_metadata_proc = subprocess.Popen( args=external_metadata_script,
shell=True,
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/cli_job/torque.py
--- a/lib/galaxy/jobs/runners/cli_job/torque.py
+++ b/lib/galaxy/jobs/runners/cli_job/torque.py
@@ -35,7 +35,8 @@
echo $? > %s
"""
-argmap = { 'Execution_Time' : '-a',
+argmap = { 'destination' : '-q',
+ 'Execution_Time' : '-a',
'Account_Name' : '-A',
'Checkpoint' : '-c',
'Error_Path' : '-e',
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/drmaa.py
--- a/lib/galaxy/jobs/runners/drmaa.py
+++ b/lib/galaxy/jobs/runners/drmaa.py
@@ -302,7 +302,15 @@
The external script will be run with sudo, and will setuid() to the specified user.
Effectively, will QSUB as a different user (then the one used by Galaxy).
"""
- p = subprocess.Popen([ '/usr/bin/sudo', '-E', self.external_runJob_script, str(username), jobtemplate_filename ],
+ script_parts = self.external_runJob_script.split()
+ script = script_parts[0]
+ command = [ '/usr/bin/sudo', '-E', script]
+ for script_argument in script_parts[1:]:
+ command.append(script_argument)
+
+ command.extend( [ str(username), jobtemplate_filename ] )
+ log.info("Running command %s" % command)
+ p = subprocess.Popen(command,
shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdoutdata, stderrdata) = p.communicate()
exitcode = p.returncode
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/local.py
--- a/lib/galaxy/jobs/runners/local.py
+++ b/lib/galaxy/jobs/runners/local.py
@@ -110,7 +110,7 @@
job_wrapper.fail( "failure running job", exception=True )
log.exception("failure running job %d" % job_wrapper.job_id)
return
- self._handle_metadata_externally( job_wrapper )
+ self._handle_metadata_externally( job_wrapper, resolve_requirements=True )
# Finish the job!
try:
job_wrapper.finish( stdout, stderr, exit_code )
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/lwr.py
--- a/lib/galaxy/jobs/runners/lwr.py
+++ b/lib/galaxy/jobs/runners/lwr.py
@@ -3,7 +3,9 @@
from galaxy import model
from galaxy.jobs.runners import AsynchronousJobState, AsynchronousJobRunner
from galaxy.jobs import JobDestination
+from galaxy.jobs.command_factory import build_command
from galaxy.util import string_as_bool_or_none
+from galaxy.util.bunch import Bunch
import errno
from time import sleep
@@ -12,11 +14,15 @@
from .lwr_client import ClientManager, url_to_destination_params
from .lwr_client import finish_job as lwr_finish_job
from .lwr_client import submit_job as lwr_submit_job
+from .lwr_client import ClientJobDescription
log = logging.getLogger( __name__ )
__all__ = [ 'LwrJobRunner' ]
+NO_REMOTE_GALAXY_FOR_METADATA_MESSAGE = "LWR misconfiguration - LWR client configured to set metadata remotely, but remote LWR isn't properly configured with a galaxy_home directory."
+NO_REMOTE_DATATYPES_CONFIG = "LWR client is configured to use remote datatypes configuration when setting metadata externally, but LWR is not configured with this information. Defaulting to datatypes_conf.xml."
+
class LwrJobRunner( AsynchronousJobRunner ):
"""
@@ -54,40 +60,31 @@
return job_state
def queue_job(self, job_wrapper):
- command_line = ''
job_destination = job_wrapper.job_destination
- try:
- job_wrapper.prepare()
- if hasattr(job_wrapper, 'prepare_input_files_cmds') and job_wrapper.prepare_input_files_cmds is not None:
- for cmd in job_wrapper.prepare_input_files_cmds: # run the commands to stage the input files
- #log.debug( 'executing: %s' % cmd )
- if 0 != os.system(cmd):
- raise Exception('Error running file staging command: %s' % cmd)
- job_wrapper.prepare_input_files_cmds = None # prevent them from being used in-line
- command_line = self.build_command_line( job_wrapper, include_metadata=False, include_work_dir_outputs=False )
- except:
- job_wrapper.fail( "failure preparing job", exception=True )
- log.exception("failure running job %d" % job_wrapper.job_id)
- return
+ command_line, client, remote_job_config = self.__prepare_job( job_wrapper, job_destination )
- # If we were able to get a command line, run the job
if not command_line:
- job_wrapper.finish( '', '' )
return
try:
- client = self.get_client_from_wrapper(job_wrapper)
- output_files = self.get_output_files(job_wrapper)
- input_files = job_wrapper.get_input_fnames()
- working_directory = job_wrapper.working_directory
- tool = job_wrapper.tool
- config_files = job_wrapper.extra_filenames
- job_id = lwr_submit_job(client, tool, command_line, config_files, input_files, output_files, working_directory)
+ dependency_resolution = LwrJobRunner.__dependency_resolution( client )
+ remote_dependency_resolution = dependency_resolution == "remote"
+ requirements = job_wrapper.tool.requirements if remote_dependency_resolution else []
+ client_job_description = ClientJobDescription(
+ command_line=command_line,
+ output_files=self.get_output_files(job_wrapper),
+ input_files=job_wrapper.get_input_fnames(),
+ working_directory=job_wrapper.working_directory,
+ tool=job_wrapper.tool,
+ config_files=job_wrapper.extra_filenames,
+ requirements=requirements,
+ )
+ job_id = lwr_submit_job(client, client_job_description, remote_job_config)
log.info("lwr job submitted with job_id %s" % job_id)
job_wrapper.set_job_destination( job_destination, job_id )
job_wrapper.change_state( model.Job.states.QUEUED )
- except:
+ except Exception:
job_wrapper.fail( "failure running job", exception=True )
log.exception("failure running job %d" % job_wrapper.job_id)
return
@@ -100,6 +97,52 @@
lwr_job_state.job_destination = job_destination
self.monitor_job(lwr_job_state)
+ def __prepare_job(self, job_wrapper, job_destination):
+ """ Build command-line and LWR client for this job. """
+ command_line = None
+ client = None
+ remote_job_config = None
+ try:
+ job_wrapper.prepare()
+ self.__prepare_input_files_locally(job_wrapper)
+ client = self.get_client_from_wrapper(job_wrapper)
+ tool = job_wrapper.tool
+ remote_job_config = client.setup(tool.id, tool.version)
+ remote_metadata = LwrJobRunner.__remote_metadata( client )
+ remote_work_dir_copy = LwrJobRunner.__remote_work_dir_copy( client )
+ dependency_resolution = LwrJobRunner.__dependency_resolution( client )
+ metadata_kwds = self.__build_metadata_configuration(client, job_wrapper, remote_metadata, remote_job_config)
+ remote_command_params = dict(
+ working_directory=remote_job_config['working_directory'],
+ metadata_kwds=metadata_kwds,
+ dependency_resolution=dependency_resolution,
+ )
+ command_line = build_command(
+ self,
+ job_wrapper=job_wrapper,
+ include_metadata=remote_metadata,
+ include_work_dir_outputs=remote_work_dir_copy,
+ remote_command_params=remote_command_params,
+ )
+ except Exception:
+ job_wrapper.fail( "failure preparing job", exception=True )
+ log.exception("failure running job %d" % job_wrapper.job_id)
+
+ # If we were able to get a command line, run the job
+ if not command_line:
+ job_wrapper.finish( '', '' )
+
+ return command_line, client, remote_job_config
+
+ def __prepare_input_files_locally(self, job_wrapper):
+ """Run task splitting commands locally."""
+ prepare_input_files_cmds = getattr(job_wrapper, 'prepare_input_files_cmds', None)
+ if prepare_input_files_cmds is not None:
+ for cmd in prepare_input_files_cmds: # run the commands to stage the input files
+ if 0 != os.system(cmd):
+ raise Exception('Error running file staging command: %s' % cmd)
+ job_wrapper.prepare_input_files_cmds = None # prevent them from being used in-line
+
def get_output_files(self, job_wrapper):
output_fnames = job_wrapper.get_output_fnames()
return [ str( o ) for o in output_fnames ]
@@ -130,34 +173,42 @@
run_results = client.raw_check_complete()
stdout = run_results.get('stdout', '')
stderr = run_results.get('stderr', '')
-
+ working_directory_contents = run_results.get('working_directory_contents', [])
# Use LWR client code to transfer/copy files back
# and cleanup job if needed.
completed_normally = \
job_wrapper.get_state() not in [ model.Job.states.ERROR, model.Job.states.DELETED ]
cleanup_job = self.app.config.cleanup_job
- work_dir_outputs = self.get_work_dir_outputs( job_wrapper )
+ remote_work_dir_copy = LwrJobRunner.__remote_work_dir_copy( client )
+ if not remote_work_dir_copy:
+ work_dir_outputs = self.get_work_dir_outputs( job_wrapper )
+ else:
+ # They have already been copied over to look like regular outputs remotely,
+ # no need to handle them differently here.
+ work_dir_outputs = []
output_files = self.get_output_files( job_wrapper )
finish_args = dict( client=client,
working_directory=job_wrapper.working_directory,
job_completed_normally=completed_normally,
cleanup_job=cleanup_job,
work_dir_outputs=work_dir_outputs,
- output_files=output_files )
+ output_files=output_files,
+ working_directory_contents=working_directory_contents )
failed = lwr_finish_job( **finish_args )
if failed:
job_wrapper.fail("Failed to find or download one or more job outputs from remote server.", exception=True)
- except:
+ except Exception:
message = "Failed to communicate with remote job server."
job_wrapper.fail( message, exception=True )
log.exception("failure running job %d" % job_wrapper.job_id)
return
- self._handle_metadata_externally( job_wrapper )
+ if not LwrJobRunner.__remote_metadata( client ):
+ self._handle_metadata_externally( job_wrapper, resolve_requirements=True )
# Finish the job
try:
job_wrapper.finish( stdout, stderr )
- except:
+ except Exception:
log.exception("Job wrapper finish method failed")
job_wrapper.fail("Unable to finish job", exception=True)
@@ -225,3 +276,71 @@
job_state.old_state = True
job_state.running = state == model.Job.states.RUNNING
self.monitor_queue.put( job_state )
+
+ @staticmethod
+ def __dependency_resolution( lwr_client ):
+ dependency_resolution = lwr_client.destination_params.get( "dependency_resolution", "local" )
+ if dependency_resolution not in ["none", "local", "remote"]:
+ raise Exception("Unknown dependency_resolution value encountered %s" % dependency_resolution)
+ return dependency_resolution
+
+ @staticmethod
+ def __remote_metadata( lwr_client ):
+ remote_metadata = string_as_bool_or_none( lwr_client.destination_params.get( "remote_metadata", False ) )
+ return remote_metadata
+
+ @staticmethod
+ def __remote_work_dir_copy( lwr_client ):
+ # Right now remote metadata handling assumes from_work_dir outputs
+ # have been copied over before it runs. So do that remotely. This is
+ # not the default though because adding it to the command line is not
+ # cross-platform (no cp on Windows) and its un-needed work outside
+ # the context of metadata settting (just as easy to download from
+ # either place.)
+ return LwrJobRunner.__remote_metadata( lwr_client )
+
+ @staticmethod
+ def __use_remote_datatypes_conf( lwr_client ):
+ """ When setting remote metadata, use integrated datatypes from this
+ Galaxy instance or use the datatypes config configured via the remote
+ LWR.
+
+ Both options are broken in different ways for same reason - datatypes
+ may not match. One can push the local datatypes config to the remote
+ server - but there is no guarentee these datatypes will be defined
+ there. Alternatively, one can use the remote datatype config - but
+ there is no guarentee that it will contain all the datatypes available
+ to this Galaxy.
+ """
+ use_remote_datatypes = string_as_bool_or_none( lwr_client.destination_params.get( "use_remote_datatypes", False ) )
+ return use_remote_datatypes
+
+ def __build_metadata_configuration(self, client, job_wrapper, remote_metadata, remote_job_config):
+ metadata_kwds = {}
+ if remote_metadata:
+ remote_system_properties = remote_job_config.get("system_properties", {})
+ remote_galaxy_home = remote_system_properties.get("galaxy_home", None)
+ if not remote_galaxy_home:
+ raise Exception(NO_REMOTE_GALAXY_FOR_METADATA_MESSAGE)
+ metadata_kwds['exec_dir'] = remote_galaxy_home
+ outputs_directory = remote_job_config['outputs_directory']
+ configs_directory = remote_job_config['configs_directory']
+ outputs = [Bunch(false_path=os.path.join(outputs_directory, os.path.basename(path)), real_path=path) for path in self.get_output_files(job_wrapper)]
+ metadata_kwds['output_fnames'] = outputs
+ metadata_kwds['config_root'] = remote_galaxy_home
+ default_config_file = os.path.join(remote_galaxy_home, 'universe_wsgi.ini')
+ metadata_kwds['config_file'] = remote_system_properties.get('galaxy_config_file', default_config_file)
+ metadata_kwds['dataset_files_path'] = remote_system_properties.get('galaxy_dataset_files_path', None)
+ if LwrJobRunner.__use_remote_datatypes_conf( client ):
+ remote_datatypes_config = remote_system_properties.get('galaxy_datatypes_config_file', None)
+ if not remote_datatypes_config:
+ log.warn(NO_REMOTE_DATATYPES_CONFIG)
+ remote_datatypes_config = os.path.join(remote_galaxy_home, 'datatypes_conf.xml')
+ metadata_kwds['datatypes_config'] = remote_datatypes_config
+ else:
+ integrates_datatypes_config = self.app.datatypes_registry.integrated_datatypes_configs
+ # Ensure this file gets pushed out to the remote config dir.
+ job_wrapper.extra_filenames.append(integrates_datatypes_config)
+
+ metadata_kwds['datatypes_config'] = os.path.join(configs_directory, os.path.basename(integrates_datatypes_config))
+ return metadata_kwds
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/lwr_client/__init__.py
--- a/lib/galaxy/jobs/runners/lwr_client/__init__.py
+++ b/lib/galaxy/jobs/runners/lwr_client/__init__.py
@@ -6,9 +6,9 @@
"""
-from .stager import submit_job, finish_job
+from .stager import submit_job, finish_job, ClientJobDescription
from .client import OutputNotFoundException
from .manager import ClientManager
from .destination import url_to_destination_params
-__all__ = [ClientManager, OutputNotFoundException, url_to_destination_params, finish_job, submit_job]
+__all__ = [ClientManager, OutputNotFoundException, url_to_destination_params, finish_job, submit_job, ClientJobDescription]
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/lwr_client/action_mapper.py
--- a/lib/galaxy/jobs/runners/lwr_client/action_mapper.py
+++ b/lib/galaxy/jobs/runners/lwr_client/action_mapper.py
@@ -21,7 +21,7 @@
>>> from tempfile import NamedTemporaryFile
>>> from os import unlink
>>> f = NamedTemporaryFile(delete=False)
- >>> f.write(json_string)
+ >>> write_result = f.write(json_string.encode('UTF-8'))
>>> f.close()
>>> class MockClient():
... default_file_action = 'none'
@@ -30,23 +30,23 @@
>>> mapper = FileActionMapper(MockClient())
>>> unlink(f.name)
>>> # Test first config line above, implicit path prefix mapper
- >>> mapper.action('/opt/galaxy/tools/filters/catWrapper.py', 'input')
- ('none',)
+ >>> mapper.action('/opt/galaxy/tools/filters/catWrapper.py', 'input')[0] == u'none'
+ True
>>> # Test another (2nd) mapper, this one with a different action
- >>> mapper.action('/galaxy/data/files/000/dataset_1.dat', 'input')
- ('transfer',)
+ >>> mapper.action('/galaxy/data/files/000/dataset_1.dat', 'input')[0] == u'transfer'
+ True
>>> # Always at least copy work_dir outputs.
- >>> mapper.action('/opt/galaxy/database/working_directory/45.sh', 'work_dir')
- ('copy',)
+ >>> mapper.action('/opt/galaxy/database/working_directory/45.sh', 'work_dir')[0] == u'copy'
+ True
>>> # Test glob mapper (matching test)
- >>> mapper.action('/cool/bamfiles/projectABC/study1/patient3.bam', 'input')
- ('copy',)
+ >>> mapper.action('/cool/bamfiles/projectABC/study1/patient3.bam', 'input')[0] == u'copy'
+ True
>>> # Test glob mapper (non-matching test)
- >>> mapper.action('/cool/bamfiles/projectABC/study1/patient3.bam.bai', 'input')
- ('none',)
+ >>> mapper.action('/cool/bamfiles/projectABC/study1/patient3.bam.bai', 'input')[0] == u'none'
+ True
>>> # Regex mapper test.
- >>> mapper.action('/old/galaxy/data/dataset_10245.dat', 'input')
- ('copy',)
+ >>> mapper.action('/old/galaxy/data/dataset_10245.dat', 'input')[0] == u'copy'
+ True
"""
def __init__(self, client):
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/lwr_client/client.py
--- a/lib/galaxy/jobs/runners/lwr_client/client.py
+++ b/lib/galaxy/jobs/runners/lwr_client/client.py
@@ -50,7 +50,7 @@
return "No remote output found for path %s" % self.path
-class Client(object):
+class JobClient(object):
"""
Objects of this client class perform low-level communication with a remote LWR server.
@@ -161,25 +161,23 @@
raise Exception("Unknown output_type returned from LWR server %s" % output_type)
return output_path
- def fetch_work_dir_output(self, source, working_directory, output_path, action='transfer'):
+ def fetch_work_dir_output(self, name, working_directory, output_path, action='transfer'):
"""
Download an output dataset specified with from_work_dir from the
remote server.
**Parameters**
- source : str
+ name : str
Path in job's working_directory to find output in.
working_directory : str
Local working_directory for the job.
output_path : str
Full path to output dataset.
"""
- output = open(output_path, "wb")
- name = os.path.basename(source)
if action == 'transfer':
- self.__raw_download_output(name, self.job_id, "work_dir", output)
- elif action == 'copy':
+ self.__raw_download_output(name, self.job_id, "work_dir", output_path)
+ else: # Even if action is none - LWR has a different work_dir so this needs to be copied.
lwr_path = self._output_path(name, self.job_id, 'work_dir')['path']
self._copy(lwr_path, output_path)
@@ -199,7 +197,7 @@
}
self._raw_execute("download_output", output_params, output_path=output_path)
- def launch(self, command_line):
+ def launch(self, command_line, requirements=[]):
"""
Run or queue up the execution of the supplied
`command_line` on the remote server.
@@ -213,6 +211,8 @@
submit_params = self._submit_params
if submit_params:
launch_params['params'] = dumps(submit_params)
+ if requirements:
+ launch_params['requirements'] = dumps([requirement.to_dict() for requirement in requirements])
return self._raw_execute("launch", launch_params)
def kill(self):
@@ -285,13 +285,13 @@
shutil.copyfile(source, destination)
-class InputCachingClient(Client):
+class InputCachingJobClient(JobClient):
"""
Beta client that cache's staged files to prevent duplication.
"""
def __init__(self, destination_params, job_id, job_manager_interface, client_cacher):
- super(InputCachingClient, self).__init__(destination_params, job_id, job_manager_interface)
+ super(InputCachingJobClient, self).__init__(destination_params, job_id, job_manager_interface)
self.client_cacher = client_cacher
@parseJson()
@@ -326,3 +326,55 @@
@parseJson()
def file_available(self, path):
return self._raw_execute("file_available", {"path": path})
+
+
+class ObjectStoreClient(object):
+
+ def __init__(self, lwr_interface):
+ self.lwr_interface = lwr_interface
+
+ @parseJson()
+ def exists(self, **kwds):
+ return self._raw_execute("object_store_exists", args=self.__data(**kwds))
+
+ @parseJson()
+ def file_ready(self, **kwds):
+ return self._raw_execute("object_store_file_ready", args=self.__data(**kwds))
+
+ @parseJson()
+ def create(self, **kwds):
+ return self._raw_execute("object_store_create", args=self.__data(**kwds))
+
+ @parseJson()
+ def empty(self, **kwds):
+ return self._raw_execute("object_store_empty", args=self.__data(**kwds))
+
+ @parseJson()
+ def size(self, **kwds):
+ return self._raw_execute("object_store_size", args=self.__data(**kwds))
+
+ @parseJson()
+ def delete(self, **kwds):
+ return self._raw_execute("object_store_delete", args=self.__data(**kwds))
+
+ @parseJson()
+ def get_data(self, **kwds):
+ return self._raw_execute("object_store_get_data", args=self.__data(**kwds))
+
+ @parseJson()
+ def get_filename(self, **kwds):
+ return self._raw_execute("object_store_get_filename", args=self.__data(**kwds))
+
+ @parseJson()
+ def update_from_file(self, **kwds):
+ return self._raw_execute("object_store_update_from_file", args=self.__data(**kwds))
+
+ @parseJson()
+ def get_store_usage_percent(self):
+ return self._raw_execute("object_store_get_store_usage_percent", args={})
+
+ def __data(self, **kwds):
+ return kwds
+
+ def _raw_execute(self, command, args={}):
+ return self.lwr_interface.execute(command, args, data=None, input_path=None, output_path=None)
diff -r 3871ca1bbfcfad27d63ca7ba8464714313a64ec0 -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 lib/galaxy/jobs/runners/lwr_client/destination.py
--- a/lib/galaxy/jobs/runners/lwr_client/destination.py
+++ b/lib/galaxy/jobs/runners/lwr_client/destination.py
@@ -51,9 +51,10 @@
>>> destination_params = {"private_token": "12345", "submit_native_specification": "-q batch"}
>>> result = submit_params(destination_params)
- >>> result.items()
- [('native_specification', '-q batch')]
+ >>> result
+ {'native_specification': '-q batch'}
"""
- return dict([(key[len(SUBMIT_PREFIX):], value)
- for key, value in (destination_params or {}).iteritems()
+ destination_params = destination_params or {}
+ return dict([(key[len(SUBMIT_PREFIX):], destination_params[key])
+ for key in destination_params
if key.startswith(SUBMIT_PREFIX)])
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/1bdc7d3beff4/
Changeset: 1bdc7d3beff4
Branch: search
User: Kyle Ellrott
Date: 2014-01-02 20:51:31
Summary: Fix to 'stringify' datetime object that was getting passed from to_dict LibraryFolder and into JSON converter.
Affected #: 1 file
diff -r 8388f17e522a775bfeb11f5a1d2eb2b3e2d80a60 -r 1bdc7d3beff411a31ac0656f648d433d3da04d69 lib/galaxy/model/item_attrs.py
--- a/lib/galaxy/model/item_attrs.py
+++ b/lib/galaxy/model/item_attrs.py
@@ -3,6 +3,8 @@
# Cannot import galaxy.model b/c it creates a circular import graph.
import galaxy
import logging
+import datetime
+
log = logging.getLogger( __name__ )
class RuntimeException( Exception ):
@@ -182,6 +184,9 @@
except:
if key in value_mapper:
return value_mapper.get( key )( item )
+ #If the item is of a class that needs to be 'stringified' before being put into a JSON data structure
+ if type(item) in [datetime.datetime]:
+ return str(item)
return item
# Create dict to represent item.
https://bitbucket.org/galaxy/galaxy-central/commits/d05b1d649290/
Changeset: d05b1d649290
User: dannon
Date: 2014-01-07 20:32:27
Summary: Merged in kellrott/galaxy-central/search (pull request #285)
Make to_dict method stringify non-JSONable objects
Affected #: 1 file
diff -r abcc67f37be7f9450749fa87f6588e90964a6681 -r d05b1d649290a8563558e8721f4a1ca8336e8180 lib/galaxy/model/item_attrs.py
--- a/lib/galaxy/model/item_attrs.py
+++ b/lib/galaxy/model/item_attrs.py
@@ -3,6 +3,8 @@
# Cannot import galaxy.model b/c it creates a circular import graph.
import galaxy
import logging
+import datetime
+
log = logging.getLogger( __name__ )
class RuntimeException( Exception ):
@@ -182,6 +184,9 @@
except:
if key in value_mapper:
return value_mapper.get( key )( item )
+ #If the item is of a class that needs to be 'stringified' before being put into a JSON data structure
+ if type(item) in [datetime.datetime]:
+ return str(item)
return item
# Create dict to represent item.
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: dannon: Remove redundant run_as_users check.
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/abcc67f37be7/
Changeset: abcc67f37be7
User: dannon
Date: 2014-01-07 20:25:11
Summary: Remove redundant run_as_users check.
Affected #: 1 file
diff -r 7e11c0d36c85a0e916534ef6733c35fbf5046c41 -r abcc67f37be7f9450749fa87f6588e90964a6681 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -950,7 +950,7 @@
run_as_users = [ user for user in self.app.config.get( "api_allow_run_as", "" ).split( "," ) if user ]
if not run_as_users:
return False
- user_in_run_as_users = self.user and run_as_users and self.user.email in run_as_users
+ user_in_run_as_users = self.user and self.user.email in run_as_users
# Can do if explicitly in list or master_api_key supplied.
can_do_run_as = user_in_run_as_users or self.api_inherit_admin
return can_do_run_as
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/ce1977c956b3/
Changeset: ce1977c956b3
User: jmchilton
Date: 2014-01-04 05:04:36
Summary: If master_api_key set, allow it to run_as if any user can run_as.
If no master_api_key set no change in behavior, if api_allow_run_as is empty no change in behavior (i.e. run_as will not work with master_api_key if no api_allow_run_as users are configured - as to not increase attack surface for these Galaxy instances).
This changeset contains no tests, but pull request #278 included functional tests for these changes (as well as original behavior) which passed.
Affected #: 1 file
diff -r f0f7c3cd2e8af64243f878c04f418c1ee054bc55 -r ce1977c956b3457bd3b7ee50506b5e2675192f17 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -947,8 +947,13 @@
return self.user and admin_users and self.user.email in admin_users
def user_can_do_run_as( self ):
- run_as_users = self.app.config.get( "api_allow_run_as", "" ).split( "," )
- return self.user and run_as_users and self.user.email in run_as_users
+ run_as_users = [ user for user in self.app.config.get( "api_allow_run_as", "" ).split( "," ) if user ]
+ if not run_as_users:
+ return False
+ user_in_run_as_users = self.user and run_as_users and self.user.email in run_as_users
+ # Can do if explicitly in list or master_api_key supplied.
+ can_do_run_as = user_in_run_as_users or self.api_inherit_admin
+ return can_do_run_as
def get_toolbox(self):
"""Returns the application toolbox"""
https://bitbucket.org/galaxy/galaxy-central/commits/7e11c0d36c85/
Changeset: 7e11c0d36c85
User: dannon
Date: 2014-01-07 20:24:11
Summary: Merged in jmchilton/galaxy-central-fork-1 (pull request #288)
If master_api_key set, allow it to run_as if any user can run_as.
Affected #: 1 file
diff -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d -r 7e11c0d36c85a0e916534ef6733c35fbf5046c41 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -947,8 +947,13 @@
return self.user and admin_users and self.user.email in admin_users
def user_can_do_run_as( self ):
- run_as_users = self.app.config.get( "api_allow_run_as", "" ).split( "," )
- return self.user and run_as_users and self.user.email in run_as_users
+ run_as_users = [ user for user in self.app.config.get( "api_allow_run_as", "" ).split( "," ) if user ]
+ if not run_as_users:
+ return False
+ user_in_run_as_users = self.user and run_as_users and self.user.email in run_as_users
+ # Can do if explicitly in list or master_api_key supplied.
+ can_do_run_as = user_in_run_as_users or self.api_inherit_admin
+ return can_do_run_as
def get_toolbox(self):
"""Returns the application toolbox"""
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: martenson: galaxy.modal refactoring; API documentation; folders API refactoring and implementation; libraries API refactoring
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/f2e3d2e41c1d/
Changeset: f2e3d2e41c1d
User: martenson
Date: 2014-01-07 19:52:04
Summary: galaxy.modal refactoring; API documentation; folders API refactoring and implementation; libraries API refactoring
Affected #: 7 files
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d lib/galaxy/webapps/galaxy/api/folders.py
--- a/lib/galaxy/webapps/galaxy/api/folders.py
+++ b/lib/galaxy/webapps/galaxy/api/folders.py
@@ -2,11 +2,12 @@
API operations on folders
"""
import logging, os, string, shutil, urllib, re, socket, traceback
+from galaxy import datatypes, jobs, web, security
+from galaxy.web.base.controller import BaseAPIController,UsesLibraryMixin,UsesLibraryMixinItems
+from galaxy.util.sanitize_html import sanitize_html
+
from cgi import escape, FieldStorage
-from galaxy import util, datatypes, jobs, web, util
-from galaxy.web.base.controller import *
-from galaxy.util.sanitize_html import sanitize_html
-from galaxy.model.orm import *
+from paste.httpexceptions import HTTPBadRequest
log = logging.getLogger( __name__ )
@@ -24,8 +25,13 @@
@web.expose_api
def show( self, trans, id, **kwd ):
"""
- GET /api/folders/{encoded_folder_id}
- Displays information about a folder
+ show( self, trans, id, **kwd )
+ *GET /api/folders/{encoded_folder_id}
+
+ Displays information about a folder.
+
+ :param encoded_parent_folder_id: the parent folder's id (required)
+ :type encoded_parent_folder_id: an encoded id string (should be prefixed by 'F')
"""
# Eliminate any 'F' in front of the folder id. Just take the
# last 16 characters:
@@ -38,59 +44,64 @@
return self.encode_all_ids( trans, content.to_dict( view='element' ) )
@web.expose_api
- def create( self, trans, payload, **kwd ):
+ def create( self, trans, encoded_parent_folder_id, **kwd ):
+
+ # is_admin = trans.user_is_admin()
+ # current_user_roles = trans.get_current_user_roles()
+
"""
- POST /api/folders/{encoded_folder_id}
- Create a new object underneath the one specified in the parameters.
- This will use the same parameters and semantics as
- /api/libraries/{LibID}/contents/{ContentId} for consistency.
- This means that datasets and folders can be generated. Note that
- /api/libraries/{LibID}/contents/{ContentId} did not need the library
- id to function properly, which is why this functionality has been
- moved here.
+ create( self, trans, encoded_parent_folder_id, **kwd )
+ *POST /api/folders/{encoded_parent_folder_id}
- o payload's relevant params:
- - folder_id: This is the parent folder's id (required)
+ Create a new folder object underneath the one specified in the parameters.
+
+ :param encoded_parent_folder_id: the parent folder's id (required)
+ :type encoded_parent_folder_id: an encoded id string (should be prefixed by 'F')
+ :param name: the name of the new folder (required)
+ :type name: str
+ :param description: the description of the new folder
+ :type description: str
+
+ :rtype: dictionary
+ :returns: information about newly created folder, notably including ID
+
+ :raises: HTTPBadRequest, MessageException
"""
-# log.debug( "FoldersController.create: enter" )
- # TODO: Create a single point of exit if possible. For now we only
- # exit at the end and on exceptions.
- if 'folder_id' not in payload:
- trans.response.status = 400
- return "Missing requred 'folder_id' parameter."
- else:
- folder_id = payload.pop( 'folder_id' )
- class_name, folder_id = self.__decode_library_content_id( trans, folder_id )
+ payload = kwd.get('payload', None)
+ if payload == None:
+ raise HTTPBadRequest( detail="Missing required parameters 'encoded_parent_folder_id' and 'name'." )
+
+ name = payload.get('name', None)
+ description = payload.get('description', '')
+ if encoded_parent_folder_id == None:
+ raise HTTPBadRequest( detail="Missing required parameter 'encoded_parent_folder_id'." )
+ elif name == None:
+ raise HTTPBadRequest( detail="Missing required parameter 'name'." )
+
+ # encoded_parent_folder_id may be prefixed by 'F'
+ encoded_parent_folder_id = self.__cut_the_prefix( encoded_parent_folder_id )
+
+ # if ( len( encoded_parent_folder_id ) == 17 and encoded_parent_folder_id.startswith( 'F' ) ):
+ # encoded_parent_folder_id = encoded_parent_folder_id[-16:]
+
try:
- # security is checked in the downstream controller
- parent_folder = self.get_library_folder( trans, folder_id, check_ownership=False, check_accessible=False )
- except Exception, e:
- return str( e )
+ decoded_parent_folder_id = trans.security.decode_id( encoded_parent_folder_id )
+ except:
+ raise MessageException( "Malformed folder id ( %s ) specified, unable to decode" % ( str( id ) ), type='error' )
- real_parent_folder_id = trans.security.encode_id( parent_folder.id )
-
- # Note that this will only create a folder; the library_contents will
- # also allow files to be generated, though that encompasses generic
- # contents:
- # TODO: reference or change create_folder, which points to the
- # library browsing; we just want to point to the /api/folders URI.
- status, output = trans.webapp.controllers['library_common'].create_folder( trans, 'api', real_parent_folder_id, '', **payload )
+ # TODO: refactor the functionality for use here instead of calling another controller
+ params = dict( [ ( "name", name ), ( "description", description ) ] )
+ status, output = trans.webapp.controllers['library_common'].create_folder( trans, 'api', encoded_parent_folder_id, '', **params )
rval = []
-
- # SM: When a folder is successfully created:
- # - get all of the created folders. We know that they're
- # folders, so prepend an "F" to them.
if 200 == status:
for k, v in output.items():
if type( v ) == trans.app.model.LibraryDatasetDatasetAssociation:
v = v.library_dataset
encoded_id = 'F' + trans.security.encode_id( v.id )
rval.append( dict( id = encoded_id,
- name = v.name,
- url = url_for( 'folder', id=encoded_id ) ) )
+ name = v.name ) )
else:
-# log.debug( "Error creating folder; setting output and status" )
trans.response.status = status
rval = output
return rval
@@ -105,12 +116,9 @@
"""
pass
- # TODO: Move this to library_common. This doesn't really belong in any
- # of the other base controllers.
- def __decode_library_content_id( self, trans, content_id ):
- if ( len( content_id ) % 16 == 0 ):
- return 'LibraryDataset', content_id
- elif ( content_id.startswith( 'F' ) ):
- return 'LibraryFolder', content_id[1:]
+ def __cut_the_prefix(self, encoded_id):
+ if ( len( encoded_id ) == 17 and encoded_id.startswith( 'F' ) ):
+ return encoded_id[-16:]
else:
- raise HTTPBadRequest( 'Malformed library content id ( %s ) specified, unable to decode.' % str( content_id ) )
+ return encoded_id
+
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -1,13 +1,13 @@
"""
-API operations on a library.
+API operations on a data library.
"""
-import logging
from galaxy import util
from galaxy import web
from galaxy.model.orm import and_, not_, or_
from galaxy.web.base.controller import BaseAPIController, url_for
from paste.httpexceptions import HTTPBadRequest, HTTPForbidden
+import logging
log = logging.getLogger( __name__ )
class LibrariesController( BaseAPIController ):
@@ -17,18 +17,17 @@
"""
index( self, trans, deleted='False', **kwd )
* GET /api/libraries:
- returns a list of summary data for libraries
+ Returns a list of summary data for all libraries that are ``non-deleted``
* GET /api/libraries/deleted:
- returns a list of summary data for deleted libraries
+ Returns a list of summary data for ``deleted`` libraries.
+ :param deleted: if True, show only deleted libraries, if False, ``non-deleted``
:type deleted: boolean
- :param deleted: if True, show only deleted libraries, if False, non-deleted
:rtype: list
:returns: list of dictionaries containing library information
.. seealso:: :attr:`galaxy.model.Library.dict_collection_visible_keys`
"""
-# log.debug( "LibrariesController.index: enter" )
query = trans.sa_session.query( trans.app.model.Library )
deleted = util.string_as_bool( deleted )
if deleted:
@@ -47,34 +46,33 @@
trans.model.LibraryPermissions.table.c.role_id.in_( current_user_role_ids ) ) ) ]
query = query.filter( or_( not_( trans.model.Library.table.c.id.in_( restricted_library_ids ) ),
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
- rval = []
+ libraries = []
for library in query:
item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
item['id'] = 'F' + trans.security.encode_id( item['id'] )
item['root_folder_id'] = 'F' + trans.security.encode_id( item['root_folder_id'] )
- rval.append( item )
- return rval
+ libraries.append( item )
+ return libraries
@web.expose_api
def show( self, trans, id, deleted='False', **kwd ):
"""
show( self, trans, id, deleted='False', **kwd )
- * GET /api/libraries/{id}:
+ * GET /api/libraries/{encoded_id}:
returns detailed information about a library
- * GET /api/libraries/deleted/{id}:
- returns detailed information about a deleted library
+ * GET /api/libraries/deleted/{encoded_id}:
+ returns detailed information about a ``deleted`` library
+ :param id: the encoded id of the library
:type id: an encoded id string
- :param id: the encoded id of the library
+ :param deleted: if True, allow information on a ``deleted`` library
:type deleted: boolean
- :param deleted: if True, allow information on a deleted library
:rtype: dictionary
:returns: detailed library information
.. seealso:: :attr:`galaxy.model.Library.dict_element_visible_keys`
"""
-# log.debug( "LibraryContentsController.show: enter" )
library_id = id
deleted = util.string_as_bool( deleted )
try:
@@ -89,7 +87,6 @@
if not library or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library( trans.get_current_user_roles(), library ) ):
raise HTTPBadRequest( detail='Invalid library id ( %s ) specified.' % id )
item = library.to_dict( view='element' )
- #item['contents_url'] = url_for( 'contents', library_id=library_id )
item['contents_url'] = url_for( 'library_contents', library_id=library_id )
return item
@@ -98,18 +95,17 @@
"""
create( self, trans, payload, **kwd )
* POST /api/libraries:
- create a new library
+ Creates a new library. Only ``name`` parameter is required.
.. note:: Currently, only admin users can create libraries.
- :type payload: dict
:param payload: (optional) dictionary structure containing::
'name': the new library's name
'description': the new library's description
'synopsis': the new library's synopsis
+ :type payload: dict
:rtype: dict
- :returns: a dictionary containing the id, name, and 'show' url
- of the new library
+ :returns: a dictionary containing the encoded_id, name, description, synopsis, and 'show' url of the new library
"""
if not trans.user_is_admin():
raise HTTPForbidden( detail='You are not authorized to create a new library.' )
@@ -127,13 +123,30 @@
trans.sa_session.add_all( ( library, root_folder ) )
trans.sa_session.flush()
encoded_id = trans.security.encode_id( library.id )
- rval = {}
- rval['url'] = url_for( 'library', id=encoded_id )
- rval['name'] = name
- rval['id'] = encoded_id
- return rval
+ new_library = {}
+ new_library['url'] = url_for( 'library', id=encoded_id )
+ new_library['name'] = name
+ new_library['description'] = description
+ new_library['synopsis'] = synopsis
+ new_library['id'] = encoded_id
+ return new_library
- def edit( self, trans, payload, **kwd ):
+ def edit( self, trans, encoded_id, payload, **kwd ):
+ """
+ * PUT /api/libraries/{encoded_id}
+ Updates the library defined by an ``encoded_id`` with the data in the payload.
+
+ .. note:: Currently, only admin users can edit libraries.
+
+ :param payload: (required) dictionary structure containing::
+ 'name': new library's name
+ 'description': new library's description
+ 'synopsis': new library's synopsis
+ :type payload: dict
+
+ :rtype: dict
+ :returns: a dictionary containing the encoded_id, name, description, synopsis, and 'show' url of the updated library
+ """
return "Not implemented yet"
@web.expose_api
@@ -142,10 +155,11 @@
delete( self, trans, id, **kwd )
* DELETE /api/histories/{id}
mark the library with the given ``id`` as deleted
+
.. note:: Currently, only admin users can delete libraries.
+ :param id: the encoded id of the library to delete
:type id: str
- :param id: the encoded id of the library to delete
:rtype: dictionary
:returns: detailed library information
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -184,6 +184,12 @@
action='download',
conditions=dict( method=[ "POST", "GET" ] ) )
+ webapp.mapper.connect( 'create_folder',
+ '/api/folders/:encoded_parent_folder_id',
+ controller='folders',
+ action='create',
+ conditions=dict( method=[ "POST" ] ) )
+
webapp.mapper.resource_with_deleted( 'library',
'libraries',
path_prefix='/api' )
@@ -196,7 +202,8 @@
controller='folder_contents',
name_prefix='folder_',
path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
+ parent_resources=dict( member_name='folder', collection_name='folders' ),
+ conditions=dict( method=[ "GET" ] ) )
webapp.mapper.resource( 'content',
'contents',
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -23,6 +23,11 @@
urlRoot: '/api/libraries'
});
+ // FOLDER AS MODEL
+ var FolderAsModel = Backbone.Model.extend({
+ urlRoot: '/api/folders'
+ });
+
// LIBRARIES
var Libraries = Backbone.Collection.extend({
url: '/api/libraries',
@@ -34,7 +39,7 @@
urlRoot : '/api/libraries/datasets'
})
- // FOLDER
+ // FOLDER AS COLLECTION
var Folder = Backbone.Collection.extend({
model: Item
})
@@ -316,7 +321,7 @@
'click #toolbtn_bulk_import' : 'modalBulkImport',
'click #toolbtn_dl' : 'bulkDownload',
'click .library-dataset' : 'showDatasetDetails',
- 'click #toolbtn_create_folder' : 'createFolderModal',
+ 'click #toolbtn_create_folder' : 'createFolderFromModal',
'click .btn_open_folder' : 'navigateToFolder'
},
@@ -407,15 +412,15 @@
// make modal
var self = this;
this.modal = new mod_modal.GalaxyModal({
+ destructible : true,
title : 'Dataset Details',
body : template,
buttons : {
'Import' : function() { self.importCurrentIntoHistory() },
'Download' : function() { self.downloadCurrent() },
- 'Close' : function() { self.modal.hide(); $('.modal').remove(); self.modal = null; } // TODO refill nicely modal with data
+ 'Close' : function() { self.modal.hideOrDestroy; self.modal = null; }
}
});
- this.modal.bindEvents(event);
$(".peek").html(item.get("peek"));
var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
@@ -426,6 +431,7 @@
$(this.modal.elMain).find('#dataset_import_single').val(self.lastSelectedHistory);
}
+ this.modal.bindEvents();
// show the prepared modal
this.modal.show();
},
@@ -563,14 +569,15 @@
// make modal
var history_modal_tmpl = _.template(self.templateBulkImportInModal(), {histories : histories.models});
self.modal = new mod_modal.GalaxyModal({
+ destructible : true,
title : 'Import into History',
body : history_modal_tmpl,
buttons : {
'Import' : function() {self.importAllIntoHistory()},
- 'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
+ 'Close' : function() {self.modal.hideOrDestroy; self.modal = null;}
}
});
- self.modal.bindEvents(event);
+ self.modal.bindEvents();
// show the prepared modal
self.modal.show();
}
@@ -693,9 +700,75 @@
},
// shows modal for creating folder
- createFolderModal: function(){
- mod_toastr.info('This will create folder...in the future');
- }
+ createFolderFromModal: function(){
+ event.preventDefault();
+ event.stopPropagation();
+
+ // create modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal({
+ destructible : true,
+ title : 'Create New Folder',
+ body : this.template_new_folder(),
+ buttons : {
+ 'Create' : function() {self.create_new_folder_event()},
+ 'Close' : function() {self.modal.destroy(); self.modal = null;}
+ }
+ });
+ this.modal.bindEvents();
+ // show prepared modal
+ this.modal.show();
+ },
+
+ // create the new folder from modal
+ create_new_folder_event: function(){
+ var folderDetails = this.serialize_new_folder();
+ if (this.validate_new_folder(folderDetails)){
+ var folder = new FolderAsModel();
+ folder.url = folder.urlRoot + '/F2fdbd5c5858e78fb' //load real ID
+ var self = this;
+ folder.save(folderDetails, {
+ success: function (folder) {
+ self.modal.destroy();
+ mod_toastr.success('Folder created');
+ self.render({id: 'F2fdbd5c5858e78fb'}); //put real id
+ },
+ error: function(){
+ mod_toastr.error('An error occured :(');
+ }
+ });
+ } else {
+ mod_toastr.error('Folder\'s name is missing');
+ }
+ return false;
+ },
+
+ // serialize data from the form
+ serialize_new_folder : function(){
+ return {
+ name: $("input[name='Name']").val(),
+ description: $("input[name='Description']").val()
+ };
+ },
+
+ // validate new library info
+ validate_new_folder: function(folderDetails){
+ return folderDetails.name !== '';
+ },
+
+ // template for new library modal
+ template_new_folder: function(){
+ tmpl_array = [];
+
+ tmpl_array.push('<div id="new_folder_modal">');
+ tmpl_array.push('<form>');
+ tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');
+ tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');
+ tmpl_array.push('</form>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ }
});
@@ -786,16 +859,16 @@
// create modal
var self = this;
- this.modal = new mod_modal.GalaxyModal(
- {
+ this.modal = new mod_modal.GalaxyModal({
+ destructible : true,
title : 'Create New Library',
body : this.template_new_library(),
buttons : {
'Create' : function() {self.create_new_library_event()},
- 'Close' : function() {self.modal.hide()}
+ 'Close' : function() {self.modal.hideOrDestroy(); self.modal = null;}
}
});
-
+ this.modal.bindEvents();
// show prepared modal
this.modal.show();
},
@@ -808,7 +881,7 @@
var self = this;
library.save(libraryDetails, {
success: function (library) {
- self.modal.hide();
+ self.modal.destroy();
self.clear_library_modal();
self.render();
mod_toastr.success('Library created');
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -18,7 +18,9 @@
},
// options
- options : {},
+ options : {
+ destructible: false // by default the modal cannot be removed by theself.destroy() method
+ },
// initialize
initialize : function(options) {
@@ -27,16 +29,23 @@
this.create(options);
},
+ hideOrDestroy: function(){
+ if (this.options.destructible){
+ self.destroy();
+ } else {
+ self.hide();
+ }
+ },
+
// bind the click-to-hide function
- bindEvents: function(event) {
- // bind the ESC key to hide() function
+ bindEvents: function() {
+ // bind the ESC key to hideOrDestroy() function
$(document).on('keyup', function(event){
- if (event.keyCode == 27) { self.hide(); $('.modal').remove();}
+ if (event.keyCode == 27) { self.hideOrDestroy() }
})
- // bind the 'click anywhere' to hide() function...
- $('html').on('click', function(event){
- self.hide();
- $('.modal').remove();
+ // bind the 'click anywhere' to hideOrDestroy() function...
+ $('html').on('click', function(event){
+ self.hideOrDestroy()
})
// ...but don't hide if the click is on modal content
$('.modal-content').on('click', function(event){
@@ -45,28 +54,20 @@
},
// unbind the click-to-hide function
- unbindEvents: function(event){
- // bind the ESC key to hide() function
+ unbindEvents: function(){
+ // unbind the ESC key to hideOrDestroy() function
$(document).off('keyup', function(event){
- if (event.keyCode == 27) { self.hide(); $('.modal').remove();}
+ if (event.keyCode == 27) { self.hideOrDestroy() }
})
- // unbind the 'click anywhere' to hide() function...
+ // unbind the 'click anywhere' to hideOrDestroy() function...
$('html').off('click', function(event){
- self.hide();
- $('.modal').remove();
+ self.hideOrDestroy()
})
$('.modal-content').off('click', function(event){
event.stopPropagation();
})
},
- // destroy
- destroy : function(){
- this.hide();
- this.unbindEvents();
- $('.modal').remove();
- },
-
// adds and displays a new frame/window
show: function(options) {
// create
@@ -102,6 +103,16 @@
// set flag
this.visible = false;
this.unbindEvents();
+ },
+
+ // destroy modal
+ destroy: function(){
+ // set flag
+ this.visible = false;
+ this.unbindEvents();
+
+ // remove
+ this.$el.remove();
},
// create
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d static/scripts/packed/galaxy.library.js
--- a/static/scripts/packed/galaxy.library.js
+++ b/static/scripts/packed/galaxy.library.js
@@ -1,1 +1,1 @@
-var view=null;var library_router=null;var responses=[];define(["galaxy.modal","galaxy.masthead","utils/galaxy.utils","libs/toastr"],function(j,k,g,m){var e=Backbone.Model.extend({urlRoot:"/api/libraries"});var n=Backbone.Collection.extend({url:"/api/libraries",model:e});var h=Backbone.Model.extend({urlRoot:"/api/libraries/datasets"});var c=Backbone.Collection.extend({model:h});var d=Backbone.Model.extend({defaults:{folder:new c(),full_path:"unknown",urlRoot:"/api/folders/",id:"unknown"},parse:function(q){this.full_path=q[0].full_path;this.get("folder").reset(q[1].folder_contents);return q}});var b=Backbone.Model.extend({urlRoot:"/api/histories/"});var i=Backbone.Model.extend({url:"/api/histories/"});var o=Backbone.Collection.extend({url:"/api/histories",model:i});var p=Backbone.Router.extend({routes:{"":"libraries","folders/:id":"folder_content","folders/:folder_id/download/:format":"download"}});var l=Backbone.View.extend({el:"#center",progress:0,progressStep:1,lastSelectedHistory:"",modal:null,folders:null,initialize:function(){this.folders=[];this.queue=jQuery.Deferred();this.queue.resolve()},templateFolder:function(){var q=[];q.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');q.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');q.push('<div id="library_folder_toolbar" >');q.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-plus"></span><span class="fa fa-folder-close"></span> folder</button>');q.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-external-link"></span> to history</button>');q.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');q.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');q.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');q.push(" </button>");q.push(' <ul class="dropdown-menu" role="menu">');q.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');q.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');q.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');q.push(" </ul>");q.push(" </div>");q.push("</div>");q.push('<div class="library_breadcrumb">');q.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');q.push("<% _.each(path, function(path_item) { %>");q.push("<% if (path_item[0] != id) { %>");q.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');q.push("<% } else { %>");q.push('<span title="You are in this folder"><%- path_item[1] %></span>');q.push("<% } %>");q.push("<% }); %>");q.push("</div>");q.push('<table id="folder_table" class="table table-condensed">');q.push(" <thead>");q.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');q.push(' <th class="button_heading">view</th>');q.push(" <th>name</th>");q.push(" <th>data type</th>");q.push(" <th>size</th>");q.push(" <th>date</th>");q.push(" </thead>");q.push(" <tbody>");q.push(" <td></td>");q.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');q.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');q.push(" <td></td>");q.push(" <td></td>");q.push(" <td></td>");q.push(" <td></td>");q.push(" </tr>");q.push(" <% _.each(items, function(content_item) { %>");q.push(' <tr class="folder_row light" id="<%- content_item.id %>">');q.push(' <% if (content_item.get("type") === "folder") { %>');q.push(" <td></td>");q.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');q.push(' <span class="fa fa-folder-open"></span> browse</td>');q.push(' <td><%- content_item.get("name") %>');q.push(' <% if (content_item.get("item_count") === 0) { %>');q.push(' <span class="muted">(empty folder)</span>');q.push(" <% } %>");q.push(" </td>");q.push(" <td>folder</td>");q.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>');q.push(" <% } else { %>");q.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');q.push(" <td>");q.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');q.push(' <span class="fa fa-eye"></span> details');q.push(" </button>");q.push(" </td>");q.push(' <td><%- content_item.get("name") %></td>');q.push(' <td><%= _.escape(content_item.get("data_type")) %></td>');q.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>');q.push(" <% } %> ");q.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>');q.push(" </tr>");q.push(" <% }); %>");q.push(" ");q.push(" </tbody>");q.push("</table>");q.push("</div>");return q.join("")},templateDatasetModal:function(){var q=[];q.push('<div id="dataset_info_modal">');q.push(' <table class="table table-striped table-condensed">');q.push(" <tr>");q.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');q.push(' <td><%= _.escape(item.get("name")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Data type</th>');q.push(' <td><%= _.escape(item.get("data_type")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Genome build</th>');q.push(' <td><%= _.escape(item.get("genome_build")) %></td>');q.push(" </tr>");q.push(' <th scope="row">Size</th>');q.push(" <td><%= _.escape(size) %></td>");q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Date uploaded</th>');q.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Uploaded by</th>');q.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');q.push(" </tr>");q.push(' <tr scope="row">');q.push(' <th scope="row">Data Lines</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');q.push(" </tr>");q.push(' <th scope="row">Comment Lines</th>');q.push(' <% if (item.get("metadata_comment_lines") === "") { %>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');q.push(" <% } else { %>");q.push(' <td scope="row">unknown</td>');q.push(" <% } %>");q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Number of Columns</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Column Types</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Miscellaneous information</th>');q.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');q.push(" </tr>");q.push(" </table>");q.push(' <pre class="peek">');q.push(" </pre>");q.push("</div>");return q.join("")},templateHistorySelectInModal:function(){var q=[];q.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');q.push("Select history: ");q.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');q.push(" <% _.each(histories, function(history) { %>");q.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');q.push(" <% }); %>");q.push("</select>");q.push("</span>");return q.join("")},templateBulkImportInModal:function(){var q=[];q.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');q.push("Select history: ");q.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');q.push(" <% _.each(histories, function(history) { %>");q.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');q.push(" <% }); %>");q.push("</select>");q.push("</span>");return q.join("")},size_to_string:function(q){var r="";if(q>=100000000000){q=q/100000000000;r="TB"}else{if(q>=100000000){q=q/100000000;r="GB"}else{if(q>=100000){q=q/100000;r="MB"}else{if(q>=100){q=q/100;r="KB"}else{q=q*10;r="b"}}}}return(Math.round(q)/10)+r},events:{"click #select-all-checkboxes":"selectAll","click .folder_row":"selectClickedRow","click #toolbtn_bulk_import":"modalBulkImport","click #toolbtn_dl":"bulkDownload","click .library-dataset":"showDatasetDetails","click #toolbtn_create_folder":"createFolderModal","click .btn_open_folder":"navigateToFolder"},render:function(q){$("#center").css("overflow","auto");view=this;var s=this;var r=new d({id:q.id});r.url=r.attributes.urlRoot+q.id+"/contents";r.fetch({success:function(t){for(var v=0;v<r.attributes.folder.models.length;v++){var u=r.attributes.folder.models[v];if(u.get("type")==="file"){u.set("readable_size",s.size_to_string(u.get("file_size")))}}var x=r.full_path;var y;if(x.length===1){y=0}else{y=x[x.length-2][0]}var w=_.template(s.templateFolder(),{path:r.full_path,items:r.attributes.folder.models,id:q.id,upper_folder_id:y});s.$el.html(w)}})},navigateToFolder:function(r){var q=$(r.target).attr("data-id");if(typeof q==="undefined"){return false}else{if(q==="0"){library_router.navigate("#",{trigger:true,replace:true})}else{library_router.navigate("folders/"+q,{trigger:true,replace:true})}}},showDatasetDetails:function(t){t.preventDefault();var u=$(t.target).parent().parent().attr("id");var s=new h();var r=new o();s.id=u;var q=this;s.fetch({success:function(v){r.fetch({success:function(w){q.renderModalAfterFetch(v,w)}})}})},renderModalAfterFetch:function(v,s){var t=this.size_to_string(v.get("file_size"));var u=_.template(this.templateDatasetModal(),{item:v,size:t});this.modal=null;var r=this;this.modal=new j.GalaxyModal({title:"Dataset Details",body:u,buttons:{Import:function(){r.importCurrentIntoHistory()},Download:function(){r.downloadCurrent()},Close:function(){r.modal.hide();$(".modal").remove();r.modal=null}}});this.modal.bindEvents(event);$(".peek").html(v.get("peek"));var q=_.template(this.templateHistorySelectInModal(),{histories:s.models});$(this.modal.elMain).find(".buttons").prepend(q);if(r.lastSelectedHistory.length>0){$(this.modal.elMain).find("#dataset_import_single").val(r.lastSelectedHistory)}this.modal.show()},downloadCurrent:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var q=[];q.push($("#id_row").attr("data-id"));var r="/api/libraries/datasets/download/uncompressed";var s={ldda_ids:q};folderContentView.processDownload(r,s);this.modal.enableButton("Import");this.modal.enableButton("Download")},importCurrentIntoHistory:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var s=$(this.modal.elMain).find("select[name=dataset_import_single] option:selected").val();this.lastSelectedHistory=s;var q=$("#id_row").attr("data-id");var t=new b();var r=this;t.url=t.urlRoot+s+"/contents";t.save({content:q,source:"library"},{success:function(){m.success("Dataset imported");r.modal.enableButton("Import");r.modal.enableButton("Download")},error:function(){m.error("An error occured! Dataset not imported. Please try again.");r.modal.enableButton("Import");r.modal.enableButton("Download")}})},selectAll:function(r){var q=r.target.checked;that=this;$(":checkbox").each(function(){this.checked=q;$row=$(this.parentElement.parentElement);(q)?that.makeDarkRow($row):that.makeWhiteRow($row)});this.checkTools()},selectClickedRow:function(r){var t="";var q;var s;if(r.target.localName==="input"){t=r.target;q=$(r.target.parentElement.parentElement);s="input"}else{if(r.target.localName==="td"){t=$("#"+r.target.parentElement.id).find(":checkbox")[0];q=$(r.target.parentElement);s="td"}}if(t===""){r.stopPropagation();return}if(t===undefined){r.stopPropagation();return}if(t.checked){if(s==="td"){t.checked="";this.makeWhiteRow(q)}else{if(s==="input"){this.makeDarkRow(q)}}}else{if(s==="td"){t.checked="selected";this.makeDarkRow(q)}else{if(s==="input"){this.makeWhiteRow(q)}}}this.checkTools()},makeDarkRow:function(q){q.removeClass("light");q.find("a").removeClass("light");q.addClass("dark");q.find("a").addClass("dark")},makeWhiteRow:function(q){q.removeClass("dark");q.find("a").removeClass("dark");q.addClass("light");q.find("a").addClass("light")},checkTools:function(){var q=$("#folder_table").find(":checked");if(q.length>0){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}else{$("#toolbtn_bulk_import").hide();$("#toolbtn_dl").hide()}},modalBulkImport:function(){var r=this;var q=new o();q.fetch({success:function(s){var t=_.template(r.templateBulkImportInModal(),{histories:s.models});r.modal=new j.GalaxyModal({title:"Import into History",body:t,buttons:{Import:function(){r.importAllIntoHistory()},Close:function(){r.modal.hide();$(".modal").remove();r.modal=null}}});r.modal.bindEvents(event);r.modal.show()}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var s=$("select[name=dataset_import_bulk] option:selected").val();var w=$("select[name=dataset_import_bulk] option:selected").text();var y=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){y.push(this.parentElement.parentElement.id)}});var x=_.template(this.templateProgressBar(),{history_name:w});$(this.modal.elMain).find(".modal-body").html(x);var t=100/y.length;this.initProgress(t);var q=[];for(var r=y.length-1;r>=0;r--){library_dataset_id=y[r];var u=new b();var v=this;u.url=u.urlRoot+s+"/contents";u.content=library_dataset_id;u.source="library";q.push(u)}this.chainCall(q)},chainCall:function(r){var q=this;var s=r.pop();if(typeof s==="undefined"){m.success("All datasets imported");this.modal.destroy();return}var t=$.when(s.save({content:s.content,source:s.source})).done(function(u){q.updateProgress();responses.push(u);q.chainCall(r)})},initProgress:function(q){this.progress=0;this.progressStep=q},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},templateProgressBar:function(){var q=[];q.push('<div class="import_text">');q.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");q.push("</div>");q.push('<div class="progress">');q.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');q.push(' <span class="completion_span">0% Complete</span>');q.push(" </div>");q.push("</div>");q.push("");return q.join("")},download:function(q,u){var s=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){s.push(this.parentElement.parentElement.id)}});var r="/api/libraries/datasets/download/"+u;var t={ldda_ids:s};this.processDownload(r,t,"get")},processDownload:function(r,s,t){if(r&&s){s=typeof s=="string"?s:$.param(s);var q="";$.each(s.split("&"),function(){var u=this.split("=");q+='<input type="hidden" name="'+u[0]+'" value="'+u[1]+'" />'});$('<form action="'+r+'" method="'+(t||"post")+'">'+q+"</form>").appendTo("body").submit().remove();m.info("Your download will begin soon")}},createFolderModal:function(){m.info("This will create folder...in the future")}});var a=Backbone.View.extend({el:"#center",events:{"click #create_new_library_btn":"show_library_modal"},initialize:function(){},template_library_list:function(){tmpl_array=[];tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');tmpl_array.push("");tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary file ">New Library</a>');tmpl_array.push('<table class="table table-condensed">');tmpl_array.push(" <thead>");tmpl_array.push(' <th class="button_heading"></th>');tmpl_array.push(" <th>name</th>");tmpl_array.push(" <th>description</th>");tmpl_array.push(" <th>synopsis</th> ");tmpl_array.push(" <th>model type</th> ");tmpl_array.push(" </thead>");tmpl_array.push(" <tbody>");tmpl_array.push(" <% _.each(libraries, function(library) { %>");tmpl_array.push(" <tr>");tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');tmpl_array.push(' <td><%- library.get("name") %></td>');tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');tmpl_array.push(" </tr>");tmpl_array.push(" <% }); %>");tmpl_array.push(" </tbody>");tmpl_array.push("</table>");tmpl_array.push("</div>");return tmpl_array.join("")},render:function(){$("#center").css("overflow","auto");var q=this;libraries=new n();libraries.fetch({success:function(r){var s=_.template(q.template_library_list(),{libraries:r.models});q.$el.html(s)},error:function(s,r){if(r.statusCode().status===403){m.error("Please log in first. Redirecting to login page in 3s.");setTimeout(q.redirectToLogin,3000)}else{m.error("An error occured. Please try again.")}}})},redirectToHome:function(){window.location="../"},redirectToLogin:function(){window.location="/user/login"},modal:null,show_library_modal:function(r){r.preventDefault();r.stopPropagation();var q=this;this.modal=new j.GalaxyModal({title:"Create New Library",body:this.template_new_library(),buttons:{Create:function(){q.create_new_library_event()},Close:function(){q.modal.hide()}}});this.modal.show()},create_new_library_event:function(){var s=this.serialize_new_library();if(this.validate_new_library(s)){var r=new e();var q=this;r.save(s,{success:function(t){q.modal.hide();q.clear_library_modal();q.render();m.success("Library created")},error:function(){m.error("An error occured :(")}})}else{m.error("Library's name is missing")}return false},clear_library_modal:function(){$("input[name='Name']").val("");$("input[name='Description']").val("");$("input[name='Synopsis']").val("")},serialize_new_library:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val(),synopsis:$("input[name='Synopsis']").val()}},validate_new_library:function(q){return q.name!==""},template_new_library:function(){tmpl_array=[];tmpl_array.push('<div id="new_library_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var f=Backbone.View.extend({folderContentView:null,galaxyLibraryview:null,initialize:function(){folderContentView=new l();galaxyLibraryview=new a();library_router=new p();library_router.on("route:libraries",function(){galaxyLibraryview.render()});library_router.on("route:folder_content",function(q){folderContentView.render({id:q})});library_router.on("route:download",function(q,r){if($("#center").find(":checked").length===0){library_router.navigate("folders/"+q,{trigger:true,replace:true})}else{folderContentView.download(q,r);library_router.navigate("folders/"+q,{trigger:false,replace:true})}});Backbone.history.start();return this}});return{GalaxyApp:f}});
\ No newline at end of file
+var view=null;var library_router=null;var responses=[];define(["galaxy.modal","galaxy.masthead","utils/galaxy.utils","libs/toastr"],function(k,l,h,n){var f=Backbone.Model.extend({urlRoot:"/api/libraries"});var c=Backbone.Model.extend({urlRoot:"/api/folders"});var o=Backbone.Collection.extend({url:"/api/libraries",model:f});var i=Backbone.Model.extend({urlRoot:"/api/libraries/datasets"});var d=Backbone.Collection.extend({model:i});var e=Backbone.Model.extend({defaults:{folder:new d(),full_path:"unknown",urlRoot:"/api/folders/",id:"unknown"},parse:function(r){this.full_path=r[0].full_path;this.get("folder").reset(r[1].folder_contents);return r}});var b=Backbone.Model.extend({urlRoot:"/api/histories/"});var j=Backbone.Model.extend({url:"/api/histories/"});var p=Backbone.Collection.extend({url:"/api/histories",model:j});var q=Backbone.Router.extend({routes:{"":"libraries","folders/:id":"folder_content","folders/:folder_id/download/:format":"download"}});var m=Backbone.View.extend({el:"#center",progress:0,progressStep:1,lastSelectedHistory:"",modal:null,folders:null,initialize:function(){this.folders=[];this.queue=jQuery.Deferred();this.queue.resolve()},templateFolder:function(){var r=[];r.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');r.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');r.push('<div id="library_folder_toolbar" >');r.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-plus"></span><span class="fa fa-folder-close"></span> folder</button>');r.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-external-link"></span> to history</button>');r.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');r.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');r.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');r.push(" </button>");r.push(' <ul class="dropdown-menu" role="menu">');r.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');r.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');r.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');r.push(" </ul>");r.push(" </div>");r.push("</div>");r.push('<div class="library_breadcrumb">');r.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');r.push("<% _.each(path, function(path_item) { %>");r.push("<% if (path_item[0] != id) { %>");r.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');r.push("<% } else { %>");r.push('<span title="You are in this folder"><%- path_item[1] %></span>');r.push("<% } %>");r.push("<% }); %>");r.push("</div>");r.push('<table id="folder_table" class="table table-condensed">');r.push(" <thead>");r.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');r.push(' <th class="button_heading">view</th>');r.push(" <th>name</th>");r.push(" <th>data type</th>");r.push(" <th>size</th>");r.push(" <th>date</th>");r.push(" </thead>");r.push(" <tbody>");r.push(" <td></td>");r.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');r.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');r.push(" <td></td>");r.push(" <td></td>");r.push(" <td></td>");r.push(" <td></td>");r.push(" </tr>");r.push(" <% _.each(items, function(content_item) { %>");r.push(' <tr class="folder_row light" id="<%- content_item.id %>">');r.push(' <% if (content_item.get("type") === "folder") { %>');r.push(" <td></td>");r.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');r.push(' <span class="fa fa-folder-open"></span> browse</td>');r.push(' <td><%- content_item.get("name") %>');r.push(' <% if (content_item.get("item_count") === 0) { %>');r.push(' <span class="muted">(empty folder)</span>');r.push(" <% } %>");r.push(" </td>");r.push(" <td>folder</td>");r.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>');r.push(" <% } else { %>");r.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');r.push(" <td>");r.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');r.push(' <span class="fa fa-eye"></span> details');r.push(" </button>");r.push(" </td>");r.push(' <td><%- content_item.get("name") %></td>');r.push(' <td><%= _.escape(content_item.get("data_type")) %></td>');r.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>');r.push(" <% } %> ");r.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>');r.push(" </tr>");r.push(" <% }); %>");r.push(" ");r.push(" </tbody>");r.push("</table>");r.push("</div>");return r.join("")},templateDatasetModal:function(){var r=[];r.push('<div id="dataset_info_modal">');r.push(' <table class="table table-striped table-condensed">');r.push(" <tr>");r.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');r.push(' <td><%= _.escape(item.get("name")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Data type</th>');r.push(' <td><%= _.escape(item.get("data_type")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Genome build</th>');r.push(' <td><%= _.escape(item.get("genome_build")) %></td>');r.push(" </tr>");r.push(' <th scope="row">Size</th>');r.push(" <td><%= _.escape(size) %></td>");r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Date uploaded</th>');r.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Uploaded by</th>');r.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');r.push(" </tr>");r.push(' <tr scope="row">');r.push(' <th scope="row">Data Lines</th>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');r.push(" </tr>");r.push(' <th scope="row">Comment Lines</th>');r.push(' <% if (item.get("metadata_comment_lines") === "") { %>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');r.push(" <% } else { %>");r.push(' <td scope="row">unknown</td>');r.push(" <% } %>");r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Number of Columns</th>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Column Types</th>');r.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');r.push(" </tr>");r.push(" <tr>");r.push(' <th scope="row">Miscellaneous information</th>');r.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');r.push(" </tr>");r.push(" </table>");r.push(' <pre class="peek">');r.push(" </pre>");r.push("</div>");return r.join("")},templateHistorySelectInModal:function(){var r=[];r.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');r.push("Select history: ");r.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');r.push(" <% _.each(histories, function(history) { %>");r.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');r.push(" <% }); %>");r.push("</select>");r.push("</span>");return r.join("")},templateBulkImportInModal:function(){var r=[];r.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');r.push("Select history: ");r.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');r.push(" <% _.each(histories, function(history) { %>");r.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');r.push(" <% }); %>");r.push("</select>");r.push("</span>");return r.join("")},size_to_string:function(r){var s="";if(r>=100000000000){r=r/100000000000;s="TB"}else{if(r>=100000000){r=r/100000000;s="GB"}else{if(r>=100000){r=r/100000;s="MB"}else{if(r>=100){r=r/100;s="KB"}else{r=r*10;s="b"}}}}return(Math.round(r)/10)+s},events:{"click #select-all-checkboxes":"selectAll","click .folder_row":"selectClickedRow","click #toolbtn_bulk_import":"modalBulkImport","click #toolbtn_dl":"bulkDownload","click .library-dataset":"showDatasetDetails","click #toolbtn_create_folder":"createFolderFromModal","click .btn_open_folder":"navigateToFolder"},render:function(r){$("#center").css("overflow","auto");view=this;var t=this;var s=new e({id:r.id});s.url=s.attributes.urlRoot+r.id+"/contents";s.fetch({success:function(u){for(var w=0;w<s.attributes.folder.models.length;w++){var v=s.attributes.folder.models[w];if(v.get("type")==="file"){v.set("readable_size",t.size_to_string(v.get("file_size")))}}var y=s.full_path;var z;if(y.length===1){z=0}else{z=y[y.length-2][0]}var x=_.template(t.templateFolder(),{path:s.full_path,items:s.attributes.folder.models,id:r.id,upper_folder_id:z});t.$el.html(x)}})},navigateToFolder:function(s){var r=$(s.target).attr("data-id");if(typeof r==="undefined"){return false}else{if(r==="0"){library_router.navigate("#",{trigger:true,replace:true})}else{library_router.navigate("folders/"+r,{trigger:true,replace:true})}}},showDatasetDetails:function(u){u.preventDefault();var v=$(u.target).parent().parent().attr("id");var t=new i();var s=new p();t.id=v;var r=this;t.fetch({success:function(w){s.fetch({success:function(x){r.renderModalAfterFetch(w,x)}})}})},renderModalAfterFetch:function(w,t){var u=this.size_to_string(w.get("file_size"));var v=_.template(this.templateDatasetModal(),{item:w,size:u});this.modal=null;var s=this;this.modal=new k.GalaxyModal({destructible:true,title:"Dataset Details",body:v,buttons:{Import:function(){s.importCurrentIntoHistory()},Download:function(){s.downloadCurrent()},Close:function(){s.modal.hideOrDestroy;s.modal=null}}});$(".peek").html(w.get("peek"));var r=_.template(this.templateHistorySelectInModal(),{histories:t.models});$(this.modal.elMain).find(".buttons").prepend(r);if(s.lastSelectedHistory.length>0){$(this.modal.elMain).find("#dataset_import_single").val(s.lastSelectedHistory)}this.modal.bindEvents();this.modal.show()},downloadCurrent:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var r=[];r.push($("#id_row").attr("data-id"));var s="/api/libraries/datasets/download/uncompressed";var t={ldda_ids:r};folderContentView.processDownload(s,t);this.modal.enableButton("Import");this.modal.enableButton("Download")},importCurrentIntoHistory:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var t=$(this.modal.elMain).find("select[name=dataset_import_single] option:selected").val();this.lastSelectedHistory=t;var r=$("#id_row").attr("data-id");var u=new b();var s=this;u.url=u.urlRoot+t+"/contents";u.save({content:r,source:"library"},{success:function(){n.success("Dataset imported");s.modal.enableButton("Import");s.modal.enableButton("Download")},error:function(){n.error("An error occured! Dataset not imported. Please try again.");s.modal.enableButton("Import");s.modal.enableButton("Download")}})},selectAll:function(s){var r=s.target.checked;that=this;$(":checkbox").each(function(){this.checked=r;$row=$(this.parentElement.parentElement);(r)?that.makeDarkRow($row):that.makeWhiteRow($row)});this.checkTools()},selectClickedRow:function(s){var u="";var r;var t;if(s.target.localName==="input"){u=s.target;r=$(s.target.parentElement.parentElement);t="input"}else{if(s.target.localName==="td"){u=$("#"+s.target.parentElement.id).find(":checkbox")[0];r=$(s.target.parentElement);t="td"}}if(u===""){s.stopPropagation();return}if(u===undefined){s.stopPropagation();return}if(u.checked){if(t==="td"){u.checked="";this.makeWhiteRow(r)}else{if(t==="input"){this.makeDarkRow(r)}}}else{if(t==="td"){u.checked="selected";this.makeDarkRow(r)}else{if(t==="input"){this.makeWhiteRow(r)}}}this.checkTools()},makeDarkRow:function(r){r.removeClass("light");r.find("a").removeClass("light");r.addClass("dark");r.find("a").addClass("dark")},makeWhiteRow:function(r){r.removeClass("dark");r.find("a").removeClass("dark");r.addClass("light");r.find("a").addClass("light")},checkTools:function(){var r=$("#folder_table").find(":checked");if(r.length>0){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}else{$("#toolbtn_bulk_import").hide();$("#toolbtn_dl").hide()}},modalBulkImport:function(){var s=this;var r=new p();r.fetch({success:function(t){var u=_.template(s.templateBulkImportInModal(),{histories:t.models});s.modal=new k.GalaxyModal({destructible:true,title:"Import into History",body:u,buttons:{Import:function(){s.importAllIntoHistory()},Close:function(){s.modal.hideOrDestroy;s.modal=null}}});s.modal.bindEvents();s.modal.show()}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var t=$("select[name=dataset_import_bulk] option:selected").val();var x=$("select[name=dataset_import_bulk] option:selected").text();var z=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){z.push(this.parentElement.parentElement.id)}});var y=_.template(this.templateProgressBar(),{history_name:x});$(this.modal.elMain).find(".modal-body").html(y);var u=100/z.length;this.initProgress(u);var r=[];for(var s=z.length-1;s>=0;s--){library_dataset_id=z[s];var v=new b();var w=this;v.url=v.urlRoot+t+"/contents";v.content=library_dataset_id;v.source="library";r.push(v)}this.chainCall(r)},chainCall:function(s){var r=this;var t=s.pop();if(typeof t==="undefined"){n.success("All datasets imported");this.modal.destroy();return}var u=$.when(t.save({content:t.content,source:t.source})).done(function(v){r.updateProgress();responses.push(v);r.chainCall(s)})},initProgress:function(r){this.progress=0;this.progressStep=r},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},templateProgressBar:function(){var r=[];r.push('<div class="import_text">');r.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");r.push("</div>");r.push('<div class="progress">');r.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');r.push(' <span class="completion_span">0% Complete</span>');r.push(" </div>");r.push("</div>");r.push("");return r.join("")},download:function(r,v){var t=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){t.push(this.parentElement.parentElement.id)}});var s="/api/libraries/datasets/download/"+v;var u={ldda_ids:t};this.processDownload(s,u,"get")},processDownload:function(s,t,u){if(s&&t){t=typeof t=="string"?t:$.param(t);var r="";$.each(t.split("&"),function(){var v=this.split("=");r+='<input type="hidden" name="'+v[0]+'" value="'+v[1]+'" />'});$('<form action="'+s+'" method="'+(u||"post")+'">'+r+"</form>").appendTo("body").submit().remove();n.info("Your download will begin soon")}},createFolderFromModal:function(){event.preventDefault();event.stopPropagation();var r=this;this.modal=new k.GalaxyModal({destructible:true,title:"Create New Folder",body:this.template_new_folder(),buttons:{Create:function(){r.create_new_folder_event()},Close:function(){r.modal.destroy();r.modal=null}}});this.modal.bindEvents();this.modal.show()},create_new_folder_event:function(){var r=this.serialize_new_folder();if(this.validate_new_folder(r)){var t=new c();t.url=t.urlRoot+"/F2fdbd5c5858e78fb";var s=this;t.save(r,{success:function(u){s.modal.destroy();n.success("Folder created");s.render({id:"F2fdbd5c5858e78fb"})},error:function(){n.error("An error occured :(")}})}else{n.error("Folder's name is missing")}return false},serialize_new_folder:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val()}},validate_new_folder:function(r){return r.name!==""},template_new_folder:function(){tmpl_array=[];tmpl_array.push('<div id="new_folder_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var a=Backbone.View.extend({el:"#center",events:{"click #create_new_library_btn":"show_library_modal"},initialize:function(){},template_library_list:function(){tmpl_array=[];tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');tmpl_array.push("");tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary file ">New Library</a>');tmpl_array.push('<table class="table table-condensed">');tmpl_array.push(" <thead>");tmpl_array.push(' <th class="button_heading"></th>');tmpl_array.push(" <th>name</th>");tmpl_array.push(" <th>description</th>");tmpl_array.push(" <th>synopsis</th> ");tmpl_array.push(" <th>model type</th> ");tmpl_array.push(" </thead>");tmpl_array.push(" <tbody>");tmpl_array.push(" <% _.each(libraries, function(library) { %>");tmpl_array.push(" <tr>");tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');tmpl_array.push(' <td><%- library.get("name") %></td>');tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');tmpl_array.push(" </tr>");tmpl_array.push(" <% }); %>");tmpl_array.push(" </tbody>");tmpl_array.push("</table>");tmpl_array.push("</div>");return tmpl_array.join("")},render:function(){$("#center").css("overflow","auto");var r=this;libraries=new o();libraries.fetch({success:function(s){var t=_.template(r.template_library_list(),{libraries:s.models});r.$el.html(t)},error:function(t,s){if(s.statusCode().status===403){n.error("Please log in first. Redirecting to login page in 3s.");setTimeout(r.redirectToLogin,3000)}else{n.error("An error occured. Please try again.")}}})},redirectToHome:function(){window.location="../"},redirectToLogin:function(){window.location="/user/login"},modal:null,show_library_modal:function(s){s.preventDefault();s.stopPropagation();var r=this;this.modal=new k.GalaxyModal({destructible:true,title:"Create New Library",body:this.template_new_library(),buttons:{Create:function(){r.create_new_library_event()},Close:function(){r.modal.hideOrDestroy();r.modal=null}}});this.modal.bindEvents();this.modal.show()},create_new_library_event:function(){var t=this.serialize_new_library();if(this.validate_new_library(t)){var s=new f();var r=this;s.save(t,{success:function(u){r.modal.destroy();r.clear_library_modal();r.render();n.success("Library created")},error:function(){n.error("An error occured :(")}})}else{n.error("Library's name is missing")}return false},clear_library_modal:function(){$("input[name='Name']").val("");$("input[name='Description']").val("");$("input[name='Synopsis']").val("")},serialize_new_library:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val(),synopsis:$("input[name='Synopsis']").val()}},validate_new_library:function(r){return r.name!==""},template_new_library:function(){tmpl_array=[];tmpl_array.push('<div id="new_library_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var g=Backbone.View.extend({folderContentView:null,galaxyLibraryview:null,initialize:function(){folderContentView=new m();galaxyLibraryview=new a();library_router=new q();library_router.on("route:libraries",function(){galaxyLibraryview.render()});library_router.on("route:folder_content",function(r){folderContentView.render({id:r})});library_router.on("route:download",function(r,s){if($("#center").find(":checked").length===0){library_router.navigate("folders/"+r,{trigger:true,replace:true})}else{folderContentView.download(r,s);library_router.navigate("folders/"+r,{trigger:false,replace:true})}});Backbone.history.start();return this}});return{GalaxyApp:g}});
\ No newline at end of file
diff -r ef98f48690fc9ce5d1b8f662a2e91f384514013d -r f2e3d2e41c1d2e90d089c1ac9ea4d626b620a94d static/scripts/packed/galaxy.modal.js
--- a/static/scripts/packed/galaxy.modal.js
+++ b/static/scripts/packed/galaxy.modal.js
@@ -1,1 +1,1 @@
-define([],function(){var a=Backbone.View.extend({elMain:"#everything",optionsDefault:{title:"galaxy-modal",body:"",backdrop:true,height:null,width:null},options:{},initialize:function(b){self=this;if(b){this.create(b)}},bindEvents:function(b){$(document).on("keyup",function(c){if(c.keyCode==27){self.hide();$(".modal").remove()}});$("html").on("click",function(c){self.hide();$(".modal").remove()});$(".modal-content").on("click",function(c){c.stopPropagation()})},unbindEvents:function(b){$(document).off("keyup",function(c){if(c.keyCode==27){self.hide();$(".modal").remove()}});$("html").off("click",function(c){self.hide();$(".modal").remove()});$(".modal-content").off("click",function(c){c.stopPropagation()})},destroy:function(){this.hide();this.unbindEvents();$(".modal").remove()},show:function(b){this.initialize(b);if(this.options.height){this.$body.css("height",this.options.height);this.$body.css("overflow","hidden")}else{this.$body.css("max-height",$(window).height()/2)}if(this.options.width){this.$dialog.css("width",this.options.width)}if(this.visible){this.$el.show()}else{this.$el.fadeIn("fast")}this.visible=true},hide:function(){this.$el.fadeOut("fast");this.visible=false;this.unbindEvents()},create:function(c){this.options=_.defaults(c,this.optionsDefault);if(this.options.body=="progress"){this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')}if(this.$el){this.$el.remove()}this.setElement(this.template(this.options.title));this.$dialog=(this.$el).find(".modal-dialog");this.$body=(this.$el).find(".modal-body");this.$footer=(this.$el).find(".modal-footer");this.$buttons=(this.$el).find(".buttons");this.$backdrop=(this.$el).find(".modal-backdrop");this.$body.html(this.options.body);if(!this.options.backdrop){this.$backdrop.removeClass("in")}if(this.options.buttons){var b=this;$.each(this.options.buttons,function(d,e){b.$buttons.append($('<button id="'+String(d).toLowerCase()+'"></button>').text(d).click(e)).append(" ")})}else{this.$footer.hide()}$(this.elMain).append($(this.el))},enableButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).prop("disabled",false)},disableButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).prop("disabled",true)},hideButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).hide()},showButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).show()},scrollTop:function(){return this.$body.scrollTop()},template:function(b){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+b+'</h4></div><div class="modal-body"></div><div class="modal-footer"><div class="buttons" style="float: right;"></div></div></div</div></div>'}});return{GalaxyModal:a}});
\ No newline at end of file
+define([],function(){var a=Backbone.View.extend({elMain:"#everything",optionsDefault:{title:"galaxy-modal",body:"",backdrop:true,height:null,width:null},options:{destructible:false},initialize:function(b){self=this;if(b){this.create(b)}},hideOrDestroy:function(){if(this.options.destructible){self.destroy()}else{self.hide()}},bindEvents:function(){$(document).on("keyup",function(b){if(b.keyCode==27){self.hideOrDestroy()}});$("html").on("click",function(b){self.hideOrDestroy()});$(".modal-content").on("click",function(b){b.stopPropagation()})},unbindEvents:function(){$(document).off("keyup",function(b){if(b.keyCode==27){self.hideOrDestroy()}});$("html").off("click",function(b){self.hideOrDestroy()});$(".modal-content").off("click",function(b){b.stopPropagation()})},show:function(b){this.initialize(b);if(this.options.height){this.$body.css("height",this.options.height);this.$body.css("overflow","hidden")}else{this.$body.css("max-height",$(window).height()/2)}if(this.options.width){this.$dialog.css("width",this.options.width)}if(this.visible){this.$el.show()}else{this.$el.fadeIn("fast")}this.visible=true},hide:function(){this.$el.fadeOut("fast");this.visible=false;this.unbindEvents()},destroy:function(){this.visible=false;this.unbindEvents();this.$el.remove()},create:function(c){this.options=_.defaults(c,this.optionsDefault);if(this.options.body=="progress"){this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')}if(this.$el){this.$el.remove()}this.setElement(this.template(this.options.title));this.$dialog=(this.$el).find(".modal-dialog");this.$body=(this.$el).find(".modal-body");this.$footer=(this.$el).find(".modal-footer");this.$buttons=(this.$el).find(".buttons");this.$backdrop=(this.$el).find(".modal-backdrop");this.$body.html(this.options.body);if(!this.options.backdrop){this.$backdrop.removeClass("in")}if(this.options.buttons){var b=this;$.each(this.options.buttons,function(d,e){b.$buttons.append($('<button id="'+String(d).toLowerCase()+'"></button>').text(d).click(e)).append(" ")})}else{this.$footer.hide()}$(this.elMain).append($(this.el))},enableButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).prop("disabled",false)},disableButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).prop("disabled",true)},hideButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).hide()},showButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).show()},scrollTop:function(){return this.$body.scrollTop()},template:function(b){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+b+'</h4></div><div class="modal-body"></div><div class="modal-footer"><div class="buttons" style="float: right;"></div></div></div</div></div>'}});return{GalaxyModal:a}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: History panel: fix positioning of title when both editing title and having search bar open; UI: add dropDownSelect and FilterControl components
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/ef98f48690fc/
Changeset: ef98f48690fc
User: carlfeberhard
Date: 2014-01-07 19:45:24
Summary: History panel: fix positioning of title when both editing title and having search bar open; UI: add dropDownSelect and FilterControl components
Affected #: 13 files
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/scripts/mvc/history/history-panel.js
--- a/static/scripts/mvc/history/history-panel.js
+++ b/static/scripts/mvc/history/history-panel.js
@@ -980,8 +980,9 @@
* clicking the clear button will clear the search
* uses searchInput in ui.js
*/
- renderSearchControls : function( $where ){
- var panel = this;
+ setUpSearchInput : function( $where ){
+ var panel = this,
+ inputSelector = '.history-search-input';
function onSearch( searchFor ){
//console.debug( 'onSearch', searchFor, panel );
@@ -996,10 +997,10 @@
onSearch( searchFor );
return;
}
- panel.$el.find( '.history-search-controls' ).searchInput( 'toggle-loading' );
+ panel.$el.find( inputSelector ).searchInput( 'toggle-loading' );
panel.model.hdas.fetchAllDetails({ silent: true })
.always( function(){
- panel.$el.find( '.history-search-controls' ).searchInput( 'toggle-loading' );
+ panel.$el.find( inputSelector ).searchInput( 'toggle-loading' );
})
.done( function(){
onSearch( searchFor );
@@ -1012,7 +1013,7 @@
panel.trigger( 'search:clear', panel );
panel.renderHdas();
}
- return $where.searchInput({
+ $where.searchInput({
initialVal : panel.searchFor,
name : 'history-search',
placeholder : 'search datasets',
@@ -1021,18 +1022,25 @@
onsearch : onSearch,
onclear : onSearchClear
});
+ return $where;
},
//TODO: to hidden/shown plugin
showSearchControls : function( speed ){
speed = ( speed === undefined )?( this.fxSpeed ):( speed );
var panel = this,
- $searchControls = this.$el.find( '.history-search-controls' );
+ $searchControls = this.$el.find( '.history-search-controls' ),
+ $input = $searchControls.find( '.history-search-input' );
// if it hasn't been rendered - do it now
- if( !$searchControls.children().size() ){
- $searchControls = this.renderSearchControls( $searchControls ).hide();
+ if( !$input.children().size() ){
+ this.setUpSearchInput( $input );
+ //$searchControls.append( faIconButton({
+ // title : _l( 'More search options' ),
+ // classes : 'history-search-advanced',
+ // faIcon : 'fa-ellipsis-horizontal'
+ //}) );
}
// then slide open, focusing on the input, and persisting the setting when it's done
- $searchControls.show( speed, function(){
+ $searchControls.slideDown( speed, function(){
$( this ).find( 'input' ).focus();
panel.preferences.set( 'searching', true );
});
@@ -1040,7 +1048,7 @@
hideSearchControls : function(){
speed = ( speed === undefined )?( this.fxSpeed ):( speed );
var panel = this;
- this.$el.find( '.history-search-controls' ).hide( speed, function(){
+ this.$el.find( '.history-search-controls' ).slideUp( speed, function(){
panel.preferences.set( 'searching', false );
});
},
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/scripts/mvc/ui.js
--- a/static/scripts/mvc/ui.js
+++ b/static/scripts/mvc/ui.js
@@ -637,7 +637,7 @@
// initial render
if( jQuery.type( options ) === 'object' ){
- options = jQuery.extend( true, defaults, options );
+ options = jQuery.extend( true, {}, defaults, options );
}
//NOTE: prepended
return $parentNode.addClass( 'search-input' ).prepend([ $input(), $clearBtn(), $loadingIndicator() ]);
@@ -881,3 +881,200 @@
};
return self;
}
+
+
+//==============================================================================
+/**
+ * Template function that produces a bootstrap dropdown to replace the
+ * vanilla HTML select input. Pass in an array of options and an initial selection:
+ * $( '.my-div' ).append( dropDownSelect( [ 'option1', 'option2' ], 'option2' );
+ *
+ * When the user changes the selected option a 'change.dropdown-select' event will
+ * fire with both the jq event and the new selection text as arguments.
+ *
+ * Get the currently selected choice using:
+ * var userChoice = $( '.my-div .dropdown-select .dropdown-select-selected' ).text();
+ *
+ */
+function dropDownSelect( options, selected ){
+ // replacement for vanilla select element using bootstrap dropdowns instead
+ selected = selected || (( !_.isEmpty( options ) )?( options[0] ):( '' ));
+ var $select = $([
+ '<div class="dropdown-select btn-group">',
+ '<button type="button" class="btn btn-default">',
+ '<span class="dropdown-select-selected">' + selected + '</span>',
+ '</button>',
+ '</div>'
+ ].join( '\n' ));
+
+ // if there's only one option, do not style/create as buttons, dropdown - use simple span
+ // otherwise, a dropdown displaying the current selection
+ if( options && options.length > 1 ){
+ $select.find( 'button' )
+ .addClass( 'dropdown-toggle' ).attr( 'data-toggle', 'dropdown' )
+ .append( ' <span class="caret"></span>' );
+ $select.append([
+ '<ul class="dropdown-menu" role="menu">',
+ _.map( options, function( option ){
+ return [
+ '<li><a href="javascript:void(0)">', option, '</a></li>'
+ ].join( '' );
+ }).join( '\n' ),
+ '</ul>'
+ ].join( '\n' ));
+ }
+
+ // trigger 'change.dropdown-select' when a new selection is made using the dropdown
+ function selectThis( event ){
+ var $this = $( this ),
+ $select = $this.parents( '.dropdown-select' ),
+ newSelection = $this.text();
+ $select.find( '.dropdown-select-selected' ).text( newSelection );
+ $select.trigger( 'change.dropdown-select', newSelection );
+ }
+
+ $select.find( 'a' ).click( selectThis );
+ return $select;
+}
+
+
+//==============================================================================
+(function(){
+ /**
+ * Creates a three part bootstrap button group (key, op, value) meant to
+ * allow the user control of filters (e.g. { key: 'name', op: 'contains', value: 'my_history' })
+ *
+ * Each field uses a dropDownSelect (from ui.js) to allow selection
+ * (with the 'value' field appearing as an input when set to do so).
+ *
+ * Any change or update in any of the fields will trigger a 'change.filter-control'
+ * event which will be passed an object containing those fields (as the example above).
+ *
+ * Pass in an array of possible filter objects to control what the user can select.
+ * Each filter object should have:
+ * key : generally the attribute name on which to filter something
+ * ops : an array of 1 or more filter operations (e.g. [ 'is', '<', 'contains', '!=' ])
+ * values (optional) : an array of possible values for the filter (e.g. [ 'true', 'false' ])
+ * @example:
+ * $( '.my-div' ).filterControl({
+ * filters : [
+ * { key: 'name', ops: [ 'is exactly', 'contains' ] }
+ * { key: 'deleted', ops: [ 'is' ], values: [ 'true', 'false' ] }
+ * ]
+ * });
+ * // after initialization, you can prog. get the current value using:
+ * $( '.my-div' ).filterControl( 'val' )
+ *
+ */
+ function FilterControl( element, options ){
+ return this.init( element, options );
+ }
+ /** the data key that this object will be stored under in the DOM element */
+ FilterControl.prototype.DATA_KEY = 'filter-control';
+
+ /** parses options, sets up instance vars, and does initial render */
+ FilterControl.prototype.init = function _init( element, options ){
+ options = options || { filters: [] };
+ this.$element = $( element ).addClass( 'filter-control btn-group' );
+ this.options = jQuery.extend( true, {}, this.defaults, options );
+
+ this.currFilter = this.options.filters[0];
+ return this.render();
+ };
+
+ /** render (or re-render) the controls on the element */
+ FilterControl.prototype.render = function _render(){
+ this.$element.empty()
+ .append([ this._renderKeySelect(), this._renderOpSelect(), this._renderValueInput() ]);
+ return this;
+ };
+
+ /** render the key dropDownSelect, bind a change event to it, and return it */
+ FilterControl.prototype._renderKeySelect = function __renderKeySelect(){
+ var filterControl = this;
+ var keys = this.options.filters.map( function( filter ){
+ return filter.key;
+ });
+ this.$keySelect = dropDownSelect( keys, this.currFilter.key )
+ .addClass( 'filter-control-key' )
+ .on( 'change.dropdown-select', function( event, selection ){
+ filterControl.currFilter = _.findWhere( filterControl.options.filters, { key: selection });
+ // when the filter/key changes, re-render the control entirely
+ filterControl.render()._triggerChange();
+ });
+ return this.$keySelect;
+ };
+
+ /** render the op dropDownSelect, bind a change event to it, and return it */
+ FilterControl.prototype._renderOpSelect = function __renderOpSelect(){
+ var filterControl = this,
+ ops = this.currFilter.ops;
+ //TODO: search for currOp in avail. ops: use that for selected if there; otherwise: first op
+ this.$opSelect = dropDownSelect( ops, ops[0] )
+ .addClass( 'filter-control-op' )
+ .on( 'change.dropdown-select', function( event, selection ){
+ filterControl._triggerChange();
+ });
+ return this.$opSelect;
+ };
+
+ /** render the value control, bind a change event to it, and return it */
+ FilterControl.prototype._renderValueInput = function __renderValueInput(){
+ var filterControl = this;
+ // if a values attribute is prov. on the filter - make this a dropdown; otherwise, use an input
+ if( this.currFilter.values ){
+ this.$valueSelect = dropDownSelect( this.currFilter.values, this.currFilter.values[0] )
+ .on( 'change.dropdown-select', function( event, selection ){
+ filterControl._triggerChange();
+ });
+ } else {
+ //TODO: allow setting a value type (mainly for which html5 input to use: range, number, etc.)
+ this.$valueSelect = $( '<input/>' ).addClass( 'form-control' )
+ .on( 'change', function( event, value ){
+ filterControl._triggerChange();
+ });
+ }
+ this.$valueSelect.addClass( 'filter-control-value' );
+ return this.$valueSelect;
+ };
+
+ /** return the current state/setting for the filter as a three key object: key, op, value */
+ FilterControl.prototype.val = function _val(){
+ var key = this.$element.find( '.filter-control-key .dropdown-select-selected' ).text(),
+ op = this.$element.find( '.filter-control-op .dropdown-select-selected' ).text(),
+ // handle either a dropdown or plain input
+ $value = this.$element.find( '.filter-control-value' ),
+ value = ( $value.hasClass( 'dropdown-select' ) )?( $value.find( '.dropdown-select-selected' ).text() )
+ :( $value.val() );
+ return { key: key, op: op, value: value };
+ };
+
+ // single point of change for change event
+ FilterControl.prototype._triggerChange = function __triggerChange(){
+ this.$element.trigger( 'change.filter-control', this.val() );
+ };
+
+
+ // as jq plugin
+ jQuery.fn.extend({
+ filterControl : function $filterControl( options ){
+ var nonOptionsArgs = jQuery.makeArray( arguments ).slice( 1 );
+ return this.map( function(){
+ var $this = $( this ),
+ data = $this.data( FilterControl.prototype.DATA_KEY );
+
+ if( jQuery.type( options ) === 'object' ){
+ data = new FilterControl( $this, options );
+ $this.data( FilterControl.prototype.DATA_KEY, data );
+ }
+ if( data && jQuery.type( options ) === 'string' ){
+ var fn = data[ options ];
+ if( jQuery.type( fn ) === 'function' ){
+ return fn.apply( data, nonOptionsArgs );
+ }
+ }
+ return this;
+ });
+ }
+ });
+}());
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/scripts/packed/mvc/history/history-panel.js
--- a/static/scripts/packed/mvc/history/history-panel.js
+++ b/static/scripts/packed/mvc/history/history-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/history/history-model","mvc/dataset/hda-model","mvc/dataset/hda-base","mvc/dataset/hda-edit"],function(c,f,a,i){var g=SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});g.storageKey=function h(){return("history-panel")};var d=SessionStorageModel.extend({defaults:{expandedHdas:{},show_deleted:false,show_hidden:false},addExpandedHda:function(j){this.save("expandedHdas",_.extend(this.get("expandedHdas"),_.object([j],[true])))},removeExpandedHda:function(j){this.save("expandedHdas",_.omit(this.get("expandedHdas"),j))},toString:function(){return"HistoryPrefs("+this.id+")"}});d.historyStorageKey=function b(j){if(!j){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+j)}return("history:"+j)};var e=Backbone.View.extend(LoggableMixin).extend({defaultHDAViewClass:i.HDAEditView,tagName:"div",className:"history-panel",fxSpeed:"fast",datasetsSelector:".datasets-list",emptyMsgSelector:".empty-history-message",msgsSelector:".message-container",initialize:function(j){j=j||{};if(j.logger){this.logger=j.logger}this.log(this+".initialize:",j);this.HDAViewClass=j.HDAViewClass||this.defaultHDAViewClass;this.linkTarget=j.linkTarget||"_blank";this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);this.preferences=new g(_.extend({id:g.storageKey()},_.pick(j,_.keys(g.prototype.defaults))));this.filters=[];this.selecting=j.selecting||false;this.annotationEditorShown=j.annotationEditorShown||false;this._setUpListeners();if(this.model){this._setUpWebStorage(j.initiallyExpanded,j.show_deleted,j.show_hidden);this._setUpModelEventHandlers()}if(j.onready){j.onready.call(this)}},_setUpListeners:function(){this.on("error",function(k,n,j,m,l){this.errorHandler(k,n,j,m,l)});this.on("loading-history",function(){this.showLoadingIndicator("loading history...")});this.on("loading-done",function(){this.hideLoadingIndicator()});this.once("rendered",function(){this.trigger("rendered:initial",this);return false});this.on("switched-history current-history new-history",function(){if(_.isEmpty(this.hdaViews)){this.trigger("empty-history",this)}});if(this.logger){this.on("all",function(j){this.log(this+"",arguments)},this)}},errorHandler:function(l,o,k,n,m){var j=this._parseErrorMessage(l,o,k,n,m);if(o&&o.status===0&&o.readyState===0){}else{if(o&&o.status===502){}else{if(!this.$el.find(this.msgsSelector).is(":visible")){this.once("rendered",function(){this.displayMessage("error",j.message,j.details)})}else{this.displayMessage("error",j.message,j.details)}}}},_parseErrorMessage:function(m,q,l,p,o){var k=Galaxy.currUser,j={message:this._bePolite(p),details:{user:(k instanceof User)?(k.toJSON()):(k+""),source:(m instanceof Backbone.Model)?(m.toJSON()):(m+""),xhr:q,options:(q)?(_.omit(l,"xhr")):(l)}};_.extend(j.details,o||{});if(q&&_.isFunction(q.getAllResponseHeaders)){var n=q.getAllResponseHeaders();n=_.compact(n.split("\n"));n=_.map(n,function(r){return r.split(": ")});j.details.xhr.responseHeaders=_.object(n)}return j},_bePolite:function(j){j=j||_l("An error occurred while getting updates from the server");return j+". "+_l("Please contact a Galaxy administrator if the problem persists.")},loadCurrentHistory:function(k){var j=this;return this.loadHistoryWithHDADetails("current",k).then(function(m,l){j.trigger("current-history",j)})},switchToHistory:function(m,l){var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories/"+m+"/set_as_current")};return this.loadHistoryWithHDADetails(m,l,k).then(function(o,n){j.trigger("switched-history",j)})},createNewHistory:function(l){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",_l("You must be logged in to create histories"));return $.when()}var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,l,k).then(function(n,m){j.trigger("new-history",j)})},loadHistoryWithHDADetails:function(m,l,k,o){var j=this,n=function(p){return j.getExpandedHdaIds(p.id)};return this.loadHistory(m,l,k,o,n)},loadHistory:function(m,l,k,p,n){this.trigger("loading-history",this);l=l||{};var j=this;var o=c.History.getHistoryData(m,{historyFn:k,hdaFn:p,hdaDetailIds:l.initiallyExpanded||n});return this._loadHistoryFromXHR(o,l).fail(function(s,q,r){j.trigger("error",j,s,l,_l("An error was encountered while "+q),{historyId:m,history:r||{}})}).always(function(){j.trigger("loading-done",j)})},_loadHistoryFromXHR:function(l,k){var j=this;l.then(function(m,n){j.setModel(m,n,k)});l.fail(function(n,m){j.render()});return l},setModel:function(l,j,k){k=k||{};if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.hdas)}this.hdaViews={};if(Galaxy&&Galaxy.currUser){l.user=Galaxy.currUser.toJSON()}this.model=new c.History(l,j,k);this._setUpWebStorage(k.initiallyExpanded,k.show_deleted,k.show_hidden);this._setUpModelEventHandlers();this.trigger("new-model",this);this.render();return this},_setUpWebStorage:function(k,j,l){this.storage=new d({id:d.historyStorageKey(this.model.get("id"))});if(_.isObject(k)){this.storage.set("exandedHdas",k)}if(_.isBoolean(j)){this.storage.set("show_deleted",j)}if(_.isBoolean(l)){this.storage.set("show_hidden",l)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get())},clearWebStorage:function(){for(var j in sessionStorage){if(j.indexOf("history:")===0){sessionStorage.removeItem(j)}}},getStoredOptions:function(k){if(!k||k==="current"){return(this.storage)?(this.storage.get()):({})}var j=sessionStorage.getItem(d.historyStorageKey(k));return(j===null)?({}):(JSON.parse(j))},getExpandedHdaIds:function(j){var k=this.getStoredOptions(j).expandedHdas;return((_.isEmpty(k))?([]):(_.keys(k)))},_setUpModelEventHandlers:function(){this.model.on("error error:hdas",function(k,m,j,l){this.errorHandler(k,m,j,l)},this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("add",this.addHdaView,this);this.model.hdas.on("change:deleted",this.handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this.handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(j){this.model.fetch()},this);this.model.hdas.on("state:ready",function(k,l,j){if((!k.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[k.id])}},this)},render:function(l,m){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k;if(this.model){k=this.renderModel()}else{k=this.renderWithoutModel()}$(j).queue("fx",[function(n){if(l&&j.$el.is(":visible")){j.$el.fadeOut(l,n)}else{n()}},function(n){j.$el.empty();if(k){j.$el.append(k.children());j.renderBasedOnPrefs()}n()},function(n){if(l&&!j.$el.is(":visible")){j.$el.fadeIn(l,n)}else{n()}},function(n){if(m){m.call(this)}j.trigger("rendered",this);n()}]);return this},renderWithoutModel:function(){var j=$("<div/>"),k=$("<div/>").addClass("message-container").css({"margin-left":"4px","margin-right":"4px"});return j.append(k)},renderModel:function(){var j=$("<div/>");if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){j.append(e.templates.anonHistoryPanel(this.model.toJSON()))}else{j.append(e.templates.historyPanel(this.model.toJSON()));if(Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(j);this._renderAnnotation(j)}}j.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(j);this.renderHdas(j);return j},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.showSearchControls(0)}},_renderTags:function(j){var k=this;this.tagsEditor=new TagsEditor({model:this.model,el:j.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("tagsEditorShown",true);k.toggleHDATagEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("tagsEditorShown",false);k.toggleHDATagEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}},_renderAnnotation:function(j){var k=this;this.annotationEditor=new AnnotationEditor({model:this.model,el:j.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("annotationEditorShown",true);k.toggleHDAAnnotationEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("annotationEditorShown",false);k.toggleHDAAnnotationEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history Annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}},_renderSearchButton:function(j){return faIconButton({title:_l("Search datasets"),classes:"history-search-btn",faIcon:"fa-search"})},_renderSelectButton:function(j){return faIconButton({title:_l("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(j){j=j||this.$el;j.find("[title]").tooltip({placement:"bottom"});if((!this.model)||(!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var k=this;j.find(".history-name").attr("title",_l("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(l){var m=k.model.get("name");if(l&&l!==m){k.$el.find(".history-name").text(l);k.model.save({name:l}).fail(function(){k.$el.find(".history-name").text(k.model.previous("name"))})}else{k.$el.find(".history-name").text(m)}}});this._setUpDatasetActionsPopup(j)},_setUpDatasetActionsPopup:function(j){var k=this;(new PopupMenu(j.find(".history-dataset-action-popup-btn"),[{html:_l("Hide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.hide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Unhide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.unhide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Delete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype["delete"];k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Undelete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.undelete;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Permanently delete datasets"),func:function(){if(confirm(_l("This will permanently remove the data in your datasets. Are you sure?"))){var l=f.HistoryDatasetAssociation.prototype.purge;k.getSelectedHdaCollection().ajaxQueue(l)}}}]))},refreshHdas:function(k,j){if(this.model){return this.model.refresh(k,j)}return $.when()},addHdaView:function(m){this.log("add."+this,m);var k=this;if(!m.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return}$({}).queue([function l(o){var n=k.$el.find(k.emptyMsgSelector);if(n.is(":visible")){n.fadeOut(k.fxSpeed,o)}else{o()}},function j(o){k.scrollToTop();var n=k.$el.find(k.datasetsSelector);k.createHdaView(m).$el.hide().prependTo(n).slideDown(k.fxSpeed)}])},createHdaView:function(l){var k=l.get("id"),j=this.storage.get("expandedHdas")[k],m=new this.HDAViewClass({model:l,linkTarget:this.linkTarget,expanded:j,tagsEditorShown:this.preferences.get("tagsEditorShown"),annotationEditorShown:this.preferences.get("annotationEditorShown"),selectable:this.selecting,hasUser:this.model.ownedByCurrUser(),logger:this.logger});this._setUpHdaListeners(m);this.hdaViews[k]=m;return m.render()},_setUpHdaListeners:function(k){var j=this;k.on("body-expanded",function(l){j.storage.addExpandedHda(l)});k.on("body-collapsed",function(l){j.storage.removeExpandedHda(l)});k.on("error",function(m,o,l,n){j.errorHandler(m,o,l,n)})},handleHdaDeletionChange:function(j){if(j.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[j.id])}},handleHdaVisibleChange:function(j){if(j.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[j.id])}},removeHdaView:function(k){if(!k){return}var j=this;k.$el.fadeOut(j.fxSpeed,function(){k.off();k.remove();delete j.hdaViews[k.model.id];if(_.isEmpty(j.hdaViews)){j.$el.find(j.emptyMsgSelector).fadeIn(j.fxSpeed,function(){j.trigger("empty-history",j)})}})},renderHdas:function(l){l=l||this.$el;this.hdaViews={};var k=this,j=l.find(this.datasetsSelector),m=this.model.hdas.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters);j.empty();if(m.length){m.each(function(n){j.prepend(k.createHdaView(n).$el)});l.find(this.emptyMsgSelector).hide()}else{l.find(this.emptyMsgSelector).show()}return this.hdaViews},toggleHDATagEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.tagsEditor){l.tagsEditor.toggle.apply(l.tagsEditor,k)}})},toggleHDAAnnotationEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.annotationEditor){l.annotationEditor.toggle.apply(l.annotationEditor,k)}})},events:{"click .message-container":"clearMessages","click .history-search-btn":"toggleSearchControls","click .history-select-btn":function(j){this.toggleSelectors(this.fxSpeed)},"click .history-select-all-datasets-btn":"selectAllDatasets"},updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(j){j.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},toggleShowDeleted:function(){this.storage.set("show_deleted",!this.storage.get("show_deleted"));this.renderHdas();return this.storage.get("show_deleted")},toggleShowHidden:function(){this.storage.set("show_hidden",!this.storage.get("show_hidden"));this.renderHdas();return this.storage.get("show_hidden")},renderSearchControls:function(k){var l=this;function n(o){l.searchFor=o;l.filters=[function(p){return p.matchesAll(l.searchFor)}];l.trigger("search:searching",o,l);l.renderHdas()}function j(o){if(l.model.hdas.haveDetails()){n(o);return}l.$el.find(".history-search-controls").searchInput("toggle-loading");l.model.hdas.fetchAllDetails({silent:true}).always(function(){l.$el.find(".history-search-controls").searchInput("toggle-loading")}).done(function(){n(o)})}function m(){l.searchFor="";l.filters=[];l.trigger("search:clear",l);l.renderHdas()}return k.searchInput({initialVal:l.searchFor,name:"history-search",placeholder:"search datasets",classes:"history-search",onfirstsearch:j,onsearch:n,onclear:m})},showSearchControls:function(l){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k=this.$el.find(".history-search-controls");if(!k.children().size()){k=this.renderSearchControls(k).hide()}k.show(l,function(){$(this).find("input").focus();j.preferences.set("searching",true)})},hideSearchControls:function(){speed=(speed===undefined)?(this.fxSpeed):(speed);var j=this;this.$el.find(".history-search-controls").hide(speed,function(){j.preferences.set("searching",false)})},toggleSearchControls:function(j){speed=(jQuery.type(j)==="number")?(j):(this.fxSpeed);if(this.$el.find(".history-search-controls").is(":visible")){this.hideSearchControls(speed)}else{this.showSearchControls(speed)}},showSelectors:function(j){this.selecting=true;this.$el.find(".history-dataset-actions").slideDown(j);_.each(this.hdaViews,function(k){k.showSelector(j)})},hideSelectors:function(j){this.selecting=false;this.$el.find(".history-dataset-actions").slideUp(j);_.each(this.hdaViews,function(k){k.hideSelector(j)})},toggleSelectors:function(j){if(!this.selecting){this.showSelectors(j)}else{this.hideSelectors(j)}},selectAllDatasets:function(k){var j=this.$el.find(".history-select-all-datasets-btn");currMode=j.data("mode");if(currMode==="select"){_.each(this.hdaViews,function(l){l.select(k)});j.data("mode","deselect");j.text(_l("De-select all"))}else{if(currMode==="deselect"){_.each(this.hdaViews,function(l){l.deselect(k)});j.data("mode","select");j.text(_l("Select all"))}}},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(j){return j.selected})},getSelectedHdaCollection:function(){return new f.HDACollection(_.map(this.getSelectedHdaViews(),function(j){return j.model}),{historyId:this.model.id})},showLoadingIndicator:function(k,j,l){j=(j!==undefined)?(j):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,l)}else{this.$el.fadeOut(j);this.indicator.show(k,j,l)}},hideLoadingIndicator:function(j,k){j=(j!==undefined)?(j):(this.fxSpeed);if(this.indicator){this.indicator.hide(j,k)}},displayMessage:function(o,p,n){var l=this;this.scrollToTop();var m=this.$el.find(this.msgsSelector),j=$("<div/>").addClass(o+"message").html(p);if(!_.isEmpty(n)){var k=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(l.messageToModalOptions(o,p,n));return false});j.append(" ",k)}return m.html(j)},messageToModalOptions:function(n,p,m){var j=this,o=$("<div/>"),l={title:"Details"};function k(q){q=_.omit(q,_.functions(q));return["<table>",_.map(q,function(s,r){s=(_.isObject(s))?(k(s)):(s);return'<tr><td style="vertical-align: top; color: grey">'+r+'</td><td style="padding-left: 8px">'+s+"</td></tr>"}).join(""),"</table>"].join("")}if(_.isObject(m)){l.body=o.append(k(m))}else{l.body=o.html(m)}l.buttons={Ok:function(){Galaxy.modal.hide();j.clearMessages()}};return l},clearMessages:function(){var j=this.$el.find(this.msgsSelector);j.empty()},scrollPosition:function(){return this.$el.parent().scrollTop()},scrollTo:function(j){this.$el.parent().scrollTop(j)},scrollToTop:function(){this.$el.parent().scrollTop(0);return this},scrollIntoView:function(k,l){if(!l){this.$el.parent().parent().scrollTop(k);return this}var j=window,m=this.$el.parent().parent(),o=$(j).innerHeight(),n=(o/2)-(l/2);$(m).scrollTop(k-n);return this},scrollToId:function(k){if((!k)||(!this.hdaViews[k])){return this}var j=this.hdaViews[k].$el;this.scrollIntoView(j.offset().top,j.outerHeight());return this},scrollToHid:function(j){var k=this.model.hdas.getByHid(j);if(!k){return this}return this.scrollToId(k.id)},connectToQuotaMeter:function(j){if(!j){return this}this.listenTo(j,"quota:over",this.showQuotaMessage);this.listenTo(j,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(j&&j.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var j=this.$el.find(".quota-message");if(j.is(":hidden")){j.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var j=this.$el.find(".quota-message");if(!j.is(":hidden")){j.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(j){if(!j){return this}this.on("new-storage",function(l,k){if(j&&l){j.findItemByHtml(_l("Include Deleted Datasets")).checked=l.get("show_deleted");j.findItemByHtml(_l("Include Hidden Datasets")).checked=l.get("show_hidden")}});return this},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});e.templates={historyPanel:Handlebars.templates["template-history-historyPanel"],anonHistoryPanel:Handlebars.templates["template-history-historyPanel-anon"]};return{HistoryPanel:e}});
\ No newline at end of file
+define(["mvc/history/history-model","mvc/dataset/hda-model","mvc/dataset/hda-base","mvc/dataset/hda-edit"],function(c,f,a,i){var g=SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});g.storageKey=function h(){return("history-panel")};var d=SessionStorageModel.extend({defaults:{expandedHdas:{},show_deleted:false,show_hidden:false},addExpandedHda:function(j){this.save("expandedHdas",_.extend(this.get("expandedHdas"),_.object([j],[true])))},removeExpandedHda:function(j){this.save("expandedHdas",_.omit(this.get("expandedHdas"),j))},toString:function(){return"HistoryPrefs("+this.id+")"}});d.historyStorageKey=function b(j){if(!j){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+j)}return("history:"+j)};var e=Backbone.View.extend(LoggableMixin).extend({defaultHDAViewClass:i.HDAEditView,tagName:"div",className:"history-panel",fxSpeed:"fast",datasetsSelector:".datasets-list",emptyMsgSelector:".empty-history-message",msgsSelector:".message-container",initialize:function(j){j=j||{};if(j.logger){this.logger=j.logger}this.log(this+".initialize:",j);this.HDAViewClass=j.HDAViewClass||this.defaultHDAViewClass;this.linkTarget=j.linkTarget||"_blank";this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);this.preferences=new g(_.extend({id:g.storageKey()},_.pick(j,_.keys(g.prototype.defaults))));this.filters=[];this.selecting=j.selecting||false;this.annotationEditorShown=j.annotationEditorShown||false;this._setUpListeners();if(this.model){this._setUpWebStorage(j.initiallyExpanded,j.show_deleted,j.show_hidden);this._setUpModelEventHandlers()}if(j.onready){j.onready.call(this)}},_setUpListeners:function(){this.on("error",function(k,n,j,m,l){this.errorHandler(k,n,j,m,l)});this.on("loading-history",function(){this.showLoadingIndicator("loading history...")});this.on("loading-done",function(){this.hideLoadingIndicator()});this.once("rendered",function(){this.trigger("rendered:initial",this);return false});this.on("switched-history current-history new-history",function(){if(_.isEmpty(this.hdaViews)){this.trigger("empty-history",this)}});if(this.logger){this.on("all",function(j){this.log(this+"",arguments)},this)}},errorHandler:function(l,o,k,n,m){var j=this._parseErrorMessage(l,o,k,n,m);if(o&&o.status===0&&o.readyState===0){}else{if(o&&o.status===502){}else{if(!this.$el.find(this.msgsSelector).is(":visible")){this.once("rendered",function(){this.displayMessage("error",j.message,j.details)})}else{this.displayMessage("error",j.message,j.details)}}}},_parseErrorMessage:function(m,q,l,p,o){var k=Galaxy.currUser,j={message:this._bePolite(p),details:{user:(k instanceof User)?(k.toJSON()):(k+""),source:(m instanceof Backbone.Model)?(m.toJSON()):(m+""),xhr:q,options:(q)?(_.omit(l,"xhr")):(l)}};_.extend(j.details,o||{});if(q&&_.isFunction(q.getAllResponseHeaders)){var n=q.getAllResponseHeaders();n=_.compact(n.split("\n"));n=_.map(n,function(r){return r.split(": ")});j.details.xhr.responseHeaders=_.object(n)}return j},_bePolite:function(j){j=j||_l("An error occurred while getting updates from the server");return j+". "+_l("Please contact a Galaxy administrator if the problem persists.")},loadCurrentHistory:function(k){var j=this;return this.loadHistoryWithHDADetails("current",k).then(function(m,l){j.trigger("current-history",j)})},switchToHistory:function(m,l){var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories/"+m+"/set_as_current")};return this.loadHistoryWithHDADetails(m,l,k).then(function(o,n){j.trigger("switched-history",j)})},createNewHistory:function(l){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",_l("You must be logged in to create histories"));return $.when()}var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,l,k).then(function(n,m){j.trigger("new-history",j)})},loadHistoryWithHDADetails:function(m,l,k,o){var j=this,n=function(p){return j.getExpandedHdaIds(p.id)};return this.loadHistory(m,l,k,o,n)},loadHistory:function(m,l,k,p,n){this.trigger("loading-history",this);l=l||{};var j=this;var o=c.History.getHistoryData(m,{historyFn:k,hdaFn:p,hdaDetailIds:l.initiallyExpanded||n});return this._loadHistoryFromXHR(o,l).fail(function(s,q,r){j.trigger("error",j,s,l,_l("An error was encountered while "+q),{historyId:m,history:r||{}})}).always(function(){j.trigger("loading-done",j)})},_loadHistoryFromXHR:function(l,k){var j=this;l.then(function(m,n){j.setModel(m,n,k)});l.fail(function(n,m){j.render()});return l},setModel:function(l,j,k){k=k||{};if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.hdas)}this.hdaViews={};if(Galaxy&&Galaxy.currUser){l.user=Galaxy.currUser.toJSON()}this.model=new c.History(l,j,k);this._setUpWebStorage(k.initiallyExpanded,k.show_deleted,k.show_hidden);this._setUpModelEventHandlers();this.trigger("new-model",this);this.render();return this},_setUpWebStorage:function(k,j,l){this.storage=new d({id:d.historyStorageKey(this.model.get("id"))});if(_.isObject(k)){this.storage.set("exandedHdas",k)}if(_.isBoolean(j)){this.storage.set("show_deleted",j)}if(_.isBoolean(l)){this.storage.set("show_hidden",l)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get())},clearWebStorage:function(){for(var j in sessionStorage){if(j.indexOf("history:")===0){sessionStorage.removeItem(j)}}},getStoredOptions:function(k){if(!k||k==="current"){return(this.storage)?(this.storage.get()):({})}var j=sessionStorage.getItem(d.historyStorageKey(k));return(j===null)?({}):(JSON.parse(j))},getExpandedHdaIds:function(j){var k=this.getStoredOptions(j).expandedHdas;return((_.isEmpty(k))?([]):(_.keys(k)))},_setUpModelEventHandlers:function(){this.model.on("error error:hdas",function(k,m,j,l){this.errorHandler(k,m,j,l)},this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("add",this.addHdaView,this);this.model.hdas.on("change:deleted",this.handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this.handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(j){this.model.fetch()},this);this.model.hdas.on("state:ready",function(k,l,j){if((!k.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[k.id])}},this)},render:function(l,m){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k;if(this.model){k=this.renderModel()}else{k=this.renderWithoutModel()}$(j).queue("fx",[function(n){if(l&&j.$el.is(":visible")){j.$el.fadeOut(l,n)}else{n()}},function(n){j.$el.empty();if(k){j.$el.append(k.children());j.renderBasedOnPrefs()}n()},function(n){if(l&&!j.$el.is(":visible")){j.$el.fadeIn(l,n)}else{n()}},function(n){if(m){m.call(this)}j.trigger("rendered",this);n()}]);return this},renderWithoutModel:function(){var j=$("<div/>"),k=$("<div/>").addClass("message-container").css({"margin-left":"4px","margin-right":"4px"});return j.append(k)},renderModel:function(){var j=$("<div/>");if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){j.append(e.templates.anonHistoryPanel(this.model.toJSON()))}else{j.append(e.templates.historyPanel(this.model.toJSON()));if(Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(j);this._renderAnnotation(j)}}j.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(j);this.renderHdas(j);return j},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.showSearchControls(0)}},_renderTags:function(j){var k=this;this.tagsEditor=new TagsEditor({model:this.model,el:j.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("tagsEditorShown",true);k.toggleHDATagEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("tagsEditorShown",false);k.toggleHDATagEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}},_renderAnnotation:function(j){var k=this;this.annotationEditor=new AnnotationEditor({model:this.model,el:j.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("annotationEditorShown",true);k.toggleHDAAnnotationEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("annotationEditorShown",false);k.toggleHDAAnnotationEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history Annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}},_renderSearchButton:function(j){return faIconButton({title:_l("Search datasets"),classes:"history-search-btn",faIcon:"fa-search"})},_renderSelectButton:function(j){return faIconButton({title:_l("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(j){j=j||this.$el;j.find("[title]").tooltip({placement:"bottom"});if((!this.model)||(!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var k=this;j.find(".history-name").attr("title",_l("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(l){var m=k.model.get("name");if(l&&l!==m){k.$el.find(".history-name").text(l);k.model.save({name:l}).fail(function(){k.$el.find(".history-name").text(k.model.previous("name"))})}else{k.$el.find(".history-name").text(m)}}});this._setUpDatasetActionsPopup(j)},_setUpDatasetActionsPopup:function(j){var k=this;(new PopupMenu(j.find(".history-dataset-action-popup-btn"),[{html:_l("Hide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.hide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Unhide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.unhide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Delete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype["delete"];k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Undelete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.undelete;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Permanently delete datasets"),func:function(){if(confirm(_l("This will permanently remove the data in your datasets. Are you sure?"))){var l=f.HistoryDatasetAssociation.prototype.purge;k.getSelectedHdaCollection().ajaxQueue(l)}}}]))},refreshHdas:function(k,j){if(this.model){return this.model.refresh(k,j)}return $.when()},addHdaView:function(m){this.log("add."+this,m);var k=this;if(!m.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return}$({}).queue([function l(o){var n=k.$el.find(k.emptyMsgSelector);if(n.is(":visible")){n.fadeOut(k.fxSpeed,o)}else{o()}},function j(o){k.scrollToTop();var n=k.$el.find(k.datasetsSelector);k.createHdaView(m).$el.hide().prependTo(n).slideDown(k.fxSpeed)}])},createHdaView:function(l){var k=l.get("id"),j=this.storage.get("expandedHdas")[k],m=new this.HDAViewClass({model:l,linkTarget:this.linkTarget,expanded:j,tagsEditorShown:this.preferences.get("tagsEditorShown"),annotationEditorShown:this.preferences.get("annotationEditorShown"),selectable:this.selecting,hasUser:this.model.ownedByCurrUser(),logger:this.logger});this._setUpHdaListeners(m);this.hdaViews[k]=m;return m.render()},_setUpHdaListeners:function(k){var j=this;k.on("body-expanded",function(l){j.storage.addExpandedHda(l)});k.on("body-collapsed",function(l){j.storage.removeExpandedHda(l)});k.on("error",function(m,o,l,n){j.errorHandler(m,o,l,n)})},handleHdaDeletionChange:function(j){if(j.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[j.id])}},handleHdaVisibleChange:function(j){if(j.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[j.id])}},removeHdaView:function(k){if(!k){return}var j=this;k.$el.fadeOut(j.fxSpeed,function(){k.off();k.remove();delete j.hdaViews[k.model.id];if(_.isEmpty(j.hdaViews)){j.$el.find(j.emptyMsgSelector).fadeIn(j.fxSpeed,function(){j.trigger("empty-history",j)})}})},renderHdas:function(l){l=l||this.$el;this.hdaViews={};var k=this,j=l.find(this.datasetsSelector),m=this.model.hdas.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters);j.empty();if(m.length){m.each(function(n){j.prepend(k.createHdaView(n).$el)});l.find(this.emptyMsgSelector).hide()}else{l.find(this.emptyMsgSelector).show()}return this.hdaViews},toggleHDATagEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.tagsEditor){l.tagsEditor.toggle.apply(l.tagsEditor,k)}})},toggleHDAAnnotationEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.annotationEditor){l.annotationEditor.toggle.apply(l.annotationEditor,k)}})},events:{"click .message-container":"clearMessages","click .history-search-btn":"toggleSearchControls","click .history-select-btn":function(j){this.toggleSelectors(this.fxSpeed)},"click .history-select-all-datasets-btn":"selectAllDatasets"},updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(j){j.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},toggleShowDeleted:function(){this.storage.set("show_deleted",!this.storage.get("show_deleted"));this.renderHdas();return this.storage.get("show_deleted")},toggleShowHidden:function(){this.storage.set("show_hidden",!this.storage.get("show_hidden"));this.renderHdas();return this.storage.get("show_hidden")},setUpSearchInput:function(k){var l=this,o=".history-search-input";function n(p){l.searchFor=p;l.filters=[function(q){return q.matchesAll(l.searchFor)}];l.trigger("search:searching",p,l);l.renderHdas()}function j(p){if(l.model.hdas.haveDetails()){n(p);return}l.$el.find(o).searchInput("toggle-loading");l.model.hdas.fetchAllDetails({silent:true}).always(function(){l.$el.find(o).searchInput("toggle-loading")}).done(function(){n(p)})}function m(){l.searchFor="";l.filters=[];l.trigger("search:clear",l);l.renderHdas()}k.searchInput({initialVal:l.searchFor,name:"history-search",placeholder:"search datasets",classes:"history-search",onfirstsearch:j,onsearch:n,onclear:m});return k},showSearchControls:function(l){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k=this.$el.find(".history-search-controls"),m=k.find(".history-search-input");if(!m.children().size()){this.setUpSearchInput(m)}k.slideDown(l,function(){$(this).find("input").focus();j.preferences.set("searching",true)})},hideSearchControls:function(){speed=(speed===undefined)?(this.fxSpeed):(speed);var j=this;this.$el.find(".history-search-controls").slideUp(speed,function(){j.preferences.set("searching",false)})},toggleSearchControls:function(j){speed=(jQuery.type(j)==="number")?(j):(this.fxSpeed);if(this.$el.find(".history-search-controls").is(":visible")){this.hideSearchControls(speed)}else{this.showSearchControls(speed)}},showSelectors:function(j){this.selecting=true;this.$el.find(".history-dataset-actions").slideDown(j);_.each(this.hdaViews,function(k){k.showSelector(j)})},hideSelectors:function(j){this.selecting=false;this.$el.find(".history-dataset-actions").slideUp(j);_.each(this.hdaViews,function(k){k.hideSelector(j)})},toggleSelectors:function(j){if(!this.selecting){this.showSelectors(j)}else{this.hideSelectors(j)}},selectAllDatasets:function(k){var j=this.$el.find(".history-select-all-datasets-btn");currMode=j.data("mode");if(currMode==="select"){_.each(this.hdaViews,function(l){l.select(k)});j.data("mode","deselect");j.text(_l("De-select all"))}else{if(currMode==="deselect"){_.each(this.hdaViews,function(l){l.deselect(k)});j.data("mode","select");j.text(_l("Select all"))}}},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(j){return j.selected})},getSelectedHdaCollection:function(){return new f.HDACollection(_.map(this.getSelectedHdaViews(),function(j){return j.model}),{historyId:this.model.id})},showLoadingIndicator:function(k,j,l){j=(j!==undefined)?(j):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,l)}else{this.$el.fadeOut(j);this.indicator.show(k,j,l)}},hideLoadingIndicator:function(j,k){j=(j!==undefined)?(j):(this.fxSpeed);if(this.indicator){this.indicator.hide(j,k)}},displayMessage:function(o,p,n){var l=this;this.scrollToTop();var m=this.$el.find(this.msgsSelector),j=$("<div/>").addClass(o+"message").html(p);if(!_.isEmpty(n)){var k=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(l.messageToModalOptions(o,p,n));return false});j.append(" ",k)}return m.html(j)},messageToModalOptions:function(n,p,m){var j=this,o=$("<div/>"),l={title:"Details"};function k(q){q=_.omit(q,_.functions(q));return["<table>",_.map(q,function(s,r){s=(_.isObject(s))?(k(s)):(s);return'<tr><td style="vertical-align: top; color: grey">'+r+'</td><td style="padding-left: 8px">'+s+"</td></tr>"}).join(""),"</table>"].join("")}if(_.isObject(m)){l.body=o.append(k(m))}else{l.body=o.html(m)}l.buttons={Ok:function(){Galaxy.modal.hide();j.clearMessages()}};return l},clearMessages:function(){var j=this.$el.find(this.msgsSelector);j.empty()},scrollPosition:function(){return this.$el.parent().scrollTop()},scrollTo:function(j){this.$el.parent().scrollTop(j)},scrollToTop:function(){this.$el.parent().scrollTop(0);return this},scrollIntoView:function(k,l){if(!l){this.$el.parent().parent().scrollTop(k);return this}var j=window,m=this.$el.parent().parent(),o=$(j).innerHeight(),n=(o/2)-(l/2);$(m).scrollTop(k-n);return this},scrollToId:function(k){if((!k)||(!this.hdaViews[k])){return this}var j=this.hdaViews[k].$el;this.scrollIntoView(j.offset().top,j.outerHeight());return this},scrollToHid:function(j){var k=this.model.hdas.getByHid(j);if(!k){return this}return this.scrollToId(k.id)},connectToQuotaMeter:function(j){if(!j){return this}this.listenTo(j,"quota:over",this.showQuotaMessage);this.listenTo(j,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(j&&j.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var j=this.$el.find(".quota-message");if(j.is(":hidden")){j.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var j=this.$el.find(".quota-message");if(!j.is(":hidden")){j.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(j){if(!j){return this}this.on("new-storage",function(l,k){if(j&&l){j.findItemByHtml(_l("Include Deleted Datasets")).checked=l.get("show_deleted");j.findItemByHtml(_l("Include Hidden Datasets")).checked=l.get("show_hidden")}});return this},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});e.templates={historyPanel:Handlebars.templates["template-history-historyPanel"],anonHistoryPanel:Handlebars.templates["template-history-historyPanel-anon"]};return{HistoryPanel:e}});
\ No newline at end of file
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/scripts/packed/mvc/ui.js
--- a/static/scripts/packed/mvc/ui.js
+++ b/static/scripts/packed/mvc/ui.js
@@ -1,1 +1,1 @@
-var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("<div/>")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((!a.disabled&&a.target)?(' target="'+a.target+'"'):("")),' href="',((!a.disabled&&a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};(function(){function a(j,p){var d=27,m=13,c=$(j),e=true,g={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(q){},minSearchLen:0,escWillClear:true,oninit:function(){}};function i(q){var r=$(this).parent().children("input");r.val("");r.trigger("clear:searchInput");p.onclear()}function o(r,q){$(this).trigger("search:searchInput",q);if(typeof p.onfirstsearch==="function"&&e){e=false;p.onfirstsearch(q)}else{p.onsearch(q)}}function f(){return['<input type="text" name="',p.name,'" placeholder="',p.placeholder,'" ','class="search-query ',p.classes,'" ',"/>"].join("")}function l(){return $(f()).focus(function(q){$(this).select()}).keyup(function(r){if(r.which===d&&p.escWillClear){i.call(this,r)}else{var q=$(this).val();if((r.which===m)||(p.minSearchLen&&q.length>=p.minSearchLen)){o.call(this,r,q)}else{if(!q.length){i.call(this,r)}}}}).val(p.initialVal)}function k(){return $(['<span class="search-clear fa fa-times-circle" ','title="',_l("clear search (esc)"),'"></span>'].join("")).tooltip({placement:"bottom"}).click(function(q){i.call(this,q)})}function n(){return $(['<span class="search-loading fa fa-spinner fa-spin" ','title="',_l("loading..."),'"></span>'].join("")).hide().tooltip({placement:"bottom"})}function h(){c.find(".search-loading").toggle();c.find(".search-clear").toggle()}if(jQuery.type(p)==="string"){if(p==="toggle-loading"){h()}return c}if(jQuery.type(p)==="object"){p=jQuery.extend(true,g,p)}return c.addClass("search-input").prepend([l(),k(),n()])}jQuery.fn.extend({searchInput:function b(c){return this.each(function(){return a(this,c)})}})}());(function(){function b(m,l){this.currModeIndex=0;return this.init(m,l)}b.prototype.DATA_KEY="mode-button";b.prototype.defaults={modes:[{mode:"default"}]};b.prototype.init=function f(m,l){l=l||{};this.$element=$(m);this.options=jQuery.extend(true,{},this.defaults,l);var o=this;this.$element.click(function n(p){o.callModeFn();o._incModeIndex();$(this).html(o.options.modes[o.currModeIndex].html)});this.currModeIndex=0;if(this.options.initialMode){this.currModeIndex=this._getModeIndex(this.options.initialMode)}return this};b.prototype._getModeIndex=function j(l){for(var m=0;m<this.options.modes.length;m+=1){if(this.options.modes[m].mode===l){return m}}throw new Error("mode not found: "+l)};b.prototype.getCurrMode=function a(){return this.options.modes[this.currModeIndex]};b.prototype.getMode=function g(l){if(!l){return this.getCurrMode()}return this.options.modes[(this._getModeIndex(l))]};b.prototype.hasMode=function k(l){return !!this.getMode(l)};b.prototype.currentMode=function e(){return this.options.modes[this.currModeIndex]};b.prototype.setMode=function c(m){var l=this.getMode(m);this.$element.html(l.html||null);return this};b.prototype._incModeIndex=function d(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};b.prototype.callModeFn=function h(l){var m=this.getMode(l).onclick;if(m&&jQuery.type(m==="function")){return m.call(this)}return undefined};jQuery.fn.extend({modeButton:function i(m){var l=jQuery.makeArray(arguments).slice(1);return this.map(function(){var p=$(this),o=p.data("mode-button");if(jQuery.type(m)==="object"){o=new b(p,m);p.data("mode-button",o)}else{if(o&&jQuery.type(m)==="string"){var n=o[m];if(jQuery.type(n)==="function"){return n.apply(o,l)}}else{if(o){return o}}}return this})}})}());function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b};
\ No newline at end of file
+var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("<div/>")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((!a.disabled&&a.target)?(' target="'+a.target+'"'):("")),' href="',((!a.disabled&&a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};(function(){function a(j,p){var d=27,m=13,c=$(j),e=true,g={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(q){},minSearchLen:0,escWillClear:true,oninit:function(){}};function i(q){var r=$(this).parent().children("input");r.val("");r.trigger("clear:searchInput");p.onclear()}function o(r,q){$(this).trigger("search:searchInput",q);if(typeof p.onfirstsearch==="function"&&e){e=false;p.onfirstsearch(q)}else{p.onsearch(q)}}function f(){return['<input type="text" name="',p.name,'" placeholder="',p.placeholder,'" ','class="search-query ',p.classes,'" ',"/>"].join("")}function l(){return $(f()).focus(function(q){$(this).select()}).keyup(function(r){if(r.which===d&&p.escWillClear){i.call(this,r)}else{var q=$(this).val();if((r.which===m)||(p.minSearchLen&&q.length>=p.minSearchLen)){o.call(this,r,q)}else{if(!q.length){i.call(this,r)}}}}).val(p.initialVal)}function k(){return $(['<span class="search-clear fa fa-times-circle" ','title="',_l("clear search (esc)"),'"></span>'].join("")).tooltip({placement:"bottom"}).click(function(q){i.call(this,q)})}function n(){return $(['<span class="search-loading fa fa-spinner fa-spin" ','title="',_l("loading..."),'"></span>'].join("")).hide().tooltip({placement:"bottom"})}function h(){c.find(".search-loading").toggle();c.find(".search-clear").toggle()}if(jQuery.type(p)==="string"){if(p==="toggle-loading"){h()}return c}if(jQuery.type(p)==="object"){p=jQuery.extend(true,{},g,p)}return c.addClass("search-input").prepend([l(),k(),n()])}jQuery.fn.extend({searchInput:function b(c){return this.each(function(){return a(this,c)})}})}());(function(){function b(m,l){this.currModeIndex=0;return this.init(m,l)}b.prototype.DATA_KEY="mode-button";b.prototype.defaults={modes:[{mode:"default"}]};b.prototype.init=function f(m,l){l=l||{};this.$element=$(m);this.options=jQuery.extend(true,{},this.defaults,l);var o=this;this.$element.click(function n(p){o.callModeFn();o._incModeIndex();$(this).html(o.options.modes[o.currModeIndex].html)});this.currModeIndex=0;if(this.options.initialMode){this.currModeIndex=this._getModeIndex(this.options.initialMode)}return this};b.prototype._getModeIndex=function j(l){for(var m=0;m<this.options.modes.length;m+=1){if(this.options.modes[m].mode===l){return m}}throw new Error("mode not found: "+l)};b.prototype.getCurrMode=function a(){return this.options.modes[this.currModeIndex]};b.prototype.getMode=function g(l){if(!l){return this.getCurrMode()}return this.options.modes[(this._getModeIndex(l))]};b.prototype.hasMode=function k(l){return !!this.getMode(l)};b.prototype.currentMode=function e(){return this.options.modes[this.currModeIndex]};b.prototype.setMode=function c(m){var l=this.getMode(m);this.$element.html(l.html||null);return this};b.prototype._incModeIndex=function d(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};b.prototype.callModeFn=function h(l){var m=this.getMode(l).onclick;if(m&&jQuery.type(m==="function")){return m.call(this)}return undefined};jQuery.fn.extend({modeButton:function i(m){var l=jQuery.makeArray(arguments).slice(1);return this.map(function(){var p=$(this),o=p.data("mode-button");if(jQuery.type(m)==="object"){o=new b(p,m);p.data("mode-button",o)}else{if(o&&jQuery.type(m)==="string"){var n=o[m];if(jQuery.type(n)==="function"){return n.apply(o,l)}}else{if(o){return o}}}return this})}})}());function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b}function dropDownSelect(b,c){c=c||((!_.isEmpty(b))?(b[0]):(""));var a=$(['<div class="dropdown-select btn-group">','<button type="button" class="btn btn-default">','<span class="dropdown-select-selected">'+c+"</span>","</button>","</div>"].join("\n"));if(b&&b.length>1){a.find("button").addClass("dropdown-toggle").attr("data-toggle","dropdown").append(' <span class="caret"></span>');a.append(['<ul class="dropdown-menu" role="menu">',_.map(b,function(e){return['<li><a href="javascript:void(0)">',e,"</a></li>"].join("")}).join("\n"),"</ul>"].join("\n"))}function d(g){var h=$(this),f=h.parents(".dropdown-select"),e=h.text();f.find(".dropdown-select-selected").text(e);f.trigger("change.dropdown-select",e)}a.find("a").click(d);return a}(function(){function e(k,j){return this.init(k,j)}e.prototype.DATA_KEY="filter-control";e.prototype.init=function g(k,j){j=j||{filters:[]};this.$element=$(k).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,j);this.currFilter=this.options.filters[0];return this.render()};e.prototype.render=function d(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};e.prototype._renderKeySelect=function a(){var j=this;var k=this.options.filters.map(function(l){return l.key});this.$keySelect=dropDownSelect(k,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(m,l){j.currFilter=_.findWhere(j.options.filters,{key:l});j.render()._triggerChange()});return this.$keySelect};e.prototype._renderOpSelect=function i(){var j=this,k=this.currFilter.ops;this.$opSelect=dropDownSelect(k,k[0]).addClass("filter-control-op").on("change.dropdown-select",function(m,l){j._triggerChange()});return this.$opSelect};e.prototype._renderValueInput=function c(){var j=this;if(this.currFilter.values){this.$valueSelect=dropDownSelect(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(l,k){j._triggerChange()})}else{this.$valueSelect=$("<input/>").addClass("form-control").on("change",function(k,l){j._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};e.prototype.val=function b(){var k=this.$element.find(".filter-control-key .dropdown-select-selected").text(),m=this.$element.find(".filter-control-op .dropdown-select-selected").text(),j=this.$element.find(".filter-control-value"),l=(j.hasClass("dropdown-select"))?(j.find(".dropdown-select-selected").text()):(j.val());return{key:k,op:m,value:l}};e.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function f(k){var j=jQuery.makeArray(arguments).slice(1);return this.map(function(){var n=$(this),m=n.data(e.prototype.DATA_KEY);if(jQuery.type(k)==="object"){m=new e(n,k);n.data(e.prototype.DATA_KEY,m)}if(m&&jQuery.type(k)==="string"){var l=m[k];if(jQuery.type(l)==="function"){return l.apply(m,j)}}return this})}})}());
\ No newline at end of file
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/scripts/packed/templates/compiled/history-templates.js
--- a/static/scripts/packed/templates/compiled/history-templates.js
+++ b/static/scripts/packed/templates/compiled/history-templates.js
@@ -1,1 +1,1 @@
-(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-body"]=b(function(g,r,p,k,z){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);z=z||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(D,C){var A="",B;A+='\n <div class="dataset-summary">\n ';if(B=p.body){B=B.call(D,{hash:{},data:C})}else{B=D.body;B=typeof B===e?B.apply(D):B}if(B||B===0){A+=B}A+='\n </div>\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';return A}function m(D,C){var A="",B;A+='\n <div class="dataset-summary">\n ';B=p["if"].call(D,D.misc_blurb,{hash:{},inverse:o.noop,fn:o.program(4,l,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p["if"].call(D,D.data_type,{hash:{},inverse:o.noop,fn:o.program(6,j,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p["if"].call(D,D.metadata_dbkey,{hash:{},inverse:o.noop,fn:o.program(9,f,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p["if"].call(D,D.misc_info,{hash:{},inverse:o.noop,fn:o.program(12,x,C),data:C});if(B||B===0){A+=B}A+='\n </div>\n\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';B=p.unless.call(D,D.deleted,{hash:{},inverse:o.noop,fn:o.program(14,w,C),data:C});if(B||B===0){A+=B}A+="\n\n ";return A}function l(D,C){var A="",B;A+='\n <div class="dataset-blurb">\n <span class="value">';if(B=p.misc_blurb){B=B.call(D,{hash:{},data:C})}else{B=D.misc_blurb;B=typeof B===e?B.apply(D):B}A+=d(B)+"</span>\n </div>\n ";return A}function j(E,D){var A="",C,B;A+='\n <div class="dataset-datatype">\n <label class="prompt">';B={hash:{},inverse:o.noop,fn:o.program(7,i,D),data:D};if(C=p.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!p.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+='</label>\n <span class="value">';if(C=p.data_type){C=C.call(E,{hash:{},data:D})}else{C=E.data_type;C=typeof C===e?C.apply(E):C}A+=d(C)+"</span>\n </div>\n ";return A}function i(B,A){return"format"}function f(E,D){var A="",C,B;A+='\n <div class="dataset-dbkey">\n <label class="prompt">';B={hash:{},inverse:o.noop,fn:o.program(10,y,D),data:D};if(C=p.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!p.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+='</label>\n <span class="value">\n ';if(C=p.metadata_dbkey){C=C.call(E,{hash:{},data:D})}else{C=E.metadata_dbkey;C=typeof C===e?C.apply(E):C}A+=d(C)+"\n </span>\n </div>\n ";return A}function y(B,A){return"database"}function x(D,C){var A="",B;A+='\n <div class="dataset-info">\n <span class="value">';if(B=p.misc_info){B=B.call(D,{hash:{},data:C})}else{B=D.misc_info;B=typeof B===e?B.apply(D):B}A+=d(B)+"</span>\n </div>\n ";return A}function w(D,C){var A="",B;A+='\n <div class="tags-display"></div>\n <div class="annotation-display"></div>\n\n <div class="dataset-display-applications">\n ';B=p.each.call(D,D.display_apps,{hash:{},inverse:o.noop,fn:o.program(15,v,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p.each.call(D,D.display_types,{hash:{},inverse:o.noop,fn:o.program(15,v,C),data:C});if(B||B===0){A+=B}A+='\n </div>\n\n <div class="dataset-peek">\n ';B=p["if"].call(D,D.peek,{hash:{},inverse:o.noop,fn:o.program(19,s,C),data:C});if(B||B===0){A+=B}A+="\n </div>\n\n ";return A}function v(D,C){var A="",B;A+='\n <div class="display-application">\n <span class="display-application-location">';if(B=p.label){B=B.call(D,{hash:{},data:C})}else{B=D.label;B=typeof B===e?B.apply(D):B}A+=d(B)+'</span>\n <span class="display-application-links">\n ';B=p.each.call(D,D.links,{hash:{},inverse:o.noop,fn:o.program(16,u,C),data:C});if(B||B===0){A+=B}A+="\n </span>\n </div>\n ";return A}function u(E,D){var A="",C,B;A+='\n <a target="';if(C=p.target){C=C.call(E,{hash:{},data:D})}else{C=E.target;C=typeof C===e?C.apply(E):C}A+=d(C)+'" href="';if(C=p.href){C=C.call(E,{hash:{},data:D})}else{C=E.href;C=typeof C===e?C.apply(E):C}A+=d(C)+'">';B={hash:{},inverse:o.noop,fn:o.program(17,t,D),data:D};if(C=p.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!p.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+="</a>\n ";return A}function t(C,B){var A;if(A=p.text){A=A.call(C,{hash:{},data:B})}else{A=C.text;A=typeof A===e?A.apply(C):A}return d(A)}function s(D,C){var A="",B;A+='\n <pre class="peek">';if(B=p.peek){B=B.call(D,{hash:{},data:C})}else{B=D.peek;B=typeof B===e?B.apply(D):B}if(B||B===0){A+=B}A+="</pre>\n ";return A}q+='<div class="dataset-body">\n ';h=p["if"].call(r,r.body,{hash:{},inverse:o.program(3,m,z),fn:o.program(1,n,z),data:z});if(h||h===0){q+=h}q+="\n</div>";return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-skeleton"]=b(function(f,r,p,k,w){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,f.helpers);w=w||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var x="",z,y;x+='\n <div class="errormessagesmall">\n ';y={hash:{},inverse:o.noop,fn:o.program(2,m,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+=":\n ";y={hash:{},inverse:o.noop,fn:o.program(4,l,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </div>\n ";return x}function m(y,x){return"There was an error getting the data for this dataset"}function l(z,y){var x;if(x=p.error){x=x.call(z,{hash:{},data:y})}else{x=z.error;x=typeof x===e?x.apply(z):x}return d(x)}function j(A,z){var x="",y;x+="\n ";y=p["if"].call(A,A.purged,{hash:{},inverse:o.program(10,v,z),fn:o.program(7,i,z),data:z});if(y||y===0){x+=y}x+="\n ";return x}function i(B,A){var x="",z,y;x+='\n <div class="dataset-purged-msg warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(8,g,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n\n ";return x}function g(y,x){return"This dataset has been deleted and removed from disk."}function v(B,A){var x="",z,y;x+='\n <div class="dataset-deleted-msg warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(11,u,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n ";return x}function u(y,x){return"This dataset has been deleted."}function t(B,A){var x="",z,y;x+='\n <div class="dataset-hidden-msg warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(14,s,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n ";return x}function s(y,x){return"This dataset has been hidden."}q+='<div class="dataset hda">\n <div class="dataset-warnings">\n ';h=p["if"].call(r,r.error,{hash:{},inverse:o.noop,fn:o.program(1,n,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(6,j,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p.unless.call(r,r.visible,{hash:{},inverse:o.noop,fn:o.program(13,t,w),data:w});if(h||h===0){q+=h}q+='\n </div>\n\n <div class="dataset-selector"><span class="fa fa-2x fa-square-o"></span></div>\n <div class="dataset-primary-actions"></div>\n \n <div class="dataset-title-bar clear" tabindex="0">\n <span class="dataset-state-icon state-icon"></span>\n <div class="dataset-title">\n <span class="hda-hid">';if(h=p.hid){h=h.call(r,{hash:{},data:w})}else{h=r.hid;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n <span class="dataset-name">';if(h=p.name){h=h.call(r,{hash:{},data:w})}else{h=r.name;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n </div>\n </div>\n\n <div class="dataset-body"></div>\n</div>';return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel-anon"]=b(function(h,r,p,l,t){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,h.helpers);t=t||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(x,w){var u="",v;u+='\n <div class="history-name">\n ';if(v=p.name){v=v.call(x,{hash:{},data:w})}else{v=x.name;v=typeof v===e?v.apply(x):v}u+=d(v)+"\n </div>\n ";return u}function m(x,w){var u="",v;u+='\n <div class="history-size">';if(v=p.nice_size){v=v.call(x,{hash:{},data:w})}else{v=x.nice_size;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function k(x,w){var u="",v;u+='\n \n <div class="';if(v=p.status){v=v.call(x,{hash:{},data:w})}else{v=x.status;v=typeof v===e?v.apply(x):v}u+=d(v)+'message">';if(v=p.message){v=v.call(x,{hash:{},data:w})}else{v=x.message;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function j(v,u){return"You are over your disk quota"}function g(v,u){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function s(v,u){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls"></div>\n\n <div class="history-title">\n \n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,t),data:t});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(5,k,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(7,j,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(9,g,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(11,s,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(g,r,p,l,x){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);x=x||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var y="",z;y+='\n <div class="history-name">\n ';if(z=p.name){z=z.call(B,{hash:{},data:A})}else{z=B.name;z=typeof z===e?z.apply(B):z}y+=d(z)+"\n </div>\n ";return y}function m(B,A){var y="",z;y+='\n <div class="history-size">';if(z=p.nice_size){z=z.call(B,{hash:{},data:A})}else{z=B.nice_size;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function k(C,B){var y="",A,z;y+='\n <div class="warningmessagesmall"><strong>\n ';z={hash:{},inverse:o.noop,fn:o.program(6,j,B),data:B};if(A=p.local){A=A.call(C,z)}else{A=C.local;A=typeof A===e?A.apply(C):A}if(!p.local){A=c.call(C,A,z)}if(A||A===0){y+=A}y+="\n </strong></div>\n ";return y}function j(z,y){return"You are currently viewing a deleted history!"}function h(B,A){var y="",z;y+='\n \n <div class="';if(z=p.status){z=z.call(B,{hash:{},data:A})}else{z=B.status;z=typeof z===e?z.apply(B):z}y+=d(z)+'message">';if(z=p.message){z=z.call(B,{hash:{},data:A})}else{z=B.message;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function w(z,y){return"You are over your disk quota"}function v(z,y){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function u(z,y){return"Select all"}function t(z,y){return"For all selected"}function s(z,y){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls"></div>\n\n <div class="history-title">\n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n ';i=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(5,k,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(8,h,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(10,w,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(12,v,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n \n <div class="tags-display"></div>\n <div class="annotation-display"></div>\n\n <div class="history-dataset-actions">\n <button class="history-select-all-datasets-btn btn btn-default"\n data-mode="select">';f={hash:{},inverse:o.noop,fn:o.program(14,u,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='</button>\n <button class="history-dataset-action-popup-btn btn btn-default"\n >';f={hash:{},inverse:o.noop,fn:o.program(16,t,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='...</button>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(18,s,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-body"]=b(function(g,r,p,k,z){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);z=z||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(D,C){var A="",B;A+='\n <div class="dataset-summary">\n ';if(B=p.body){B=B.call(D,{hash:{},data:C})}else{B=D.body;B=typeof B===e?B.apply(D):B}if(B||B===0){A+=B}A+='\n </div>\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';return A}function m(D,C){var A="",B;A+='\n <div class="dataset-summary">\n ';B=p["if"].call(D,D.misc_blurb,{hash:{},inverse:o.noop,fn:o.program(4,l,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p["if"].call(D,D.data_type,{hash:{},inverse:o.noop,fn:o.program(6,j,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p["if"].call(D,D.metadata_dbkey,{hash:{},inverse:o.noop,fn:o.program(9,f,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p["if"].call(D,D.misc_info,{hash:{},inverse:o.noop,fn:o.program(12,x,C),data:C});if(B||B===0){A+=B}A+='\n </div>\n\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';B=p.unless.call(D,D.deleted,{hash:{},inverse:o.noop,fn:o.program(14,w,C),data:C});if(B||B===0){A+=B}A+="\n\n ";return A}function l(D,C){var A="",B;A+='\n <div class="dataset-blurb">\n <span class="value">';if(B=p.misc_blurb){B=B.call(D,{hash:{},data:C})}else{B=D.misc_blurb;B=typeof B===e?B.apply(D):B}A+=d(B)+"</span>\n </div>\n ";return A}function j(E,D){var A="",C,B;A+='\n <div class="dataset-datatype">\n <label class="prompt">';B={hash:{},inverse:o.noop,fn:o.program(7,i,D),data:D};if(C=p.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!p.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+='</label>\n <span class="value">';if(C=p.data_type){C=C.call(E,{hash:{},data:D})}else{C=E.data_type;C=typeof C===e?C.apply(E):C}A+=d(C)+"</span>\n </div>\n ";return A}function i(B,A){return"format"}function f(E,D){var A="",C,B;A+='\n <div class="dataset-dbkey">\n <label class="prompt">';B={hash:{},inverse:o.noop,fn:o.program(10,y,D),data:D};if(C=p.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!p.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+='</label>\n <span class="value">\n ';if(C=p.metadata_dbkey){C=C.call(E,{hash:{},data:D})}else{C=E.metadata_dbkey;C=typeof C===e?C.apply(E):C}A+=d(C)+"\n </span>\n </div>\n ";return A}function y(B,A){return"database"}function x(D,C){var A="",B;A+='\n <div class="dataset-info">\n <span class="value">';if(B=p.misc_info){B=B.call(D,{hash:{},data:C})}else{B=D.misc_info;B=typeof B===e?B.apply(D):B}A+=d(B)+"</span>\n </div>\n ";return A}function w(D,C){var A="",B;A+='\n <div class="tags-display"></div>\n <div class="annotation-display"></div>\n\n <div class="dataset-display-applications">\n ';B=p.each.call(D,D.display_apps,{hash:{},inverse:o.noop,fn:o.program(15,v,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p.each.call(D,D.display_types,{hash:{},inverse:o.noop,fn:o.program(15,v,C),data:C});if(B||B===0){A+=B}A+='\n </div>\n\n <div class="dataset-peek">\n ';B=p["if"].call(D,D.peek,{hash:{},inverse:o.noop,fn:o.program(19,s,C),data:C});if(B||B===0){A+=B}A+="\n </div>\n\n ";return A}function v(D,C){var A="",B;A+='\n <div class="display-application">\n <span class="display-application-location">';if(B=p.label){B=B.call(D,{hash:{},data:C})}else{B=D.label;B=typeof B===e?B.apply(D):B}A+=d(B)+'</span>\n <span class="display-application-links">\n ';B=p.each.call(D,D.links,{hash:{},inverse:o.noop,fn:o.program(16,u,C),data:C});if(B||B===0){A+=B}A+="\n </span>\n </div>\n ";return A}function u(E,D){var A="",C,B;A+='\n <a target="';if(C=p.target){C=C.call(E,{hash:{},data:D})}else{C=E.target;C=typeof C===e?C.apply(E):C}A+=d(C)+'" href="';if(C=p.href){C=C.call(E,{hash:{},data:D})}else{C=E.href;C=typeof C===e?C.apply(E):C}A+=d(C)+'">';B={hash:{},inverse:o.noop,fn:o.program(17,t,D),data:D};if(C=p.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!p.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+="</a>\n ";return A}function t(C,B){var A;if(A=p.text){A=A.call(C,{hash:{},data:B})}else{A=C.text;A=typeof A===e?A.apply(C):A}return d(A)}function s(D,C){var A="",B;A+='\n <pre class="peek">';if(B=p.peek){B=B.call(D,{hash:{},data:C})}else{B=D.peek;B=typeof B===e?B.apply(D):B}if(B||B===0){A+=B}A+="</pre>\n ";return A}q+='<div class="dataset-body">\n ';h=p["if"].call(r,r.body,{hash:{},inverse:o.program(3,m,z),fn:o.program(1,n,z),data:z});if(h||h===0){q+=h}q+="\n</div>";return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-skeleton"]=b(function(f,r,p,k,w){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,f.helpers);w=w||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var x="",z,y;x+='\n <div class="errormessagesmall">\n ';y={hash:{},inverse:o.noop,fn:o.program(2,m,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+=":\n ";y={hash:{},inverse:o.noop,fn:o.program(4,l,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </div>\n ";return x}function m(y,x){return"There was an error getting the data for this dataset"}function l(z,y){var x;if(x=p.error){x=x.call(z,{hash:{},data:y})}else{x=z.error;x=typeof x===e?x.apply(z):x}return d(x)}function j(A,z){var x="",y;x+="\n ";y=p["if"].call(A,A.purged,{hash:{},inverse:o.program(10,v,z),fn:o.program(7,i,z),data:z});if(y||y===0){x+=y}x+="\n ";return x}function i(B,A){var x="",z,y;x+='\n <div class="dataset-purged-msg warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(8,g,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n\n ";return x}function g(y,x){return"This dataset has been deleted and removed from disk."}function v(B,A){var x="",z,y;x+='\n <div class="dataset-deleted-msg warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(11,u,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n ";return x}function u(y,x){return"This dataset has been deleted."}function t(B,A){var x="",z,y;x+='\n <div class="dataset-hidden-msg warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(14,s,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n ";return x}function s(y,x){return"This dataset has been hidden."}q+='<div class="dataset hda">\n <div class="dataset-warnings">\n ';h=p["if"].call(r,r.error,{hash:{},inverse:o.noop,fn:o.program(1,n,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(6,j,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p.unless.call(r,r.visible,{hash:{},inverse:o.noop,fn:o.program(13,t,w),data:w});if(h||h===0){q+=h}q+='\n </div>\n\n <div class="dataset-selector"><span class="fa fa-2x fa-square-o"></span></div>\n <div class="dataset-primary-actions"></div>\n \n <div class="dataset-title-bar clear" tabindex="0">\n <span class="dataset-state-icon state-icon"></span>\n <div class="dataset-title">\n <span class="hda-hid">';if(h=p.hid){h=h.call(r,{hash:{},data:w})}else{h=r.hid;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n <span class="dataset-name">';if(h=p.name){h=h.call(r,{hash:{},data:w})}else{h=r.name;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n </div>\n </div>\n\n <div class="dataset-body"></div>\n</div>';return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel-anon"]=b(function(h,r,p,l,t){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,h.helpers);t=t||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(x,w){var u="",v;u+='\n <div class="history-name">\n ';if(v=p.name){v=v.call(x,{hash:{},data:w})}else{v=x.name;v=typeof v===e?v.apply(x):v}u+=d(v)+"\n </div>\n ";return u}function m(x,w){var u="",v;u+='\n <div class="history-size">';if(v=p.nice_size){v=v.call(x,{hash:{},data:w})}else{v=x.nice_size;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function k(x,w){var u="",v;u+='\n \n <div class="';if(v=p.status){v=v.call(x,{hash:{},data:w})}else{v=x.status;v=typeof v===e?v.apply(x):v}u+=d(v)+'message">';if(v=p.message){v=v.call(x,{hash:{},data:w})}else{v=x.message;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function j(v,u){return"You are over your disk quota"}function g(v,u){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function s(v,u){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls">\n <div class="history-search-input"></div>\n </div>\n\n <div class="history-title">\n \n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,t),data:t});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(5,k,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(7,j,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(9,g,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(11,s,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel-anon"]=b(function(h,r,p,l,t){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,h.helpers);t=t||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(x,w){var u="",v;u+='\n <div class="history-name">\n ';if(v=p.name){v=v.call(x,{hash:{},data:w})}else{v=x.name;v=typeof v===e?v.apply(x):v}u+=d(v)+"\n </div>\n ";return u}function m(x,w){var u="",v;u+='\n <div class="history-size">';if(v=p.nice_size){v=v.call(x,{hash:{},data:w})}else{v=x.nice_size;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function k(x,w){var u="",v;u+='\n \n <div class="';if(v=p.status){v=v.call(x,{hash:{},data:w})}else{v=x.status;v=typeof v===e?v.apply(x):v}u+=d(v)+'message">';if(v=p.message){v=v.call(x,{hash:{},data:w})}else{v=x.message;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function j(v,u){return"You are over your disk quota"}function g(v,u){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function s(v,u){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls">\n <div class="history-search-input"></div>\n </div>\n\n <div class="history-title">\n \n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,t),data:t});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(5,k,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(7,j,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(9,g,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(11,s,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(g,r,p,l,x){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);x=x||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var y="",z;y+='\n <div class="history-name">\n ';if(z=p.name){z=z.call(B,{hash:{},data:A})}else{z=B.name;z=typeof z===e?z.apply(B):z}y+=d(z)+"\n </div>\n ";return y}function m(B,A){var y="",z;y+='\n <div class="history-size">';if(z=p.nice_size){z=z.call(B,{hash:{},data:A})}else{z=B.nice_size;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function k(C,B){var y="",A,z;y+='\n <div class="warningmessagesmall"><strong>\n ';z={hash:{},inverse:o.noop,fn:o.program(6,j,B),data:B};if(A=p.local){A=A.call(C,z)}else{A=C.local;A=typeof A===e?A.apply(C):A}if(!p.local){A=c.call(C,A,z)}if(A||A===0){y+=A}y+="\n </strong></div>\n ";return y}function j(z,y){return"You are currently viewing a deleted history!"}function h(B,A){var y="",z;y+='\n \n <div class="';if(z=p.status){z=z.call(B,{hash:{},data:A})}else{z=B.status;z=typeof z===e?z.apply(B):z}y+=d(z)+'message">';if(z=p.message){z=z.call(B,{hash:{},data:A})}else{z=B.message;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function w(z,y){return"You are over your disk quota"}function v(z,y){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function u(z,y){return"Select all"}function t(z,y){return"For all selected"}function s(z,y){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls">\n <div class="history-search-input"></div>\n </div>\n\n <div class="history-title">\n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n ';i=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(5,k,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(8,h,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(10,w,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(12,v,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n \n <div class="tags-display"></div>\n <div class="annotation-display"></div>\n\n <div class="history-dataset-actions">\n <button class="history-select-all-datasets-btn btn btn-default"\n data-mode="select">';f={hash:{},inverse:o.noop,fn:o.program(14,u,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='</button>\n <button class="history-dataset-action-popup-btn btn btn-default"\n >';f={hash:{},inverse:o.noop,fn:o.program(16,t,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='...</button>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(18,s,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(g,r,p,l,x){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);x=x||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var y="",z;y+='\n <div class="history-name">\n ';if(z=p.name){z=z.call(B,{hash:{},data:A})}else{z=B.name;z=typeof z===e?z.apply(B):z}y+=d(z)+"\n </div>\n ";return y}function m(B,A){var y="",z;y+='\n <div class="history-size">';if(z=p.nice_size){z=z.call(B,{hash:{},data:A})}else{z=B.nice_size;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function k(C,B){var y="",A,z;y+='\n <div class="warningmessagesmall"><strong>\n ';z={hash:{},inverse:o.noop,fn:o.program(6,j,B),data:B};if(A=p.local){A=A.call(C,z)}else{A=C.local;A=typeof A===e?A.apply(C):A}if(!p.local){A=c.call(C,A,z)}if(A||A===0){y+=A}y+="\n </strong></div>\n ";return y}function j(z,y){return"You are currently viewing a deleted history!"}function h(B,A){var y="",z;y+='\n \n <div class="';if(z=p.status){z=z.call(B,{hash:{},data:A})}else{z=B.status;z=typeof z===e?z.apply(B):z}y+=d(z)+'message">';if(z=p.message){z=z.call(B,{hash:{},data:A})}else{z=B.message;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function w(z,y){return"You are over your disk quota"}function v(z,y){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function u(z,y){return"Select all"}function t(z,y){return"For all selected"}function s(z,y){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls">\n <div class="history-search-input"></div>\n </div>\n\n <div class="history-title">\n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n ';i=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(5,k,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(8,h,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(10,w,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(12,v,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n \n <div class="tags-display"></div>\n <div class="annotation-display"></div>\n\n <div class="history-dataset-actions">\n <button class="history-select-all-datasets-btn btn btn-default"\n data-mode="select">';f={hash:{},inverse:o.noop,fn:o.program(14,u,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='</button>\n <button class="history-dataset-action-popup-btn btn btn-default"\n >';f={hash:{},inverse:o.noop,fn:o.program(16,t,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='...</button>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(18,s,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();
\ No newline at end of file
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/scripts/packed/templates/compiled/template-history-historyPanel-anon.js
--- a/static/scripts/packed/templates/compiled/template-history-historyPanel-anon.js
+++ b/static/scripts/packed/templates/compiled/template-history-historyPanel-anon.js
@@ -1,1 +1,1 @@
-(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel-anon"]=b(function(h,r,p,l,t){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,h.helpers);t=t||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(x,w){var u="",v;u+='\n <div class="history-name">\n ';if(v=p.name){v=v.call(x,{hash:{},data:w})}else{v=x.name;v=typeof v===e?v.apply(x):v}u+=d(v)+"\n </div>\n ";return u}function m(x,w){var u="",v;u+='\n <div class="history-size">';if(v=p.nice_size){v=v.call(x,{hash:{},data:w})}else{v=x.nice_size;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function k(x,w){var u="",v;u+='\n \n <div class="';if(v=p.status){v=v.call(x,{hash:{},data:w})}else{v=x.status;v=typeof v===e?v.apply(x):v}u+=d(v)+'message">';if(v=p.message){v=v.call(x,{hash:{},data:w})}else{v=x.message;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function j(v,u){return"You are over your disk quota"}function g(v,u){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function s(v,u){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls"></div>\n\n <div class="history-title">\n \n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,t),data:t});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(5,k,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(7,j,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(9,g,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(11,s,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel-anon"]=b(function(h,r,p,l,t){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,h.helpers);t=t||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(x,w){var u="",v;u+='\n <div class="history-name">\n ';if(v=p.name){v=v.call(x,{hash:{},data:w})}else{v=x.name;v=typeof v===e?v.apply(x):v}u+=d(v)+"\n </div>\n ";return u}function m(x,w){var u="",v;u+='\n <div class="history-size">';if(v=p.nice_size){v=v.call(x,{hash:{},data:w})}else{v=x.nice_size;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function k(x,w){var u="",v;u+='\n \n <div class="';if(v=p.status){v=v.call(x,{hash:{},data:w})}else{v=x.status;v=typeof v===e?v.apply(x):v}u+=d(v)+'message">';if(v=p.message){v=v.call(x,{hash:{},data:w})}else{v=x.message;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function j(v,u){return"You are over your disk quota"}function g(v,u){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function s(v,u){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls">\n <div class="history-search-input"></div>\n </div>\n\n <div class="history-title">\n \n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,t),data:t});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(5,k,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(7,j,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(9,g,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(11,s,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();
\ No newline at end of file
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/scripts/packed/templates/compiled/template-history-historyPanel.js
--- a/static/scripts/packed/templates/compiled/template-history-historyPanel.js
+++ b/static/scripts/packed/templates/compiled/template-history-historyPanel.js
@@ -1,1 +1,1 @@
-(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(g,r,p,l,x){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);x=x||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var y="",z;y+='\n <div class="history-name">\n ';if(z=p.name){z=z.call(B,{hash:{},data:A})}else{z=B.name;z=typeof z===e?z.apply(B):z}y+=d(z)+"\n </div>\n ";return y}function m(B,A){var y="",z;y+='\n <div class="history-size">';if(z=p.nice_size){z=z.call(B,{hash:{},data:A})}else{z=B.nice_size;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function k(C,B){var y="",A,z;y+='\n <div class="warningmessagesmall"><strong>\n ';z={hash:{},inverse:o.noop,fn:o.program(6,j,B),data:B};if(A=p.local){A=A.call(C,z)}else{A=C.local;A=typeof A===e?A.apply(C):A}if(!p.local){A=c.call(C,A,z)}if(A||A===0){y+=A}y+="\n </strong></div>\n ";return y}function j(z,y){return"You are currently viewing a deleted history!"}function h(B,A){var y="",z;y+='\n \n <div class="';if(z=p.status){z=z.call(B,{hash:{},data:A})}else{z=B.status;z=typeof z===e?z.apply(B):z}y+=d(z)+'message">';if(z=p.message){z=z.call(B,{hash:{},data:A})}else{z=B.message;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function w(z,y){return"You are over your disk quota"}function v(z,y){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function u(z,y){return"Select all"}function t(z,y){return"For all selected"}function s(z,y){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls"></div>\n\n <div class="history-title">\n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n ';i=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(5,k,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(8,h,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(10,w,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(12,v,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n \n <div class="tags-display"></div>\n <div class="annotation-display"></div>\n\n <div class="history-dataset-actions">\n <button class="history-select-all-datasets-btn btn btn-default"\n data-mode="select">';f={hash:{},inverse:o.noop,fn:o.program(14,u,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='</button>\n <button class="history-dataset-action-popup-btn btn btn-default"\n >';f={hash:{},inverse:o.noop,fn:o.program(16,t,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='...</button>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(18,s,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(g,r,p,l,x){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);x=x||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var y="",z;y+='\n <div class="history-name">\n ';if(z=p.name){z=z.call(B,{hash:{},data:A})}else{z=B.name;z=typeof z===e?z.apply(B):z}y+=d(z)+"\n </div>\n ";return y}function m(B,A){var y="",z;y+='\n <div class="history-size">';if(z=p.nice_size){z=z.call(B,{hash:{},data:A})}else{z=B.nice_size;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function k(C,B){var y="",A,z;y+='\n <div class="warningmessagesmall"><strong>\n ';z={hash:{},inverse:o.noop,fn:o.program(6,j,B),data:B};if(A=p.local){A=A.call(C,z)}else{A=C.local;A=typeof A===e?A.apply(C):A}if(!p.local){A=c.call(C,A,z)}if(A||A===0){y+=A}y+="\n </strong></div>\n ";return y}function j(z,y){return"You are currently viewing a deleted history!"}function h(B,A){var y="",z;y+='\n \n <div class="';if(z=p.status){z=z.call(B,{hash:{},data:A})}else{z=B.status;z=typeof z===e?z.apply(B):z}y+=d(z)+'message">';if(z=p.message){z=z.call(B,{hash:{},data:A})}else{z=B.message;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function w(z,y){return"You are over your disk quota"}function v(z,y){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function u(z,y){return"Select all"}function t(z,y){return"For all selected"}function s(z,y){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls">\n <div class="history-search-input"></div>\n </div>\n\n <div class="history-title">\n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n ';i=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(5,k,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(8,h,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(10,w,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(12,v,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n \n <div class="tags-display"></div>\n <div class="annotation-display"></div>\n\n <div class="history-dataset-actions">\n <button class="history-select-all-datasets-btn btn btn-default"\n data-mode="select">';f={hash:{},inverse:o.noop,fn:o.program(14,u,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='</button>\n <button class="history-dataset-action-popup-btn btn btn-default"\n >';f={hash:{},inverse:o.noop,fn:o.program(16,t,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='...</button>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(18,s,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();
\ No newline at end of file
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/scripts/templates/compiled/history-templates.js
--- a/static/scripts/templates/compiled/history-templates.js
+++ b/static/scripts/templates/compiled/history-templates.js
@@ -363,7 +363,103 @@
return "Your history is empty. Click 'Get Data' on the left pane to start";
}
- buffer += "<div class=\"history-controls\">\n <div class=\"history-search-controls\"></div>\n\n <div class=\"history-title\">\n "
+ buffer += "<div class=\"history-controls\">\n <div class=\"history-search-controls\">\n <div class=\"history-search-input\"></div>\n </div>\n\n <div class=\"history-title\">\n "
+ + "\n ";
+ stack1 = helpers['if'].call(depth0, depth0.name, {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n </div>\n\n <div class=\"history-subtitle clear\">\n ";
+ stack1 = helpers['if'].call(depth0, depth0.nice_size, {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n\n <div class=\"history-secondary-actions\"></div>\n </div>\n\n <div class=\"message-container\">\n ";
+ stack1 = helpers['if'].call(depth0, depth0.message, {hash:{},inverse:self.noop,fn:self.program(5, program5, data),data:data});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n </div>\n\n <div class=\"quota-message errormessage\">\n ";
+ options = {hash:{},inverse:self.noop,fn:self.program(7, program7, data),data:data};
+ if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
+ else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += ".\n ";
+ options = {hash:{},inverse:self.noop,fn:self.program(9, program9, data),data:data};
+ if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
+ else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += ".\n </div>\n\n </div>"
+ + "\n\n "
+ + "\n <div class=\"datasets-list\"></div>\n\n <div class=\"empty-history-message infomessagesmall\">\n ";
+ options = {hash:{},inverse:self.noop,fn:self.program(11, program11, data),data:data};
+ if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
+ else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n </div>";
+ return buffer;
+ });
+})();(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-history-historyPanel-anon'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ this.compilerInfo = [4,'>= 1.0.0'];
+helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+ var buffer = "", stack1, options, functionType="function", escapeExpression=this.escapeExpression, self=this, blockHelperMissing=helpers.blockHelperMissing;
+
+function program1(depth0,data) {
+
+ var buffer = "", stack1;
+ buffer += "\n <div class=\"history-name\">\n ";
+ if (stack1 = helpers.name) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
+ else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ buffer += escapeExpression(stack1)
+ + "\n </div>\n ";
+ return buffer;
+ }
+
+function program3(depth0,data) {
+
+ var buffer = "", stack1;
+ buffer += "\n <div class=\"history-size\">";
+ if (stack1 = helpers.nice_size) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
+ else { stack1 = depth0.nice_size; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ buffer += escapeExpression(stack1)
+ + "</div>\n ";
+ return buffer;
+ }
+
+function program5(depth0,data) {
+
+ var buffer = "", stack1;
+ buffer += "\n "
+ + "\n <div class=\"";
+ if (stack1 = helpers.status) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
+ else { stack1 = depth0.status; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ buffer += escapeExpression(stack1)
+ + "message\">";
+ if (stack1 = helpers.message) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
+ else { stack1 = depth0.message; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ buffer += escapeExpression(stack1)
+ + "</div>\n ";
+ return buffer;
+ }
+
+function program7(depth0,data) {
+
+
+ return "You are over your disk quota";
+ }
+
+function program9(depth0,data) {
+
+
+ return "Tool execution is on hold until your disk usage drops below your allocated quota";
+ }
+
+function program11(depth0,data) {
+
+
+ return "Your history is empty. Click 'Get Data' on the left pane to start";
+ }
+
+ buffer += "<div class=\"history-controls\">\n <div class=\"history-search-controls\">\n <div class=\"history-search-input\"></div>\n </div>\n\n <div class=\"history-title\">\n "
+ "\n ";
stack1 = helpers['if'].call(depth0, depth0.name, {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
@@ -489,7 +585,147 @@
return "Your history is empty. Click 'Get Data' on the left pane to start";
}
- buffer += "<div class=\"history-controls\">\n <div class=\"history-search-controls\"></div>\n\n <div class=\"history-title\">\n ";
+ buffer += "<div class=\"history-controls\">\n <div class=\"history-search-controls\">\n <div class=\"history-search-input\"></div>\n </div>\n\n <div class=\"history-title\">\n ";
+ stack1 = helpers['if'].call(depth0, depth0.name, {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n </div>\n\n <div class=\"history-subtitle clear\">\n ";
+ stack1 = helpers['if'].call(depth0, depth0.nice_size, {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n\n <div class=\"history-secondary-actions\"></div>\n </div>\n\n ";
+ stack1 = helpers['if'].call(depth0, depth0.deleted, {hash:{},inverse:self.noop,fn:self.program(5, program5, data),data:data});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n\n <div class=\"message-container\">\n ";
+ stack1 = helpers['if'].call(depth0, depth0.message, {hash:{},inverse:self.noop,fn:self.program(8, program8, data),data:data});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n </div>\n\n <div class=\"quota-message errormessage\">\n ";
+ options = {hash:{},inverse:self.noop,fn:self.program(10, program10, data),data:data};
+ if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
+ else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += ".\n ";
+ options = {hash:{},inverse:self.noop,fn:self.program(12, program12, data),data:data};
+ if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
+ else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += ".\n </div>\n \n <div class=\"tags-display\"></div>\n <div class=\"annotation-display\"></div>\n\n <div class=\"history-dataset-actions\">\n <button class=\"history-select-all-datasets-btn btn btn-default\"\n data-mode=\"select\">";
+ options = {hash:{},inverse:self.noop,fn:self.program(14, program14, data),data:data};
+ if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
+ else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "</button>\n <button class=\"history-dataset-action-popup-btn btn btn-default\"\n >";
+ options = {hash:{},inverse:self.noop,fn:self.program(16, program16, data),data:data};
+ if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
+ else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "...</button>\n </div>\n\n </div>"
+ + "\n\n "
+ + "\n <div class=\"datasets-list\"></div>\n\n <div class=\"empty-history-message infomessagesmall\">\n ";
+ options = {hash:{},inverse:self.noop,fn:self.program(18, program18, data),data:data};
+ if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
+ else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n </div>";
+ return buffer;
+ });
+})();(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-history-historyPanel'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ this.compilerInfo = [4,'>= 1.0.0'];
+helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+ var buffer = "", stack1, options, functionType="function", escapeExpression=this.escapeExpression, self=this, blockHelperMissing=helpers.blockHelperMissing;
+
+function program1(depth0,data) {
+
+ var buffer = "", stack1;
+ buffer += "\n <div class=\"history-name\">\n ";
+ if (stack1 = helpers.name) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
+ else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ buffer += escapeExpression(stack1)
+ + "\n </div>\n ";
+ return buffer;
+ }
+
+function program3(depth0,data) {
+
+ var buffer = "", stack1;
+ buffer += "\n <div class=\"history-size\">";
+ if (stack1 = helpers.nice_size) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
+ else { stack1 = depth0.nice_size; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ buffer += escapeExpression(stack1)
+ + "</div>\n ";
+ return buffer;
+ }
+
+function program5(depth0,data) {
+
+ var buffer = "", stack1, options;
+ buffer += "\n <div class=\"warningmessagesmall\"><strong>\n ";
+ options = {hash:{},inverse:self.noop,fn:self.program(6, program6, data),data:data};
+ if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
+ else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n </strong></div>\n ";
+ return buffer;
+ }
+function program6(depth0,data) {
+
+
+ return "You are currently viewing a deleted history!";
+ }
+
+function program8(depth0,data) {
+
+ var buffer = "", stack1;
+ buffer += "\n "
+ + "\n <div class=\"";
+ if (stack1 = helpers.status) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
+ else { stack1 = depth0.status; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ buffer += escapeExpression(stack1)
+ + "message\">";
+ if (stack1 = helpers.message) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
+ else { stack1 = depth0.message; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+ buffer += escapeExpression(stack1)
+ + "</div>\n ";
+ return buffer;
+ }
+
+function program10(depth0,data) {
+
+
+ return "You are over your disk quota";
+ }
+
+function program12(depth0,data) {
+
+
+ return "Tool execution is on hold until your disk usage drops below your allocated quota";
+ }
+
+function program14(depth0,data) {
+
+
+ return "Select all";
+ }
+
+function program16(depth0,data) {
+
+
+ return "For all selected";
+ }
+
+function program18(depth0,data) {
+
+
+ return "Your history is empty. Click 'Get Data' on the left pane to start";
+ }
+
+ buffer += "<div class=\"history-controls\">\n <div class=\"history-search-controls\">\n <div class=\"history-search-input\"></div>\n </div>\n\n <div class=\"history-title\">\n ";
stack1 = helpers['if'].call(depth0, depth0.name, {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n </div>\n\n <div class=\"history-subtitle clear\">\n ";
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/scripts/templates/compiled/template-history-historyPanel-anon.js
--- a/static/scripts/templates/compiled/template-history-historyPanel-anon.js
+++ b/static/scripts/templates/compiled/template-history-historyPanel-anon.js
@@ -61,7 +61,7 @@
return "Your history is empty. Click 'Get Data' on the left pane to start";
}
- buffer += "<div class=\"history-controls\">\n <div class=\"history-search-controls\"></div>\n\n <div class=\"history-title\">\n "
+ buffer += "<div class=\"history-controls\">\n <div class=\"history-search-controls\">\n <div class=\"history-search-input\"></div>\n </div>\n\n <div class=\"history-title\">\n "
+ "\n ";
stack1 = helpers['if'].call(depth0, depth0.name, {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/scripts/templates/compiled/template-history-historyPanel.js
--- a/static/scripts/templates/compiled/template-history-historyPanel.js
+++ b/static/scripts/templates/compiled/template-history-historyPanel.js
@@ -91,7 +91,7 @@
return "Your history is empty. Click 'Get Data' on the left pane to start";
}
- buffer += "<div class=\"history-controls\">\n <div class=\"history-search-controls\"></div>\n\n <div class=\"history-title\">\n ";
+ buffer += "<div class=\"history-controls\">\n <div class=\"history-search-controls\">\n <div class=\"history-search-input\"></div>\n </div>\n\n <div class=\"history-title\">\n ";
stack1 = helpers['if'].call(depth0, depth0.name, {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
if(stack1 || stack1 === 0) { buffer += stack1; }
buffer += "\n </div>\n\n <div class=\"history-subtitle clear\">\n ";
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/scripts/templates/history-templates.html
--- a/static/scripts/templates/history-templates.html
+++ b/static/scripts/templates/history-templates.html
@@ -2,7 +2,9 @@
<script type="text/template" class="template-history" id="template-history-historyPanel"><div class="history-controls">
- <div class="history-search-controls"></div>
+ <div class="history-search-controls">
+ <div class="history-search-input"></div>
+ </div><div class="history-title">
{{#if name }}
@@ -63,7 +65,9 @@
<script type="text/template" class="template-history" id="template-history-historyPanel-anon"><div class="history-controls">
- <div class="history-search-controls"></div>
+ <div class="history-search-controls">
+ <div class="history-search-input"></div>
+ </div><div class="history-title">
{{! wouldn't this always be 'unnamed history'? }}
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/style/blue/base.css
--- a/static/style/blue/base.css
+++ b/static/style/blue/base.css
@@ -1564,7 +1564,7 @@
.icon-btn:hover{background-color:white;color:maroon}
.icon-btn.disabled{background-color:transparent;color:rgba(0,0,0,0.05)}
.search-input .search-query{width:100%;padding-right:24px}
-.search-input .search-clear,.search-input .search-loading{position:relative;float:right;left:-4px;top:-23px;font-size:1.4em;line-height:23px;color:grey}
+.search-input .search-clear,.search-input .search-loading{position:relative;display:inline-block;float:right;margin-top:-23px;margin-right:4px;font-size:1.4em;line-height:23px;color:grey}
.search-input .search-clear:hover{color:#303030}
#history-refresh-button,#history-options-button{display:inline-block;height:20px;width:20px;text-align:center;line-height:17px;font-size:1.2em;padding:0px}
.history-panel [class$="messagesmall"]{margin:0px}
diff -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f -r ef98f48690fc9ce5d1b8f662a2e91f384514013d static/style/src/less/history.less
--- a/static/style/src/less/history.less
+++ b/static/style/src/less/history.less
@@ -69,9 +69,12 @@
.search-loading {
// it places the icons on the right of the bar (and puts the lotion on its skin)
position : relative;
+ display : inline-block;
+
float : right;
- left : -4px;
- top : -23px;
+ margin-top : -23px;
+ margin-right : 4px;
+
font-size : 1.4em;
line-height : 23px;
color : grey;
@@ -123,6 +126,13 @@
padding: 0px;
.history-search-controls {
+ .history-search-input {
+ //margin-right: 22px;
+ }
+ //.history-search-advanced {
+ // margin-top : -21px;
+ // float : right;
+ //}
display: none;
padding: 0px 0px 8px 0px;
}
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: Restrict population of repository dependency installation containers during the tool shed's install and test run if they were last populated 12 hours previously or less.
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/4c6b4cc9e060/
Changeset: 4c6b4cc9e060
User: greg
Date: 2014-01-07 19:15:46
Summary: Restrict population of repository dependency installation containers during the tool shed's install and test run if they were last populated 12 hours previously or less.
Affected #: 1 file
diff -r 5fbd5656e3dde2ba871f39f01a0841cea656a669 -r 4c6b4cc9e060f98ec0c060bbbf8f5ac9cd54c75f test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -24,6 +24,9 @@
import tool_shed.util.shed_util_common as suc
import urllib
+from datetime import datetime
+from datetime import timedelta
+
from common import update
from galaxy.util import asbool
@@ -312,7 +315,7 @@
dependency tree will be inspected.
"""
log.debug( 'Checking revision %s of repository %s owned by %s for missing repository dependencies.' % \
- ( str( repository.changeset_revision ), str( repository.name ), str( repository.changeset_revision ) ) )
+ ( str( repository.changeset_revision ), str( repository.name ), str( repository.owner ) ) )
missing_repository_dependencies = repository.missing_repository_dependencies
for missing_required_repository in missing_repository_dependencies:
log.debug( 'Revision %s of required repository %s owned by %s has status %s.' % \
@@ -331,7 +334,7 @@
dependency tree will be inspected.
"""
log.debug( 'Checking revision %s of repository %s owned by %s for missing tool dependencies.' % \
- ( str( repository.changeset_revision ), str( repository.name ), str( repository.changeset_revision ) ) )
+ ( str( repository.changeset_revision ), str( repository.name ), str( repository.owner ) ) )
missing_tool_dependencies = repository.missing_tool_dependencies
for missing_tool_dependency in missing_tool_dependencies:
log.debug( 'Tool dependency %s version %s has status %s.' % \
@@ -517,6 +520,26 @@
static_style_dir = os.path.join( static_dir, 'june_2007_style', 'blue' ),
static_robots_txt = os.path.join( static_dir, 'robots.txt' ) )
+def get_time_last_tested( tool_shed_url, encoded_repository_metadata_id ):
+ """
+ Return the datetime value stored in the Tool Shed's repository_metadata.time_last_tested column
+ via the Tool Shed API.
+ """
+ error_message = ''
+ parts = [ 'api', 'repository_revisions', encoded_repository_metadata_id ]
+ api_url = get_api_url( base=tool_shed_url, parts=parts )
+ repository_metadata_dict, error_message = json_from_url( api_url )
+ if error_message:
+ return None, error_message
+ if isinstance( repository_metadata_dict, dict ):
+ # The tool_test_results used to be stored as a single dictionary rather than a list, but we currently
+ # return a list.
+ time_last_tested = repository_metadata_dict.get( 'time_last_tested', None )
+ return time_last_tested, error_message
+ else:
+ error_message = 'The url %s returned the invalid repository_metadata_dict %s' % ( str( api_url ), str( repository_metadata_dict ) )
+ return None, error_message
+
def get_tool_test_results_dict( tool_test_results_dicts ):
if tool_test_results_dicts:
# Inspect the tool_test_results_dict for the last test run to make sure it contains only a test_environment
@@ -862,7 +885,7 @@
name = str( repository_dependencies_dict[ 'name' ] )
owner = str( repository_dependencies_dict[ 'owner' ] )
changeset_revision = str( repository_dependencies_dict[ 'changeset_revision' ] )
- log.debug( 'Checking installation containers for revision %s of required repository %s owned by %s' % \
+ log.debug( 'Checking installation containers for revision %s of repository dependency %s owned by %s' % \
( changeset_revision, name, owner ) )
required_repository_metadata_id = repository_dependencies_dict[ 'id' ]
# Get the current list of tool_test_results dictionaries associated with the repository_metadata
@@ -870,78 +893,92 @@
tool_test_results_dicts, error_message = get_tool_test_results_dicts( galaxy_tool_shed_url,
required_repository_metadata_id )
if error_message:
- log.debug( 'Cannot check install container for version %s of repository %s owned by %s ' % \
+ log.debug( 'Cannot check install container for version %s of repository dependency %s owned by %s ' % \
( changeset_revision, name, owner ) )
log.debug( 'due to the following error getting tool_test_results:\n%s' % str( error_message ) )
else:
- # Inspect the tool_test_results_dict for the last test run to see if it has not yet been populated
+ # Check the required repository's time_last_tested value to see if its tool_test_results column
+ # has been updated within the past 12 hours.
+ twelve_hours_ago = datetime.utcnow() - timedelta( hours=12 )
+ time_last_tested, error_message = get_time_last_tested( galaxy_tool_shed_url, repository_metadata_id )
+ if time_last_tested is not None and time_last_tested < twelve_hours_ago:
+ log.debug( 'The install containers for version %s of repository dependency %s owned by %s have been ' % \
+ ( changeset_revision, name, owner ) )
+ log.debug( 'populated within the past 12 hours (likely in this test run), so skipping this check.' )
+ continue
+ if time_last_tested is None:
+ log.debug( 'The time_last_tested column value is None for version %s of repository dependency %s owned by %s.' % \
+ ( changeset_revision, name, owner ) )
+ elif time_last_tested < twelve_hours_ago:
+ log.debug( 'Version %s of repository dependency %s owned by %s was last tested less than 12 hours ago.' % \
+ ( changeset_revision, name, owner ) )
+ else:
+ log.debug( 'Version %s of repository dependency %s owned by %s was last tested more than 12 hours ago.' % \
+ ( changeset_revision, name, owner ) )
+ # Inspect the tool_test_results_dict for the last test run to see if it has not yet been populated.
if len( tool_test_results_dicts ) == 0:
- populated = False
tool_test_results_dict = {}
else:
tool_test_results_dict = tool_test_results_dicts[ 0 ]
if len( tool_test_results_dict ) <= 1:
- populated = False
tool_test_results_dict = tool_test_results_dicts.pop( 0 )
elif len( tool_test_results_dict ) == 2 and \
'test_environment' in tool_test_results_dict and \
'missing_test_components' in tool_test_results_dict:
- populated = False
tool_test_results_dict = tool_test_results_dicts.pop( 0 )
else:
- populated = True
- if not populated:
- # Make sure all expected entries are available in the tool_test_results_dict.
- tool_test_results_dict = initialize_tool_tests_results_dict( app, tool_test_results_dict )
- # Get the installed repository record from the Galaxy database.
- cleaned_tool_shed_url = clean_tool_shed_url( galaxy_tool_shed_url )
- required_repository = \
- suc.get_tool_shed_repository_by_shed_name_owner_changeset_revision( app,
- cleaned_tool_shed_url,
- name,
- owner,
- changeset_revision )
- if required_repository is not None:
- repository_identifier_dict = dict( name=name, owner=owner, changeset_revision=changeset_revision )
- if required_repository.is_installed:
- # The required_repository was successfully installed, so populate the installation
- # containers (success and error) for the repository's immediate dependencies.
- params, install_and_test_statistics_dict, tool_test_results_dict = \
- populate_dependency_install_containers( app,
- required_repository,
- repository_identifier_dict,
- install_and_test_statistics_dict,
- tool_test_results_dict )
- response_dict = save_test_results_for_changeset_revision( galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dependencies_dict,
- params,
- can_update_tool_shed )
- log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( changeset_revision, name, owner, str( response_dict ) ) )
- log.debug('\n=============================================================\n' )
- else:
- # The required repository's installation failed.
- tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = str( required_repository.error_message )
- params = dict( test_install_error=True,
- do_not_test=False )
- response_dict = save_test_results_for_changeset_revision( galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dependencies_dict,
- params,
- can_update_tool_shed )
- log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( changeset_revision, name, owner, str( response_dict ) ) )
- log.debug('\n=============================================================\n' )
+ tool_test_results_dict = {}
+ # Make sure all expected entries are available in the tool_test_results_dict.
+ tool_test_results_dict = initialize_tool_tests_results_dict( app, tool_test_results_dict )
+ # Get the installed repository record from the Galaxy database.
+ cleaned_tool_shed_url = clean_tool_shed_url( galaxy_tool_shed_url )
+ required_repository = \
+ suc.get_tool_shed_repository_by_shed_name_owner_changeset_revision( app,
+ cleaned_tool_shed_url,
+ name,
+ owner,
+ changeset_revision )
+ if required_repository is not None:
+ repository_identifier_dict = dict( name=name, owner=owner, changeset_revision=changeset_revision )
+ if required_repository.is_installed:
+ # The required_repository was successfully installed, so populate the installation
+ # containers (success and error) for the repository's immediate dependencies.
+ params, install_and_test_statistics_dict, tool_test_results_dict = \
+ populate_dependency_install_containers( app,
+ required_repository,
+ repository_identifier_dict,
+ install_and_test_statistics_dict,
+ tool_test_results_dict )
+ response_dict = save_test_results_for_changeset_revision( galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dependencies_dict,
+ params,
+ can_update_tool_shed )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('\n=============================================================\n' )
else:
- log.debug( 'Cannot retrieve revision %s of required repository %s owned by %s from the database ' % \
- ( changeset_revision, name, owner ) )
- log.debug( 'so tool_test_results cannot be saved.' )
- log.debug( 'The attributes used to retrieve the record are:' )
- log.debug( '\ntool_shed: %s name: %s owner: %s changeset_revision: %s' % \
- ( cleaned_tool_shed_url, name, owner, changeset_revision ) )
+ # The required repository's installation failed.
+ tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = str( required_repository.error_message )
+ params = dict( test_install_error=True,
+ do_not_test=False )
+ response_dict = save_test_results_for_changeset_revision( galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dependencies_dict,
+ params,
+ can_update_tool_shed )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('\n=============================================================\n' )
+ else:
+ log.debug( 'Cannot retrieve revision %s of required repository %s owned by %s from the database ' % \
+ ( changeset_revision, name, owner ) )
+ log.debug( 'so tool_test_results cannot be saved.' )
+ log.debug( 'The attributes used to retrieve the record are:\n' )
+ log.debug( 'tool_shed: %s name: %s owner: %s changeset_revision: %s' % \
+ ( cleaned_tool_shed_url, name, owner, changeset_revision ) )
def run_tests( test_config ):
loader = nose.loader.TestLoader( config=test_config )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/5fbd5656e3dd/
Changeset: 5fbd5656e3dd
User: dannon
Date: 2014-01-07 15:47:37
Summary: Update cloudlaunch default AMI
Affected #: 2 files
diff -r 2f1c111663308e0d8f081c2b443aa39808641046 -r 5fbd5656e3dde2ba871f39f01a0841cea656a669 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -313,7 +313,7 @@
self.datatypes_config = kwargs.get( 'datatypes_config_file', 'datatypes_conf.xml' )
# Cloud configuration options
self.enable_cloud_launch = string_as_bool( kwargs.get( 'enable_cloud_launch', False ) )
- self.cloudlaunch_default_ami = kwargs.get( 'cloudlaunch_default_ami', 'ami-118bfc78' )
+ self.cloudlaunch_default_ami = kwargs.get( 'cloudlaunch_default_ami', 'ami-a7dbf6ce' )
# Galaxy messaging (AMQP) configuration options
self.amqp = {}
try:
diff -r 2f1c111663308e0d8f081c2b443aa39808641046 -r 5fbd5656e3dde2ba871f39f01a0841cea656a669 universe_wsgi.ini.sample
--- a/universe_wsgi.ini.sample
+++ b/universe_wsgi.ini.sample
@@ -426,7 +426,7 @@
# Cloud Launch
#enable_cloud_launch = False
-#cloudlaunch_default_ami = ami-118bfc78
+#cloudlaunch_default_ami = ami-a7dbf6ce
# -- Advanced proxy features
https://bitbucket.org/galaxy/galaxy-central/commits/a0f7e0e5f05a/
Changeset: a0f7e0e5f05a
Branch: stable
User: dannon
Date: 2014-01-07 15:47:37
Summary: Update cloudlaunch default AMI
Affected #: 2 files
diff -r 27350e3fc09bd36fea964c703b5ea787e2215de4 -r a0f7e0e5f05af4112ded852cf61c138582c99c04 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -299,7 +299,7 @@
self.datatypes_config = kwargs.get( 'datatypes_config_file', 'datatypes_conf.xml' )
# Cloud configuration options
self.enable_cloud_launch = string_as_bool( kwargs.get( 'enable_cloud_launch', False ) )
- self.cloudlaunch_default_ami = kwargs.get( 'cloudlaunch_default_ami', 'ami-118bfc78' )
+ self.cloudlaunch_default_ami = kwargs.get( 'cloudlaunch_default_ami', 'ami-a7dbf6ce' )
# Galaxy messaging (AMQP) configuration options
self.amqp = {}
try:
diff -r 27350e3fc09bd36fea964c703b5ea787e2215de4 -r a0f7e0e5f05af4112ded852cf61c138582c99c04 universe_wsgi.ini.sample
--- a/universe_wsgi.ini.sample
+++ b/universe_wsgi.ini.sample
@@ -419,7 +419,7 @@
# Cloud Launch
#enable_cloud_launch = False
-#cloudlaunch_default_ami = ami-118bfc78
+#cloudlaunch_default_ami = ami-a7dbf6ce
# -- Advanced proxy features
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: Fix for the tool shed's install and test framework.
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/2f1c11166330/
Changeset: 2f1c11166330
User: greg
Date: 2014-01-07 15:23:01
Summary: Fix for the tool shed's install and test framework.
Affected #: 1 file
diff -r 22fb2b5a46ac2fd1a97b6dd50124b29962e2103b -r 2f1c111663308e0d8f081c2b443aa39808641046 test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -207,6 +207,23 @@
def shutdown( self ):
pass
+def clean_tool_shed_url( base_url ):
+ """Eliminate the protocol from the received base_url and return the possibly altered url."""
+ # The tool_shed value stored in the tool_shed_repository record does not include the protocol, but does
+ # include the port if one exists.
+ if base_url:
+ if base_url.find( '://' ) > -1:
+ try:
+ protocol, base = base_url.split( '://' )
+ except ValueError, e:
+ # The received base_url must be an invalid url.
+ log.debug( "Returning unchanged invalid base_url from clean_tool_shed_url: %s" % str( base_url ) )
+ return base_url
+ return base.rstrip( '/' )
+ return base_url.rstrip( '/' )
+ log.debug( "Returning base_url from clean_tool_shed_url: %s" % str( base_url ) )
+ return base_url
+
def display_repositories_by_owner( repository_dicts ):
# Group summary display by repository owner.
repository_dicts_by_owner = {}
@@ -877,9 +894,10 @@
# Make sure all expected entries are available in the tool_test_results_dict.
tool_test_results_dict = initialize_tool_tests_results_dict( app, tool_test_results_dict )
# Get the installed repository record from the Galaxy database.
+ cleaned_tool_shed_url = clean_tool_shed_url( galaxy_tool_shed_url )
required_repository = \
suc.get_tool_shed_repository_by_shed_name_owner_changeset_revision( app,
- galaxy_tool_shed_url,
+ cleaned_tool_shed_url,
name,
owner,
changeset_revision )
@@ -919,8 +937,11 @@
log.debug('\n=============================================================\n' )
else:
log.debug( 'Cannot retrieve revision %s of required repository %s owned by %s from the database ' % \
- ( changeset_revision, name, owner, str( response_dict ) ) )
+ ( changeset_revision, name, owner ) )
log.debug( 'so tool_test_results cannot be saved.' )
+ log.debug( 'The attributes used to retrieve the record are:' )
+ log.debug( '\ntool_shed: %s name: %s owner: %s changeset_revision: %s' % \
+ ( cleaned_tool_shed_url, name, owner, changeset_revision ) )
def run_tests( test_config ):
loader = nose.loader.TestLoader( config=test_config )
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: Handle case where a repository dependency has not yet been created during the current test run in the tool shed's install and test framework.
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/22fb2b5a46ac/
Changeset: 22fb2b5a46ac
User: greg
Date: 2014-01-07 12:56:19
Summary: Handle case where a repository dependency has not yet been created during the current test run in the tool shed's install and test framework.
Affected #: 1 file
diff -r 19391092ba1b49999eca9ced0ae9864015ea3199 -r 22fb2b5a46ac2fd1a97b6dd50124b29962e2103b test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -883,39 +883,44 @@
name,
owner,
changeset_revision )
- repository_identifier_dict = dict( name=name, owner=owner, changeset_revision=changeset_revision )
- if required_repository.is_installed:
- # The required_repository was successfully installed, so populate the installation
- # containers (success and error) for the repository's immediate dependencies.
- params, install_and_test_statistics_dict, tool_test_results_dict = \
- populate_dependency_install_containers( app,
- required_repository,
- repository_identifier_dict,
- install_and_test_statistics_dict,
- tool_test_results_dict )
- response_dict = save_test_results_for_changeset_revision( galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dependencies_dict,
- params,
- can_update_tool_shed )
- log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ if required_repository is not None:
+ repository_identifier_dict = dict( name=name, owner=owner, changeset_revision=changeset_revision )
+ if required_repository.is_installed:
+ # The required_repository was successfully installed, so populate the installation
+ # containers (success and error) for the repository's immediate dependencies.
+ params, install_and_test_statistics_dict, tool_test_results_dict = \
+ populate_dependency_install_containers( app,
+ required_repository,
+ repository_identifier_dict,
+ install_and_test_statistics_dict,
+ tool_test_results_dict )
+ response_dict = save_test_results_for_changeset_revision( galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dependencies_dict,
+ params,
+ can_update_tool_shed )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('\n=============================================================\n' )
+ else:
+ # The required repository's installation failed.
+ tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = str( required_repository.error_message )
+ params = dict( test_install_error=True,
+ do_not_test=False )
+ response_dict = save_test_results_for_changeset_revision( galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dependencies_dict,
+ params,
+ can_update_tool_shed )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('\n=============================================================\n' )
+ else:
+ log.debug( 'Cannot retrieve revision %s of required repository %s owned by %s from the database ' % \
( changeset_revision, name, owner, str( response_dict ) ) )
- log.debug('\n=============================================================\n' )
- else:
- # The required repository's installation failed.
- tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = str( required_repository.error_message )
- params = dict( test_install_error=True,
- do_not_test=False )
- response_dict = save_test_results_for_changeset_revision( galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dependencies_dict,
- params,
- can_update_tool_shed )
- log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( changeset_revision, name, owner, str( response_dict ) ) )
- log.debug('\n=============================================================\n' )
+ log.debug( 'so tool_test_results cannot be saved.' )
def run_tests( test_config ):
loader = nose.loader.TestLoader( config=test_config )
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: Handle exceptions attempting to retrieve the tool_test_results column from the repository_metadata table in the tool shed.
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/19391092ba1b/
Changeset: 19391092ba1b
User: greg
Date: 2014-01-07 03:35:57
Summary: Handle exceptions attempting to retrieve the tool_test_results column from the repository_metadata table in the tool shed.
Affected #: 1 file
diff -r 47e869a73907381c094b691185c88d620a13fa97 -r 19391092ba1b49999eca9ced0ae9864015ea3199 test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -532,13 +532,17 @@
error_message = ''
parts = [ 'api', 'repository_revisions', encoded_repository_metadata_id ]
api_url = get_api_url( base=tool_shed_url, parts=parts )
- repository_metadata, error_message = json_from_url( api_url )
+ repository_metadata_dict, error_message = json_from_url( api_url )
if error_message:
return None, error_message
- # The tool_test_results used to be stored as a single dictionary rather than a list, but we currently
- # return a list.
- tool_test_results = listify( repository_metadata.get( 'tool_test_results', [] ) )
- return tool_test_results, error_message
+ if isinstance( repository_metadata_dict, dict ):
+ # The tool_test_results used to be stored as a single dictionary rather than a list, but we currently
+ # return a list.
+ tool_test_results = listify( repository_metadata_dict.get( 'tool_test_results', [] ) )
+ return tool_test_results, error_message
+ else:
+ error_message = 'The url %s returned the invalid repository_metadata_dict %s' % ( str( api_url ), str( repository_metadata_dict ) )
+ return None, error_message
def get_webapp_global_conf():
"""Return the global_conf dictionary sent as the first argument to app_factory."""
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: Always initialize the tool shed install and tes framework's tool_test_results_dict.
by commits-noreply@bitbucket.org 07 Jan '14
by commits-noreply@bitbucket.org 07 Jan '14
07 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/47e869a73907/
Changeset: 47e869a73907
User: greg
Date: 2014-01-07 03:13:25
Summary: Always initialize the tool shed install and tes framework's tool_test_results_dict.
Affected #: 1 file
diff -r 9df289e311209975ab34c288d8229096683af0e0 -r 47e869a73907381c094b691185c88d620a13fa97 test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -856,7 +856,7 @@
# Inspect the tool_test_results_dict for the last test run to see if it has not yet been populated
if len( tool_test_results_dicts ) == 0:
populated = False
- tool_test_results_dict = initialize_tool_tests_results_dict( app, {} )
+ tool_test_results_dict = {}
else:
tool_test_results_dict = tool_test_results_dicts[ 0 ]
if len( tool_test_results_dict ) <= 1:
@@ -870,6 +870,8 @@
else:
populated = True
if not populated:
+ # Make sure all expected entries are available in the tool_test_results_dict.
+ tool_test_results_dict = initialize_tool_tests_results_dict( app, tool_test_results_dict )
# Get the installed repository record from the Galaxy database.
required_repository = \
suc.get_tool_shed_repository_by_shed_name_owner_changeset_revision( app,
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Enhance the tool shed's install and test framework to populate and store information about the installation of each repository dependency in addition to the repository itself. This changeset also include miscellaneous typo fixes and code cleanup.
by commits-noreply@bitbucket.org 06 Jan '14
by commits-noreply@bitbucket.org 06 Jan '14
06 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/9df289e31120/
Changeset: 9df289e31120
User: greg
Date: 2014-01-06 22:30:27
Summary: Enhance the tool shed's install and test framework to populate and store information about the installation of each repository dependency in addition to the repository itself. This changeset also include miscellaneous typo fixes and code cleanup.
Affected #: 11 files
diff -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 -r 9df289e311209975ab34c288d8229096683af0e0 lib/galaxy/model/tool_shed_install/__init__.py
--- a/lib/galaxy/model/tool_shed_install/__init__.py
+++ b/lib/galaxy/model/tool_shed_install/__init__.py
@@ -279,6 +279,10 @@
def is_deactivated_or_installed( self ):
return self.status in [ self.installation_status.DEACTIVATED,
self.installation_status.INSTALLED ]
+
+ @property
+ def is_installed( self ):
+ return self.status == self.installation_status.INSTALLED
@property
def is_latest_installable_revision( self ):
diff -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 -r 9df289e311209975ab34c288d8229096683af0e0 lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py
--- a/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py
+++ b/lib/galaxy/webapps/galaxy/api/tool_shed_repositories.py
@@ -171,7 +171,7 @@
:param tool_shed_url (required): the base URL of the Tool Shed from which to install the Repository
:param name (required): the name of the Repository
:param owner (required): the owner of the Repository
- :param changset_revision (required): the changset_revision of the RepositoryMetadata object associated with the Repository
+ :param changeset_revision (required): the changeset_revision of the RepositoryMetadata object associated with the Repository
:param new_tool_panel_section_label (optional): label of a new section to be added to the Galaxy tool panel in which to load
tools contained in the Repository. Either this parameter must be an empty string or
the tool_panel_section_id parameter must be an empty string or both must be an empty
@@ -372,7 +372,7 @@
:param tool_shed_urls: the base URLs of the Tool Sheds from which to install a specified Repository
:param names: the names of the Repositories to be installed
:param owners: the owners of the Repositories to be installed
- :param changset_revisions: the changset_revisions of each RepositoryMetadata object associated with each Repository to be installed
+ :param changeset_revisions: the changeset_revisions of each RepositoryMetadata object associated with each Repository to be installed
:param new_tool_panel_section_label: optional label of a new section to be added to the Galaxy tool panel in which to load
tools contained in the Repository. Either this parameter must be an empty string or
the tool_panel_section_id parameter must be an empty string, as both cannot be used.
@@ -454,7 +454,7 @@
:param tool_shed_url (required): the base URL of the Tool Shed from which the Repository was installed
:param name (required): the name of the Repository
:param owner (required): the owner of the Repository
- :param changset_revision (required): the changset_revision of the RepositoryMetadata object associated with the Repository
+ :param changeset_revision (required): the changeset_revision of the RepositoryMetadata object associated with the Repository
"""
# Get the information about the repository to be installed from the payload.
tool_shed_url = payload.get( 'tool_shed_url', '' )
diff -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 -r 9df289e311209975ab34c288d8229096683af0e0 lib/galaxy/webapps/tool_shed/api/repositories.py
--- a/lib/galaxy/webapps/tool_shed/api/repositories.py
+++ b/lib/galaxy/webapps/tool_shed/api/repositories.py
@@ -60,7 +60,7 @@
:param name: the name of the Repository
:param owner: the owner of the Repository
- :param changset_revision: the changset_revision of the RepositoryMetadata object associated with the Repository
+ :param changeset_revision: the changeset_revision of the RepositoryMetadata object associated with the Repository
Returns a list of the following dictionaries::
- a dictionary defining the Repository. For example:
diff -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 -r 9df289e311209975ab34c288d8229096683af0e0 lib/galaxy/webapps/tool_shed/api/repository_revisions.py
--- a/lib/galaxy/webapps/tool_shed/api/repository_revisions.py
+++ b/lib/galaxy/webapps/tool_shed/api/repository_revisions.py
@@ -30,7 +30,7 @@
:param tool_shed_url (required): the base URL of the Tool Shed from which the Repository was installed
:param name (required): the name of the Repository
:param owner (required): the owner of the Repository
- :param changset_revision (required): the changset_revision of the RepositoryMetadata object associated with the Repository
+ :param changeset_revision (required): the changeset_revision of the RepositoryMetadata object associated with the Repository
:param export_repository_dependencies (optional): whether to export repository dependencies - defaults to False
:param download_dir (optional): the local directory to which to download the archive - defaults to /tmp
"""
diff -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 -r 9df289e311209975ab34c288d8229096683af0e0 lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -1273,8 +1273,8 @@
"""Return a tool shed repository database record defined by the id."""
# This method is used only in Galaxy, not the tool shed.
return trans.install_model.context.query( trans.install_model.ToolShedRepository ) \
- .filter( trans.install_model.ToolShedRepository.table.c.id == trans.security.decode_id( repository_id ) ) \
- .first()
+ .filter( trans.install_model.ToolShedRepository.table.c.id == trans.security.decode_id( repository_id ) ) \
+ .first()
def get_tool_shed_repository_by_shed_name_owner_changeset_revision( app, tool_shed, name, owner, changeset_revision ):
"""Return a tool shed repository database record defined by the combination of a tool_shed, repository name, repository owner and current changeet_revision."""
diff -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 -r 9df289e311209975ab34c288d8229096683af0e0 lib/tool_shed/util/tool_dependency_util.py
--- a/lib/tool_shed/util/tool_dependency_util.py
+++ b/lib/tool_shed/util/tool_dependency_util.py
@@ -415,10 +415,13 @@
tool_dependency_name=tool_dependency.name,
tool_dependency_version=tool_dependency.version )
if remove_installation_path:
- # This will be True only in the case where an exception was encountered during the installation process after the installation
- # path was created, but before any information was written to the installation log, and the tool dependency status was not
- # set to "Installed" or "Error".
+ # This will be True only in the case where an exception was encountered during the installation process after
+ # the installation path was created but before any information was written to the installation log and the
+ # tool dependency status was not set to "Installed" or "Error".
if os.path.exists( install_dir ):
+ log.debug( 'Attempting to remove installation directory %s for version %s of tool dependency %s %s' % \
+ ( str( install_dir ), str( tool_dependency.version ), str( tool_dependency.type ), str( tool_dependency.name ) ) )
+ log.debug( 'due to the following installation error:\n%s' % str( error_message ) )
try:
shutil.rmtree( install_dir )
except Exception, e:
diff -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 -r 9df289e311209975ab34c288d8229096683af0e0 test/install_and_test_tool_shed_repositories/base/twilltestcase.py
--- a/test/install_and_test_tool_shed_repositories/base/twilltestcase.py
+++ b/test/install_and_test_tool_shed_repositories/base/twilltestcase.py
@@ -41,13 +41,10 @@
self.shed_tools_dict = {}
self.home()
- def initiate_installation_process( self,
- install_tool_dependencies=False,
- install_repository_dependencies=True,
- no_changes=True,
+ def initiate_installation_process( self, install_tool_dependencies=False, install_repository_dependencies=True, no_changes=True,
new_tool_panel_section_label=None ):
html = self.last_page()
- # Since the installation process is by necessity asynchronous, we have to get the parameters to 'manually' initiate the
+ # Since the installation process is by necessity asynchronous we have to get the parameters to 'manually' initiate the
# installation process. This regex will return the tool shed repository IDs in group(1), the encoded_kwd parameter in
# group(2), and the reinstalling flag in group(3) and pass them to the manage_repositories method in the Galaxy
# admin_toolshed controller.
diff -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 -r 9df289e311209975ab34c288d8229096683af0e0 test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -289,12 +289,36 @@
else:
return suc.INITIAL_CHANGELOG_HASH, error_message
+def get_missing_repository_dependencies( repository ):
+ """
+ Return the entire list of missing repository dependencies for the received repository. The entire
+ dependency tree will be inspected.
+ """
+ log.debug( 'Checking revision %s of repository %s owned by %s for missing repository dependencies.' % \
+ ( str( repository.changeset_revision ), str( repository.name ), str( repository.changeset_revision ) ) )
+ missing_repository_dependencies = repository.missing_repository_dependencies
+ for missing_required_repository in missing_repository_dependencies:
+ log.debug( 'Revision %s of required repository %s owned by %s has status %s.' % \
+ ( str( missing_required_repository.changeset_revision ),
+ str( missing_required_repository.name ),
+ str( missing_required_repository.owner ),
+ str( missing_required_repository.status ) ) )
+ for repository_dependency in repository.repository_dependencies:
+ if repository_dependency.missing_repository_dependencies:
+ missing_repository_dependencies.extend( get_missing_repository_dependencies( repository_dependency ) )
+ return missing_repository_dependencies
+
def get_missing_tool_dependencies( repository ):
+ """
+ Return the entire list of missing tool dependencies for the received repository. The entire
+ dependency tree will be inspected.
+ """
log.debug( 'Checking revision %s of repository %s owned by %s for missing tool dependencies.' % \
( str( repository.changeset_revision ), str( repository.name ), str( repository.changeset_revision ) ) )
missing_tool_dependencies = repository.missing_tool_dependencies
- for tool_dependency in repository.tool_dependencies:
- log.debug( 'Tool dependency %s version %s has status %s.' % ( tool_dependency.name, tool_dependency.version, tool_dependency.status ) )
+ for missing_tool_dependency in missing_tool_dependencies:
+ log.debug( 'Tool dependency %s version %s has status %s.' % \
+ ( str( missing_tool_dependency.name ), str( missing_tool_dependency.version ), str( missing_tool_dependency.status ) ) )
for repository_dependency in repository.repository_dependencies:
if repository_dependency.includes_tool_dependencies:
missing_tool_dependencies.extend( get_missing_tool_dependencies( repository_dependency ) )
@@ -336,8 +360,7 @@
for baseline_repository_dict in baseline_repository_dicts:
# We need to get some details from the tool shed API, such as repository name and owner, to pass on to the
# module that will generate the install methods.
- repository_dict, error_message = \
- get_repository_dict( galaxy_tool_shed_url, baseline_repository_dict )
+ repository_dict, error_message = get_repository_dict( galaxy_tool_shed_url, baseline_repository_dict )
if error_message:
log.debug( 'Error getting additional details from the API: %s' % str( error_message ) )
else:
@@ -439,6 +462,19 @@
extended_dict[ 'latest_revision' ] = str( latest_changeset_revision )
return extended_dict, error_message
+def get_repository_dependencies_dicts( url, encoded_repository_metadata_id ):
+ """
+ Return a list if dictionaries that define the repository dependencies of the repository defined by the
+ received repository_dict.
+ """
+ error_message = ''
+ parts = [ 'api', 'repository_revisions', encoded_repository_metadata_id, 'repository_dependencies' ]
+ api_url = get_api_url( base=url, parts=parts )
+ repository_dependencies_dicts, error_message = json_from_url( api_url )
+ if error_message:
+ return None, error_message
+ return repository_dependencies_dicts, error_message
+
def get_repository_tuple_from_elem( elem ):
attributes = elem.attrib
name = attributes.get( 'name', None )
@@ -511,55 +547,6 @@
global_conf.update( get_static_settings() )
return global_conf
-def handle_missing_dependencies( app, repository, missing_tool_dependencies, repository_dict, tool_test_results_dicts,
- tool_test_results_dict, params, can_update_tool_shed ):
- """Handle missing repository or tool dependencies for an installed repository."""
- # If a tool dependency fails to install correctly, this should be considered an installation error,
- # and functional tests should be skipped, since the tool dependency needs to be correctly installed
- # for the test to be considered reliable.
- log.debug( 'The following dependencies of revision %s of repository %s owned by %s are missing.' % \
- ( str( repository.changeset_revision ), str( repository.name ), str( repository.owner ) ) )
- # In keeping with the standard display layout, add the error message to the dict for each tool individually.
- for dependency in repository.missing_tool_dependencies:
- name = str( dependency.name )
- type = str( dependency.type )
- version = str( dependency.version )
- error_message = unicodify( dependency.error_message )
- log.debug( 'Missing tool dependency %s of type %s version %s: %s' % ( name, type, version, error_message ) )
- missing_tool_dependency_info_dict = dict( type=type,
- name=name,
- version=version,
- error_message=error_message )
- tool_test_results_dict[ 'installation_errors' ][ 'tool_dependencies' ].append( missing_tool_dependency_info_dict )
- for dependency in repository.missing_repository_dependencies:
- tool_shed = str( dependency.tool_shed )
- name = str( dependency.name )
- owner = str( dependency.owner )
- changeset_revision = str( dependency.changeset_revision )
- error_message = unicodify( dependency.error_message )
- log.debug( 'Missing repository dependency %s changeset revision %s owned by %s: %s' % \
- ( name, changeset_revision, owner, error_message ) )
- missing_repository_dependency_info_dict = dict( tool_shed=tool_shed,
- name=name,
- owner=owner,
- changeset_revision=changeset_revision,
- error_message=error_message )
- tool_test_results_dict[ 'installation_errors' ][ 'repository_dependencies' ]\
- .append( missing_repository_dependency_info_dict )
- # Record the status of this repository in the tool shed.
- log.debug('=============================================================' )
- log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( str( repository.changeset_revision ), str( repository.name ), str( repository.owner ), str( tool_test_results_dict ) ) )
- response_dict = register_test_result( galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params,
- can_update_tool_shed )
- log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( str( repository.changeset_revision ), str( repository.name ), str( repository.owner ), str( response_dict ) ) )
- log.debug('=============================================================' )
-
def initialize_install_and_test_statistics_dict( test_framework ):
# Initialize a dictionary for the summary that will be printed to stdout.
install_and_test_statistics_dict = {}
@@ -747,32 +734,42 @@
log.debug( 'The exclude file %s defines no repositories to be excluded from testing.' % str( xml_filename ) )
return exclude_list
-def register_installed_and_missing_dependencies( app, repository, repository_identifier_dict, install_and_test_statistics_dict,
- tool_test_results_dict ):
- # The repository was successfully installed.
- log.debug( 'Installation succeeded for revision %s of repository %s owned by %s.' % \
- ( str( repository.changeset_revision ), str( repository.name ), str( repository.owner ) ) )
+def populate_dependency_install_containers( app, repository, repository_identifier_dict, install_and_test_statistics_dict,
+ tool_test_results_dict ):
+ """
+ Populate the installation containers (successful or errors) for the received repository's (which
+ itself was successfully installed) immediate repository and tool dependencies. The entire dependency
+ tree is not handled here.
+ """
+ repository_name = str( repository.name )
+ repository_owner = str( repository.owner )
+ repository_changeset_revision = str( repository.changeset_revision )
install_and_test_statistics_dict[ 'successful_repository_installations' ].append( repository_identifier_dict )
tool_test_results_dict[ 'successful_installations' ][ 'current_repository' ].append( repository_identifier_dict )
params = dict( test_install_error=False,
do_not_test=False )
if repository.missing_repository_dependencies:
+ log.debug( 'The following repository dependencies for revision %s of repository %s owned by %s have installation errors:' % \
+ ( repository_changeset_revision, repository_name, repository_owner ) )
params[ 'test_install_error' ] = True
# Keep statistics for this repository's repository dependencies that resulted in installation errors.
for missing_repository_dependency in repository.missing_repository_dependencies:
tool_shed = str( missing_repository_dependency.tool_shed )
name = str( missing_repository_dependency.name )
owner = str( missing_repository_dependency.owner )
- changset_revision = str( missing_repository_dependency.changeset_revision )
+ changeset_revision = str( missing_repository_dependency.changeset_revision )
error_message = unicodify( missing_repository_dependency.error_message )
+ log.debug( 'Revision %s of repository %s owned by %s:\n%s' % ( changeset_revision, name, owner, error_message ) )
missing_repository_dependency_info_dict = dict( tool_shed=tool_shed,
name=name,
owner=owner,
- changset_revision=changset_revision,
+ changeset_revision=changeset_revision,
error_message=error_message )
- install_and_test_statistics_dict[ 'repositories_with_installation_error' ].append( missing_repository_dependency_dict )
+ install_and_test_statistics_dict[ 'repositories_with_installation_error' ].append( missing_repository_dependency_info_dict )
tool_test_results_dict[ 'installation_errors' ][ 'repository_dependencies' ].append( missing_repository_dependency_info_dict )
if repository.missing_tool_dependencies:
+ log.debug( 'The following tool dependencies for revision %s of repository %s owned by %s have installation errors:' % \
+ ( repository_changeset_revision, repository_name, repository_owner ) )
params[ 'test_install_error' ] = True
# Keep statistics for this repository's tool dependencies that resulted in installation errors.
for missing_tool_dependency in repository.missing_tool_dependencies:
@@ -780,6 +777,7 @@
type = str( missing_tool_dependency.type )
version = str( missing_tool_dependency.version )
error_message = unicodify( missing_tool_dependency.error_message )
+ log.debug( 'Version %s of tool dependency %s %s:\n%s' % ( version, type, name, error_message ) )
missing_tool_dependency_info_dict = dict( type=type,
name=name,
version=version,
@@ -787,12 +785,15 @@
install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ].append( missing_tool_dependency_info_dict )
tool_test_results_dict[ 'installation_errors' ][ 'tool_dependencies' ].append( missing_tool_dependency_info_dict )
if repository.installed_repository_dependencies:
+ log.debug( 'The following repository dependencies for revision %s of repository %s owned by %s are installed:' % \
+ ( repository_changeset_revision, repository_name, repository_owner ) )
# Keep statistics for this repository's tool dependencies that resulted in successful installations.
for repository_dependency in repository.installed_repository_dependencies:
tool_shed = str( repository_dependency.tool_shed )
name = str( repository_dependency.name )
owner = str( repository_dependency.owner )
changeset_revision = str( repository_dependency.changeset_revision )
+ log.debug( 'Revision %s of repository %s owned by %s.' % ( changeset_revision, name, owner ) )
repository_dependency_info_dict = dict( tool_shed=tool_shed,
name=name,
owner=owner,
@@ -800,12 +801,15 @@
install_and_test_statistics_dict[ 'successful_repository_installations' ].append( repository_dependency_info_dict )
tool_test_results_dict[ 'successful_installations' ][ 'repository_dependencies' ].append( repository_dependency_info_dict )
if repository.installed_tool_dependencies:
+ log.debug( 'The following tool dependencies for revision %s of repository %s owned by %s are installed:' % \
+ ( repository_changeset_revision, repository_name, repository_owner ) )
# Keep statistics for this repository's tool dependencies that resulted in successful installations.
for tool_dependency in repository.installed_tool_dependencies:
name = str( tool_dependency.name )
type = str( tool_dependency.type )
version = str( tool_dependency.version )
installation_directory = tool_dependency.installation_directory( app )
+ log.debug( 'Version %s of tool dependency %s %s is installed in: %s' % ( version, type, name, installation_directory ) )
tool_dependency_info_dict = dict( type=type,
name=name,
version=version,
@@ -814,29 +818,98 @@
tool_test_results_dict[ 'successful_installations' ][ 'tool_dependencies' ].append( tool_dependency_info_dict )
return params, install_and_test_statistics_dict, tool_test_results_dict
-def register_test_result( url, tool_test_results_dicts, tool_test_results_dict, repository_dict, params, can_update_tool_shed ):
+def populate_install_containers_for_repository_dependencies( app, repository, repository_metadata_id, install_and_test_statistics_dict,
+ can_update_tool_shed ):
"""
- Update the repository metadata tool_test_results and appropriate flags using the Tool Shed API. This method
- updates tool_test_results with the relevant data, sets the do_not_test and tools_functionally correct flags
- to the appropriate values and updates the time_last_tested field to the value of the received time_tested.
+ The handle_repository_dependencies check box is always checked when a repository is installed, so the
+ tool_test_results dictionary must be inspected for each dependency to make sure installation containers
+ (success or errors) have been populated. Since multiple repositories can depend on the same repository,
+ some of the containers may have been populated during a previous installation.
"""
- if can_update_tool_shed:
- metadata_revision_id = repository_dict.get( 'id', None )
- if metadata_revision_id is not None:
- log.debug( 'Updating tool_test_results for repository_metadata id %s.' % str( metadata_revision_id ) )
- tool_test_results_dicts.insert( 0, tool_test_results_dict )
- params[ 'tool_test_results' ] = tool_test_results_dicts
- # Set the time_last_tested entry so that the repository_metadata.time_last_tested will be set in the tool shed.
- params[ 'time_last_tested' ] = 'This entry will result in this value being set via the Tool Shed API.'
- url = '%s' % ( suc.url_join( galaxy_tool_shed_url,'api', 'repository_revisions', str( metadata_revision_id ) ) )
- try:
- return update( tool_shed_api_key, url, params, return_formatted=False )
- except Exception, e:
- log.exception( 'Error updating tool_test_results for repository_metadata id %s:\n%s' % \
- ( str( metadata_revision_id ), str( e ) ) )
- return {}
+ # Get the list of dictionaries that define the received repository's repository dependencies
+ # via the Tool Shed API.
+ repository_name = str( repository.name )
+ repository_owner = str( repository.owner )
+ repository_changeset_revision = str( repository.changeset_revision )
+ repository_dependencies_dicts, error_message = get_repository_dependencies_dicts( galaxy_tool_shed_url, repository_metadata_id )
+ if error_message:
+ log.debug( 'Cannot check or populate repository dependency install containers for version %s of repository %s owned by %s ' % \
+ ( repository_changeset_revision, repository_name, repository_owner ) )
+ log.debug( 'due to the following error getting repository_dependencies_dicts:\n%s' % str( error_message ) )
else:
- return {}
+ for repository_dependencies_dict in repository_dependencies_dicts:
+ name = str( repository_dependencies_dict[ 'name' ] )
+ owner = str( repository_dependencies_dict[ 'owner' ] )
+ changeset_revision = str( repository_dependencies_dict[ 'changeset_revision' ] )
+ log.debug( 'Checking installation containers for revision %s of required repository %s owned by %s' % \
+ ( changeset_revision, name, owner ) )
+ required_repository_metadata_id = repository_dependencies_dict[ 'id' ]
+ # Get the current list of tool_test_results dictionaries associated with the repository_metadata
+ # record in the tool shed.
+ tool_test_results_dicts, error_message = get_tool_test_results_dicts( galaxy_tool_shed_url,
+ required_repository_metadata_id )
+ if error_message:
+ log.debug( 'Cannot check install container for version %s of repository %s owned by %s ' % \
+ ( changeset_revision, name, owner ) )
+ log.debug( 'due to the following error getting tool_test_results:\n%s' % str( error_message ) )
+ else:
+ # Inspect the tool_test_results_dict for the last test run to see if it has not yet been populated
+ if len( tool_test_results_dicts ) == 0:
+ populated = False
+ tool_test_results_dict = initialize_tool_tests_results_dict( app, {} )
+ else:
+ tool_test_results_dict = tool_test_results_dicts[ 0 ]
+ if len( tool_test_results_dict ) <= 1:
+ populated = False
+ tool_test_results_dict = tool_test_results_dicts.pop( 0 )
+ elif len( tool_test_results_dict ) == 2 and \
+ 'test_environment' in tool_test_results_dict and \
+ 'missing_test_components' in tool_test_results_dict:
+ populated = False
+ tool_test_results_dict = tool_test_results_dicts.pop( 0 )
+ else:
+ populated = True
+ if not populated:
+ # Get the installed repository record from the Galaxy database.
+ required_repository = \
+ suc.get_tool_shed_repository_by_shed_name_owner_changeset_revision( app,
+ galaxy_tool_shed_url,
+ name,
+ owner,
+ changeset_revision )
+ repository_identifier_dict = dict( name=name, owner=owner, changeset_revision=changeset_revision )
+ if required_repository.is_installed:
+ # The required_repository was successfully installed, so populate the installation
+ # containers (success and error) for the repository's immediate dependencies.
+ params, install_and_test_statistics_dict, tool_test_results_dict = \
+ populate_dependency_install_containers( app,
+ required_repository,
+ repository_identifier_dict,
+ install_and_test_statistics_dict,
+ tool_test_results_dict )
+ response_dict = save_test_results_for_changeset_revision( galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dependencies_dict,
+ params,
+ can_update_tool_shed )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('\n=============================================================\n' )
+ else:
+ # The required repository's installation failed.
+ tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = str( required_repository.error_message )
+ params = dict( test_install_error=True,
+ do_not_test=False )
+ response_dict = save_test_results_for_changeset_revision( galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dependencies_dict,
+ params,
+ can_update_tool_shed )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('\n=============================================================\n' )
def run_tests( test_config ):
loader = nose.loader.TestLoader( config=test_config )
@@ -853,3 +926,34 @@
test_runner = plug_runner
result = test_runner.run( tests )
return result, test_config.plugins._plugins
+
+def save_test_results_for_changeset_revision( url, tool_test_results_dicts, tool_test_results_dict, repository_dict,
+ params, can_update_tool_shed ):
+ """
+ Update the repository metadata tool_test_results and appropriate flags using the Tool Shed API. This method
+ updates tool_test_results with the received tool_test_results_dict, sets the do_not_test and tools_functionally
+ correct flags to the appropriate values and updates the time_last_tested field.
+ """
+ if can_update_tool_shed:
+ metadata_revision_id = repository_dict.get( 'id', None )
+ if metadata_revision_id is not None:
+ name = str( repository_dict[ 'name' ] )
+ owner = str( repository_dict[ 'owner' ] )
+ changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ log.debug('\n=============================================================\n' )
+ log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
+ log.debug( 'Updating tool_test_results for repository_metadata id %s.' % str( metadata_revision_id ) )
+ tool_test_results_dicts.insert( 0, tool_test_results_dict )
+ params[ 'tool_test_results' ] = tool_test_results_dicts
+ # Set the time_last_tested entry so that the repository_metadata.time_last_tested will be set in the tool shed.
+ params[ 'time_last_tested' ] = 'This entry will result in this value being set via the Tool Shed API.'
+ url = '%s' % ( suc.url_join( galaxy_tool_shed_url,'api', 'repository_revisions', str( metadata_revision_id ) ) )
+ try:
+ return update( tool_shed_api_key, url, params, return_formatted=False )
+ except Exception, e:
+ log.exception( 'Error updating tool_test_results for repository_metadata id %s:\n%s' % \
+ ( str( metadata_revision_id ), str( e ) ) )
+ return {}
+ else:
+ return {}
diff -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 -r 9df289e311209975ab34c288d8229096683af0e0 test/install_and_test_tool_shed_repositories/functional/test_install_repositories.py
--- a/test/install_and_test_tool_shed_repositories/functional/test_install_repositories.py
+++ b/test/install_and_test_tool_shed_repositories/functional/test_install_repositories.py
@@ -7,7 +7,7 @@
class InstallTestRepositories( InstallTestRepository ):
- """Abstract test case that installs and uninstalls a predefined list of repositories."""
+ """Abstract test case that installs a predefined list of repositories."""
def do_install( self, repository_dict ):
self.logout()
@@ -21,27 +21,34 @@
def generate_install_method( repository_dict=None ):
"""Generate abstract test cases for the defined list of repositories."""
+
+ def make_install_method( repository_dict ):
+ def test_install_repository( self ):
+ self.do_install( repository_dict )
+ return test_install_repository
+
if repository_dict is None:
return
# Push all the toolbox tests to module level
G = globals()
# Eliminate all previous tests from G.
for key, val in G.items():
- if key.startswith( 'TestInstallRepository_' ) or key.startswith( 'TestUninstallRepository_' ) or key.startswith( 'TestForTool_' ):
+ if key.startswith( 'TestInstallRepository_' ) or key.startswith( 'TestForTool_' ):
del G[ key ]
- # Create a new subclass with a method named install_repository_XXX that installs the repository specified by the provided dict.
- name = "TestInstallRepository_" + repository_dict[ 'name' ]
+ tool_shed = str( repository_dict[ 'tool_shed_url' ] )
+ repository_name = str( repository_dict[ 'name' ] )
+ repository_owner = str( repository_dict[ 'owner' ] )
+ changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ # Create a new subclass with a method named install_repository_XXX that installs the repository defined
+ # by the received repository_dict along with all of its dependency hierarchy.
+ test_name = "TestInstallRepository_" + repository_name
baseclasses = ( InstallTestRepositories, )
namespace = dict()
- def make_install_method( repository_dict ):
- def test_install_repository( self ):
- self.do_install( repository_dict )
- return test_install_repository
test_method = make_install_method( repository_dict )
- test_method.__doc__ = "Install the repository %s from %s." % \
- ( str( repository_dict[ 'name' ] ), str( repository_dict[ 'tool_shed_url' ] ) )
- namespace[ 'install_repository_%s' % str( repository_dict[ 'name' ] ) ] = test_method
+ test_method.__doc__ = "Installing revision %s of repository %s owned by %s from tool shed %s." % \
+ ( changeset_revision, repository_name, repository_owner, tool_shed )
+ namespace[ 'install_repository_%s' % repository_name ] = test_method
# The new.classobj function returns a new class object with name name derived
# from baseclasses (which should be a tuple of classes) and with namespace dict.
- new_class_obj = new.classobj( str( name ), baseclasses, namespace )
- G[ name ] = new_class_obj
+ new_class_obj = new.classobj( str( test_name ), baseclasses, namespace )
+ G[ test_name ] = new_class_obj
diff -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 -r 9df289e311209975ab34c288d8229096683af0e0 test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -180,7 +180,8 @@
install_and_test_base_util.get_tool_test_results_dicts( install_and_test_base_util.galaxy_tool_shed_url,
encoded_repository_metadata_id )
if error_message:
- log.debug( error_message )
+ log.debug( 'Cannot install version %s of repository %s owned by %s due to the following error getting tool_test_results:\n%s' % \
+ ( changeset_revision, name, owner, str( error_message ) ) )
else:
tool_test_results_dict = install_and_test_base_util.get_tool_test_results_dict( tool_test_results_dicts )
is_excluded, reason = install_and_test_base_util.is_excluded( exclude_list_dicts,
@@ -194,18 +195,16 @@
# If this repository is being skipped, register the reason.
tool_test_results_dict[ 'not_tested' ] = dict( reason=reason )
params = dict( do_not_test=False )
- log.debug('=============================================================' )
- log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
- response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params,
- can_update_tool_shed )
+ response_dict = \
+ install_and_test_base_util.save_test_results_for_changeset_revision( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
( changeset_revision, name, owner, str( response_dict ) ) )
- log.debug('=============================================================' )
+ log.debug('\n=============================================================\n' )
else:
# See if the repository was installed in a previous test.
repository = install_and_test_base_util.get_repository( name, owner, changeset_revision )
@@ -219,47 +218,54 @@
log.debug( 'Installation failed for revision %s of repository %s owned by %s.' % ( changeset_revision, name, owner ) )
install_and_test_statistics_dict[ 'repositories_with_installation_error' ].append( repository_identifier_dict )
tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = error_message
- params = dict( test_install_error=True )
- log.debug('=============================================================' )
- log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
- response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params,
- can_update_tool_shed )
+ params = dict( test_install_error=True,
+ do_not_test=False )
+ response_dict = \
+ install_and_test_base_util.save_test_results_for_changeset_revision( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
( changeset_revision, name, owner, str( response_dict ) ) )
- log.debug('=============================================================' )
+ log.debug('\n=============================================================\n' )
else:
# The repository was successfully installed.
log.debug( 'Installation succeeded for revision %s of repository %s owned by %s.' % \
( changeset_revision, name, owner ) )
- params, install_and_test_statistics_dict, tool_test_results_dict = \
- install_and_test_base_util.register_installed_and_missing_dependencies( app,
- repository,
- repository_identifier_dict,
- install_and_test_statistics_dict,
- tool_test_results_dict )
# Add an empty 'missing_test_results' entry if it is missing from the tool_test_results_dict. The
# ~/tool_shed/scripts/check_repositories_for_functional_tests.py will have entered information in the
# 'missing_test_components' entry of the tool_test_results_dict dictionary for repositories that are
# missing test components.
if 'missing_test_components' not in tool_test_results_dict:
tool_test_results_dict[ 'missing_test_components' ] = []
+ # Populate the installation containers (success and error) for the repository's immediate dependencies
+ # (the entire dependency tree is not handled here).
+ params, install_and_test_statistics_dict, tool_test_results_dict = \
+ install_and_test_base_util.populate_dependency_install_containers( app,
+ repository,
+ repository_identifier_dict,
+ install_and_test_statistics_dict,
+ tool_test_results_dict )
+ # Populate the installation containers (success or error) for the repository's immediate repository
+ # dependencies whose containers are not yet populated.
+ install_and_test_base_util.populate_install_containers_for_repository_dependencies( app,
+ repository,
+ encoded_repository_metadata_id,
+ install_and_test_statistics_dict,
+ can_update_tool_shed )
+ # Execute the contained tool's functional tests only if the repository's entire dependency
+ # tree is successfully installed.
+ missing_repository_dependencies = install_and_test_base_util.get_missing_repository_dependencies( repository )
missing_tool_dependencies = install_and_test_base_util.get_missing_tool_dependencies( repository )
- if missing_tool_dependencies or repository.missing_repository_dependencies:
- install_and_test_base_util.handle_missing_dependencies( app=app,
- repository=repository,
- missing_tool_dependencies=missing_tool_dependencies,
- repository_dict=repository_dict,
- tool_test_results_dicts=tool_test_results_dicts,
- tool_test_results_dict=tool_test_results_dict,
- params=params,
- can_update_tool_shed=can_update_tool_shed )
+ if missing_repository_dependencies or missing_tool_dependencies:
+ log.debug( 'Cannot execute tests for tools in revision %s of repository %s owned by %s ' % \
+ ( changeset_revision, name, owner ) )
+ log.debug( 'because one or more dependencies has installation errors.' )
else:
- log.debug( 'Installation of %s succeeded, running all defined functional tests.' % str( repository.name ) )
+ log.debug( 'Revision %s of repository %s owned by %s installed successfully, so running tool tests.' % \
+ ( changeset_revision, name, owner ) )
# Generate the shed_tools_dict that specifies the location of test data contained within this repository.
# and configure and run functional tests for this repository. This is equivalent to
# sh run_functional_tests.sh -installed
@@ -287,19 +293,16 @@
install_and_test_statistics_dict[ 'at_least_one_test_failed' ].append( repository_identifier_dict )
# Record the status of this repository in the tool shed.
params[ 'tools_functionally_correct' ] = False
- log.debug('=============================================================' )
- log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
response_dict = \
- install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params,
- can_update_tool_shed )
+ install_and_test_base_util.save_test_results_for_changeset_revision( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
( changeset_revision, name, owner, str( response_dict ) ) )
- log.debug('=============================================================' )
+ log.debug('\n=============================================================\n' )
else:
log.debug( 'Skipped attempt to install revision %s of repository %s owned by %s because ' % \
( changeset_revision, name, owner ) )
@@ -661,21 +664,19 @@
params = dict( tools_functionally_correct=True,
do_not_test=False,
test_install_error=False )
- # Call the register_test_result() method to execute a PUT request to the repository_revisions API
- # controller with the status of the test. This also sets the do_not_test and tools_functionally
- # correct flags and updates the time_last_tested field to today's date.
- log.debug('=============================================================' )
- log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
- response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params,
- can_update_tool_shed )
+ # Call the save_test_results_for_changeset_revision() method to execute a PUT request to the
+ # repository_revisions API controller with the status of the test. This also sets the do_not_test
+ # and tools_functionally correct flags and updates the time_last_tested field to today's date.
+ response_dict = \
+ install_and_test_base_util.save_test_results_for_changeset_revision( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
( changeset_revision, name, owner, str( response_dict ) ) )
- log.debug('=============================================================' )
+ log.debug('\n=============================================================\n' )
else:
# The get_failed_test_dicts() method returns a list.
log.debug( 'Revision %s of repository %s owned by %s installed successfully but did not pass functional tests.' % \
@@ -690,18 +691,16 @@
params = dict( tools_functionally_correct=False,
test_install_error=False,
do_not_test=str( set_do_not_test ) )
- log.debug('=============================================================' )
- log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
- response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params,
- can_update_tool_shed )
+ response_dict = \
+ install_and_test_base_util.save_test_results_for_changeset_revision( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
( changeset_revision, name, owner, str( response_dict ) ) )
- log.debug('=============================================================' )
+ log.debug('\n=============================================================\n' )
# Remove the just-executed tests so twill will not find and re-test them along with the tools
# contained in the next repository.
remove_tests( app )
diff -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 -r 9df289e311209975ab34c288d8229096683af0e0 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -114,7 +114,8 @@
install_and_test_base_util.get_tool_test_results_dicts( install_and_test_base_util.galaxy_tool_shed_url,
encoded_repository_metadata_id )
if error_message:
- log.debug( error_message )
+ log.debug( 'Cannot install version %s of repository %s owned by %s due to the following error getting tool_test_results:\n%s' % \
+ ( changeset_revision, name, owner, str( error_message ) ) )
else:
tool_test_results_dict = install_and_test_base_util.get_tool_test_results_dict( tool_test_results_dicts )
is_excluded, reason = install_and_test_base_util.is_excluded( exclude_list_dicts,
@@ -128,18 +129,16 @@
( changeset_revision, name, owner ) )
tool_test_results_dict[ 'not_tested' ] = dict( reason=reason )
params = dict( do_not_test=False )
- log.debug('=============================================================' )
- log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
- response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params,
- can_update_tool_shed )
+ response_dict = \
+ install_and_test_base_util.save_test_results_for_changeset_revision( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
( changeset_revision, name, owner, str( response_dict ) ) )
- log.debug('=============================================================' )
+ log.debug('\n=============================================================\n' )
else:
# See if the repository was installed in a previous test.
repository = install_and_test_base_util.get_repository( name, owner, changeset_revision )
@@ -153,41 +152,47 @@
log.debug( 'Installation failed for revision %s of repository %s owned by %s.' % ( changeset_revision, name, owner ) )
install_and_test_statistics_dict[ 'repositories_with_installation_error' ].append( repository_identifier_dict )
tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = error_message
- params = dict( test_install_error=True )
- log.debug('=============================================================' )
- log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
- response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params,
- can_update_tool_shed )
+ params = dict( test_install_error=True,
+ do_not_test=False )
+ response_dict = \
+ install_and_test_base_util.save_test_results_for_changeset_revision( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
( changeset_revision, name, owner, str( response_dict ) ) )
- log.debug('=============================================================' )
+ log.debug('\n=============================================================\n' )
else:
# The repository was successfully installed.
log.debug( 'Installation succeeded for revision %s of repository %s owned by %s.' % \
( changeset_revision, name, owner ) )
+ # Populate the installation containers (success and error) for the repository's immediate dependencies
+ # (the entire dependency tree is not handled here).
params, install_and_test_statistics_dict, tool_test_results_dict = \
- install_and_test_base_util.register_installed_and_missing_dependencies( app,
- repository,
- repository_identifier_dict,
- install_and_test_statistics_dict,
- tool_test_results_dict )
- log.debug('=============================================================' )
- log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
- ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
- response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params,
- can_update_tool_shed )
+ install_and_test_base_util.populate_dependency_install_containers( app,
+ repository,
+ repository_identifier_dict,
+ install_and_test_statistics_dict,
+ tool_test_results_dict )
+ response_dict = \
+ install_and_test_base_util.save_test_results_for_changeset_revision( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
( changeset_revision, name, owner, str( response_dict ) ) )
- log.debug('=============================================================' )
+ log.debug('\n=============================================================\n' )
+ # Populate the installation containers (success or error) for the repository's immediate repository
+ # dependencies whose containers are not yet populated.
+ install_and_test_base_util.populate_install_containers_for_repository_dependencies( app,
+ repository,
+ encoded_repository_metadata_id,
+ install_and_test_statistics_dict,
+ can_update_tool_shed )
else:
log.debug( 'Skipped attempt to install revision %s of repository %s owned by %s because ' % \
( changeset_revision, name, owner ) )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/49394b5fc1e9/
Changeset: 49394b5fc1e9
User: carlfeberhard
Date: 2014-01-06 19:49:20
Summary: History view: replace mako gen. w/ js panel, allow importing to include deleted datasets (by using import when deleted datasets are visible); The original view page is still (temporarily) accessible by altering the url to history/original_view
Affected #: 16 files
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -7,6 +7,7 @@
from galaxy.datatypes.data import nice_size
from galaxy.model.item_attrs import UsesAnnotations, UsesItemRatings
from galaxy.model.orm import and_, eagerload_all, func
+from galaxy import util
from galaxy.util import Params
from galaxy.util.odict import odict
from galaxy.util.sanitize_html import sanitize_html
@@ -86,7 +87,7 @@
)
operations = [
grids.GridOperation( "Switch", allow_multiple=False, condition=( lambda item: not item.deleted ), async_compatible=True ),
- grids.GridOperation( "View", allow_multiple=False, inbound=True ),
+ grids.GridOperation( "View", allow_multiple=False ),
grids.GridOperation( "Share or Publish", allow_multiple=False, condition=( lambda item: not item.deleted ), async_compatible=False ),
grids.GridOperation( "Rename", condition=( lambda item: not item.deleted ), async_compatible=False, inbound=True ),
grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ), async_compatible=True ),
@@ -809,6 +810,9 @@
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
+ # include activatable/deleted datasets when copying?
+ include_deleted = util.string_as_bool( kwd.get( 'include_deleted', False ) )
+
# Do import.
if not id:
return trans.show_error_message( "You must specify a history you want to import.<br>You can %s." % referer_message, use_panels=True )
@@ -822,7 +826,7 @@
#dan: I can import my own history.
#if import_history.user_id == user.id:
# return trans.show_error_message( "You cannot import your own history.<br>You can %s." % referer_message, use_panels=True )
- new_history = import_history.copy( target_user=user )
+ new_history = import_history.copy( target_user=user, activatable=include_deleted )
new_history.name = "imported: " + new_history.name
new_history.user_id = user.id
galaxy_session = trans.get_galaxy_session()
@@ -840,7 +844,10 @@
return trans.show_ok_message(
message="""History "%s" has been imported. <br>You can <a href="%s" onclick="parent.window.location='%s';">start using this history</a> or %s."""
% ( new_history.name, web.url_for( '/' ), web.url_for( '/' ), referer_message ), use_panels=True )
+
elif not user_history or not user_history.datasets or confirm:
+ #TODO:?? should anon-users be allowed to include deleted datasets when importing?
+ #new_history = import_history.copy( activatable=include_deleted )
new_history = import_history.copy()
new_history.name = "imported: " + new_history.name
new_history.user_id = None
@@ -863,8 +870,9 @@
history. <br>You can <a href="%s">continue and import this history</a> or %s.
""" % ( web.url_for(controller='history', action='imp', id=id, confirm=True, referer=trans.request.referer ), referer_message ), use_panels=True )
+ # Replaced with view (below) but kept (available via manual URL editing) for now
@web.expose
- def view( self, trans, id=None, show_deleted=False, use_panels=True ):
+ def original_view( self, trans, id=None, show_deleted=False, use_panels=True ):
"""View a history. If a history is importable, then it is viewable by any user."""
# Get history to view.
if not id:
@@ -885,13 +893,67 @@
use_panels = galaxy.util.string_as_bool( use_panels )
except:
pass # already a bool
- return trans.stream_template_mako( "history/view.mako",
+ return trans.stream_template_mako( "history/original_view.mako",
history = history_to_view,
datasets = datasets,
show_deleted = show_deleted,
use_panels = use_panels )
@web.expose
+ def view( self, trans, id=None, show_deleted=False, show_hidden=False, use_panels=True ):
+ """
+ View a history. If a history is importable, then it is viewable by any user.
+ """
+ # Get history to view.
+ if not id:
+ return trans.show_error_message( "You must specify a history you want to view." )
+
+ show_deleted = galaxy.util.string_as_bool( show_deleted )
+ show_hidden = galaxy.util.string_as_bool( show_hidden )
+ use_panels = galaxy.util.string_as_bool( use_panels )
+
+ history_dictionary = {}
+ hda_dictionaries = []
+ try:
+ history_to_view = self.get_history( trans, id, False )
+ if not history_to_view:
+ return trans.show_error_message( "The specified history does not exist." )
+
+ # Admin users can view any history
+ if( ( history_to_view.user != trans.user )
+ and ( not trans.user_is_admin() )
+ and ( not history_to_view.importable ) ):
+ #TODO: no check for shared with
+ return trans.show_error_message( "Either you are not allowed to view this history"
+ + " or the owner of this history has not made it accessible." )
+
+ hdas = self.get_history_datasets( trans, history_to_view, show_deleted=True, show_hidden=True )
+ for hda in hdas:
+ hda_dict = {}
+ try:
+ hda_dict = self.get_hda_dict( trans, hda )
+
+ except Exception, exc:
+ # don't fail entire list if hda err's, record and move on
+ log.error( 'Error bootstrapping hda %d: %s', hda.id, str( exc ), exc_info=True )
+ hda_dict = self.get_hda_dict_with_error( trans, hda, str( exc ) )
+
+ hda_dictionaries.append( hda_dict )
+
+ # re-use the hdas above to get the history data...
+ history_dictionary = self.get_history_dict( trans, history_to_view, hda_dictionaries=hda_dictionaries )
+
+ except Exception, exc:
+ user_id = str( trans.user.id ) if trans.user else '(anonymous)'
+ log.exception( 'Error bootstrapping history for user %s: %s', user_id, str( exc ) )
+ history_dictionary[ 'error' ] = ( 'An error occurred getting the history data from the server. '
+ + 'Please contact a Galaxy administrator if the problem persists.' )
+
+ return trans.fill_template_mako( "history/view.mako",
+ history=history_dictionary, hdas=hda_dictionaries,
+ show_deleted=show_deleted, show_hidden=show_hidden, use_panels=use_panels )
+
+ @web.expose
def display_by_username_and_slug( self, trans, username, slug ):
""" Display history based on a username and slug. """
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d static/scripts/mvc/dataset/hda-base.js
--- a/static/scripts/mvc/dataset/hda-base.js
+++ b/static/scripts/mvc/dataset/hda-base.js
@@ -93,13 +93,7 @@
// re-get web controller urls for functions relating to this hda. (new model data may have changed this)
this.urls = this.model.urls();
-
- // create a new render using a skeleton template, render title buttons, render body, and set up events, etc.
- var $newRender = $( HDABaseView.templates.skeleton( this.model.toJSON() ) );
- $newRender.find( '.dataset-primary-actions' ).append( this._render_titleButtons() );
- $newRender.children( '.dataset-body' ).replaceWith( this._render_body() );
- this._setUpBehaviors( $newRender );
- //this._renderSelectable( $newRender );
+ var $newRender = this._buildNewRender();
// fade the old render out (if desired)
if( fade ){
@@ -128,6 +122,16 @@
});
return this;
},
+
+ _buildNewRender : function(){
+ // create a new render using a skeleton template, render title buttons, render body, and set up events, etc.
+ var $newRender = $( HDABaseView.templates.skeleton( this.model.toJSON() ) );
+ $newRender.find( '.dataset-primary-actions' ).append( this._render_titleButtons() );
+ $newRender.children( '.dataset-body' ).replaceWith( this._render_body() );
+ this._setUpBehaviors( $newRender );
+ //this._renderSelectable( $newRender );
+ return $newRender;
+ },
/** set up js behaviors, event handlers for elements within the given container
* @param {jQuery} $container jq object that contains the elements to process (defaults to this.$el)
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d static/scripts/mvc/dataset/hda-edit.js
--- a/static/scripts/mvc/dataset/hda-edit.js
+++ b/static/scripts/mvc/dataset/hda-edit.js
@@ -261,6 +261,21 @@
}
return $icon;
},
+
+ // ......................................................................... render main
+ _buildNewRender : function(){
+ var $newRender = hdaBase.HDABaseView.prototype._buildNewRender.call( this );
+
+ //TODO: this won't localize easily
+ $newRender.find( '.dataset-deleted-msg' ).append(
+ _l( 'Click <a href="javascript:void(0);" class="dataset-undelete">here</a> to undelete it' +
+ ' or <a href="javascript:void(0);" class="dataset-purge">here</a> to immediately remove it from disk' ));
+
+ $newRender.find( '.dataset-hidden-msg' ).append(
+ _l( 'Click <a href="javascript:void(0);" class="dataset-unhide">here</a> to unhide it' ));
+
+ return $newRender;
+ },
// ......................................................................... state body renderers
/** Render an HDA where the metadata wasn't produced correctly.
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d static/scripts/mvc/ui.js
--- a/static/scripts/mvc/ui.js
+++ b/static/scripts/mvc/ui.js
@@ -710,7 +710,7 @@
ModeButton.prototype.init = function _init( element, options ){
options = options || {};
this.$element = $( element );
- this.options = jQuery.extend( true, this.defaults, options );
+ this.options = jQuery.extend( true, {}, this.defaults, options );
var modeButton = this;
this.$element.click( function _ModeButtonClick( event ){
@@ -774,20 +774,23 @@
jQuery.fn.extend({
modeButton : function $modeButton( options ){
var nonOptionsArgs = jQuery.makeArray( arguments ).slice( 1 );
- return this.each( function(){
+ //TODO: does map still work with jq multi selection (i.e. $( '.class-for-many-btns' ).modeButton)?
+ return this.map( function(){
var $this = $( this ),
data = $this.data( 'mode-button' );
if( jQuery.type( options ) === 'object' ){
data = new ModeButton( $this, options );
$this.data( 'mode-button', data );
- }
- if( data && jQuery.type( options ) === 'string' ){
+
+ } else if( data && jQuery.type( options ) === 'string' ){
var fn = data[ options ];
if( jQuery.type( fn ) === 'function' ){
return fn.apply( data, nonOptionsArgs );
}
- }
+ } else if ( data ){
+ return data;
+ }
return this;
});
}
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d static/scripts/packed/mvc/dataset/hda-base.js
--- a/static/scripts/packed/mvc/dataset/hda-base.js
+++ b/static/scripts/packed/mvc/dataset/hda-base.js
@@ -1,1 +1,1 @@
-define(["mvc/dataset/hda-model"],function(b){var a=Backbone.View.extend(LoggableMixin).extend({tagName:"div",className:"dataset hda history-panel-hda",id:function(){return"hda-"+this.model.get("id")},fxSpeed:"fast",initialize:function(c){if(c.logger){this.logger=this.model.logger=c.logger}this.log(this+".initialize:",c);this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton];this.linkTarget=c.linkTarget||"_blank";this.selectable=c.selectable||false;this.selected=c.selected||false;this.expanded=c.expanded||false;this.draggable=c.draggable||false;this._setUpListeners()},_setUpListeners:function(){this.model.on("change",function(d,c){if(this.model.changedAttributes().state&&this.model.inReadyState()&&this.expanded&&!this.model.hasDetails()){this.model.fetch()}else{this.render()}},this)},render:function(e){e=(e===undefined)?(true):(e);var c=this;this.$el.find("[title]").tooltip("destroy");this.urls=this.model.urls();var d=$(a.templates.skeleton(this.model.toJSON()));d.find(".dataset-primary-actions").append(this._render_titleButtons());d.children(".dataset-body").replaceWith(this._render_body());this._setUpBehaviors(d);if(e){$(c).queue(function(f){this.$el.fadeOut(c.fxSpeed,f)})}$(c).queue(function(f){this.$el.empty().attr("class",c.className).addClass("state-"+c.model.get("state")).append(d.children());if(this.selectable){this.showSelector(0)}f()});if(e){$(c).queue(function(f){this.$el.fadeIn(c.fxSpeed,f)})}$(c).queue(function(f){this.trigger("rendered",c);if(this.model.inReadyState()){this.trigger("rendered:ready",c)}if(this.draggable){this.draggableOn()}f()});return this},_setUpBehaviors:function(c){c=c||this.$el;make_popup_menus(c);c.find("[title]").tooltip({placement:"bottom"})},_render_titleButtons:function(){return[this._render_displayButton()]},_render_displayButton:function(){if((this.model.get("state")===b.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.NEW)||(!this.model.get("accessible"))){return null}var d={target:this.linkTarget,classes:"dataset-display"};if(this.model.get("purged")){d.disabled=true;d.title=_l("Cannot display datasets removed from disk")}else{if(this.model.get("state")===b.HistoryDatasetAssociation.STATES.UPLOAD){d.disabled=true;d.title=_l("This dataset must finish uploading before it can be viewed")}else{d.title=_l("View data");d.href=this.urls.display;var c=this;d.onclick=function(){if(Galaxy.frame&&Galaxy.frame.active){Galaxy.frame.add({title:"Data Viewer: "+c.model.get("name"),type:"url",content:c.urls.display})}}}}d.faIcon="fa-eye";return faIconButton(d)},_render_downloadButton:function(){if(this.model.get("purged")||!this.model.hasData()){return null}var d=this.urls,e=this.model.get("meta_files");if(_.isEmpty(e)){return $(['<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>',"</a>"].join(""))}var f="dataset-"+this.model.get("id")+"-popup",c=['<div popupmenu="'+f+'">','<a href="'+d.download+'">',_l("Download Dataset"),"</a>","<a>"+_l("Additional Files")+"</a>",_.map(e,function(g){return['<a class="action-button" href="',d.meta_download+g.file_type,'">',_l("Download")," ",g.file_type,"</a>"].join("")}).join("\n"),"</div>",'<div class="icon-btn-group">','<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>','</a><a class="icon-btn popup" id="'+f+'">','<span class="fa fa-caret-down"></span>',"</a>","</div>"].join("\n");return $(c)},_render_showParamsButton:function(){return faIconButton({title:_l("View details"),href:this.urls.show_params,target:this.linkTarget,faIcon:"fa-info-circle"})},_render_body:function(){var d=$('<div>Error: unknown dataset state "'+this.model.get("state")+'".</div>'),c=this["_render_body_"+this.model.get("state")];if(_.isFunction(c)){d=c.call(this)}this._setUpBehaviors(d);if(this.expanded){d.show()}return d},_render_stateBodyHelper:function(c,f){f=f||[];var d=this,e=$(a.templates.body(_.extend(this.model.toJSON(),{body:c})));e.find(".dataset-actions .left").append(_.map(f,function(g){return g.call(d)}));return e},_render_body_new:function(){return this._render_stateBodyHelper("<div>"+_l("This is a new dataset and not all of its data are available yet")+"</div>")},_render_body_noPermission:function(){return this._render_stateBodyHelper("<div>"+_l("You do not have permission to view this dataset")+"</div>")},_render_body_discarded:function(){return this._render_stateBodyHelper("<div>"+_l("The job creating this dataset was cancelled before completion")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_queued:function(){return this._render_stateBodyHelper("<div>"+_l("This job is waiting to run")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_upload:function(){return this._render_stateBodyHelper("<div>"+_l("This dataset is currently uploading")+"</div>")},_render_body_setting_metadata:function(){return this._render_stateBodyHelper("<div>"+_l("Metadata is being auto-detected")+"</div>")},_render_body_running:function(){return this._render_stateBodyHelper("<div>"+_l("This job is currently running")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_paused:function(){return this._render_stateBodyHelper("<div>"+_l('This job is paused. Use the "Resume Paused Jobs" in the history menu to resume')+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_error:function(){var c=['<span class="help-text">',_l("An error occurred with this dataset"),":</span>",'<div class="job-error-text">',$.trim(this.model.get("misc_info")),"</div>"].join("");if(!this.model.get("purged")){c="<div>"+this.model.get("misc_blurb")+"</div>"+c}return this._render_stateBodyHelper(c,[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers))},_render_body_empty:function(){return this._render_stateBodyHelper("<div>"+_l("No data")+": <i>"+this.model.get("misc_blurb")+"</i></div>",this.defaultPrimaryActionButtonRenderers)},_render_body_failed_metadata:function(){var c=$('<div class="warningmessagesmall"></div>').append($("<strong/>").text(_l("An error occurred setting the metadata for this dataset"))),d=this._render_body_ok();d.prepend(c);return d},_render_body_ok:function(){var c=this,e=$(a.templates.body(this.model.toJSON())),d=[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers);e.find(".dataset-actions .left").append(_.map(d,function(f){return f.call(c)}));if(this.model.isDeletedOrPurged()){return e}return e},events:{"click .dataset-title-bar":"toggleBodyVisibility","keydown .dataset-title-bar":"toggleBodyVisibility","click .dataset-selector":"toggleSelect",},toggleBodyVisibility:function(f,d){var c=32,e=13;if(f&&(f.type==="keydown")&&!(f.keyCode===c||f.keyCode===e)){return true}var g=this.$el.find(".dataset-body");d=(d===undefined)?(!g.is(":visible")):(d);if(d){this.expandBody()}else{this.collapseBody()}return false},expandBody:function(){var c=this;function d(){c.$el.children(".dataset-body").replaceWith(c._render_body());c.$el.children(".dataset-body").slideDown(c.fxSpeed,function(){c.expanded=true;c.trigger("body-expanded",c.model.get("id"))})}if(this.model.inReadyState()&&!this.model.hasDetails()){this.model.fetch({silent:true}).always(function(e){d()})}else{d()}},collapseBody:function(){var c=this;this.$el.children(".dataset-body").slideUp(c.fxSpeed,function(){c.expanded=false;c.trigger("body-collapsed",c.model.get("id"))})},showSelector:function(e){if(this.$el.find(".dataset-selector").css("display")!=="none"){return}e=(e!==undefined)?(e):(this.fxSpeed);if(this.selected){this.select(null,true)}var d=this,c=32;if(e){this.$el.queue("fx",function(f){$(this).find(".dataset-primary-actions").fadeOut(e,f)});this.$el.queue("fx",function(f){$(this).find(".dataset-selector").show().animate({width:c},e,f);$(this).find(".dataset-title-bar").animate({"margin-left":c},e,f);d.selectable=true;d.trigger("selectable",true,d)})}else{this.$el.find(".dataset-primary-actions").hide();this.$el.find(".dataset-selector").show().css({width:c});this.$el.find(".dataset-title-bar").show().css({"margin-left":c});d.selectable=true;d.trigger("selectable",true,d)}},hideSelector:function(c){c=(c!==undefined)?(c):(this.fxSpeed);this.selectable=false;this.trigger("selectable",false,this);if(c){this.$el.queue("fx",function(d){$(this).find(".dataset-title-bar").show().css({"margin-left":"0"});$(this).find(".dataset-selector").animate({width:"0px"},c,function(){$(this).hide();d()})});this.$el.queue("fx",function(d){$(this).find(".dataset-primary-actions").fadeIn(c,d)})}else{$(this).find(".dataset-selector").css({width:"0px"}).hide();$(this).find(".dataset-primary-actions").show()}},toggleSelector:function(c){if(!this.$el.find(".dataset-selector").is(":visible")){this.showSelector(c)}else{this.hideSelector(c)}},select:function(c){this.$el.find(".dataset-selector span").removeClass("fa-square-o").addClass("fa-check-square-o");if(!this.selected){this.trigger("selected",this);this.selected=true}return false},deselect:function(c){this.$el.find(".dataset-selector span").removeClass("fa-check-square-o").addClass("fa-square-o");if(this.selected){this.trigger("de-selected",this);this.selected=false}return false},toggleSelect:function(c){if(this.selected){this.deselect(c)}else{this.select(c)}},draggableOn:function(){this.draggable=true;this.dragStartHandler=_.bind(this._dragStartHandler,this);this.dragEndHandler=_.bind(this._dragEndHandler,this);var c=this.$el.find(".dataset-title-bar").attr("draggable",true).get(0);c.addEventListener("dragstart",this.dragStartHandler,false);c.addEventListener("dragend",this.dragEndHandler,false)},draggableOff:function(){this.draggable=false;var c=this.$el.find(".dataset-title-bar").attr("draggable",false).get(0);c.removeEventListener("dragstart",this.dragStartHandler,false);c.removeEventListener("dragend",this.dragEndHandler,false)},toggleDraggable:function(){if(this.draggable){this.draggableOff()}else{this.draggableOn()}},_dragStartHandler:function(c){this.trigger("dragstart",this);c.dataTransfer.effectAllowed="move";c.dataTransfer.setData("text",JSON.stringify(this.model.toJSON()));return false},_dragEndHandler:function(c){this.trigger("dragend",this);return false},remove:function(d){var c=this;this.$el.fadeOut(c.fxSpeed,function(){c.$el.remove();c.off();if(d){d()}})},toString:function(){var c=(this.model)?(this.model+""):("(no model)");return"HDABaseView("+c+")"}});a.templates={skeleton:Handlebars.templates["template-hda-skeleton"],body:Handlebars.templates["template-hda-body"]};return{HDABaseView:a}});
\ No newline at end of file
+define(["mvc/dataset/hda-model"],function(b){var a=Backbone.View.extend(LoggableMixin).extend({tagName:"div",className:"dataset hda history-panel-hda",id:function(){return"hda-"+this.model.get("id")},fxSpeed:"fast",initialize:function(c){if(c.logger){this.logger=this.model.logger=c.logger}this.log(this+".initialize:",c);this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton];this.linkTarget=c.linkTarget||"_blank";this.selectable=c.selectable||false;this.selected=c.selected||false;this.expanded=c.expanded||false;this.draggable=c.draggable||false;this._setUpListeners()},_setUpListeners:function(){this.model.on("change",function(d,c){if(this.model.changedAttributes().state&&this.model.inReadyState()&&this.expanded&&!this.model.hasDetails()){this.model.fetch()}else{this.render()}},this)},render:function(e){e=(e===undefined)?(true):(e);var c=this;this.$el.find("[title]").tooltip("destroy");this.urls=this.model.urls();var d=this._buildNewRender();if(e){$(c).queue(function(f){this.$el.fadeOut(c.fxSpeed,f)})}$(c).queue(function(f){this.$el.empty().attr("class",c.className).addClass("state-"+c.model.get("state")).append(d.children());if(this.selectable){this.showSelector(0)}f()});if(e){$(c).queue(function(f){this.$el.fadeIn(c.fxSpeed,f)})}$(c).queue(function(f){this.trigger("rendered",c);if(this.model.inReadyState()){this.trigger("rendered:ready",c)}if(this.draggable){this.draggableOn()}f()});return this},_buildNewRender:function(){var c=$(a.templates.skeleton(this.model.toJSON()));c.find(".dataset-primary-actions").append(this._render_titleButtons());c.children(".dataset-body").replaceWith(this._render_body());this._setUpBehaviors(c);return c},_setUpBehaviors:function(c){c=c||this.$el;make_popup_menus(c);c.find("[title]").tooltip({placement:"bottom"})},_render_titleButtons:function(){return[this._render_displayButton()]},_render_displayButton:function(){if((this.model.get("state")===b.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.NEW)||(!this.model.get("accessible"))){return null}var d={target:this.linkTarget,classes:"dataset-display"};if(this.model.get("purged")){d.disabled=true;d.title=_l("Cannot display datasets removed from disk")}else{if(this.model.get("state")===b.HistoryDatasetAssociation.STATES.UPLOAD){d.disabled=true;d.title=_l("This dataset must finish uploading before it can be viewed")}else{d.title=_l("View data");d.href=this.urls.display;var c=this;d.onclick=function(){if(Galaxy.frame&&Galaxy.frame.active){Galaxy.frame.add({title:"Data Viewer: "+c.model.get("name"),type:"url",content:c.urls.display})}}}}d.faIcon="fa-eye";return faIconButton(d)},_render_downloadButton:function(){if(this.model.get("purged")||!this.model.hasData()){return null}var d=this.urls,e=this.model.get("meta_files");if(_.isEmpty(e)){return $(['<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>',"</a>"].join(""))}var f="dataset-"+this.model.get("id")+"-popup",c=['<div popupmenu="'+f+'">','<a href="'+d.download+'">',_l("Download Dataset"),"</a>","<a>"+_l("Additional Files")+"</a>",_.map(e,function(g){return['<a class="action-button" href="',d.meta_download+g.file_type,'">',_l("Download")," ",g.file_type,"</a>"].join("")}).join("\n"),"</div>",'<div class="icon-btn-group">','<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>','</a><a class="icon-btn popup" id="'+f+'">','<span class="fa fa-caret-down"></span>',"</a>","</div>"].join("\n");return $(c)},_render_showParamsButton:function(){return faIconButton({title:_l("View details"),href:this.urls.show_params,target:this.linkTarget,faIcon:"fa-info-circle"})},_render_body:function(){var d=$('<div>Error: unknown dataset state "'+this.model.get("state")+'".</div>'),c=this["_render_body_"+this.model.get("state")];if(_.isFunction(c)){d=c.call(this)}this._setUpBehaviors(d);if(this.expanded){d.show()}return d},_render_stateBodyHelper:function(c,f){f=f||[];var d=this,e=$(a.templates.body(_.extend(this.model.toJSON(),{body:c})));e.find(".dataset-actions .left").append(_.map(f,function(g){return g.call(d)}));return e},_render_body_new:function(){return this._render_stateBodyHelper("<div>"+_l("This is a new dataset and not all of its data are available yet")+"</div>")},_render_body_noPermission:function(){return this._render_stateBodyHelper("<div>"+_l("You do not have permission to view this dataset")+"</div>")},_render_body_discarded:function(){return this._render_stateBodyHelper("<div>"+_l("The job creating this dataset was cancelled before completion")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_queued:function(){return this._render_stateBodyHelper("<div>"+_l("This job is waiting to run")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_upload:function(){return this._render_stateBodyHelper("<div>"+_l("This dataset is currently uploading")+"</div>")},_render_body_setting_metadata:function(){return this._render_stateBodyHelper("<div>"+_l("Metadata is being auto-detected")+"</div>")},_render_body_running:function(){return this._render_stateBodyHelper("<div>"+_l("This job is currently running")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_paused:function(){return this._render_stateBodyHelper("<div>"+_l('This job is paused. Use the "Resume Paused Jobs" in the history menu to resume')+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_error:function(){var c=['<span class="help-text">',_l("An error occurred with this dataset"),":</span>",'<div class="job-error-text">',$.trim(this.model.get("misc_info")),"</div>"].join("");if(!this.model.get("purged")){c="<div>"+this.model.get("misc_blurb")+"</div>"+c}return this._render_stateBodyHelper(c,[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers))},_render_body_empty:function(){return this._render_stateBodyHelper("<div>"+_l("No data")+": <i>"+this.model.get("misc_blurb")+"</i></div>",this.defaultPrimaryActionButtonRenderers)},_render_body_failed_metadata:function(){var c=$('<div class="warningmessagesmall"></div>').append($("<strong/>").text(_l("An error occurred setting the metadata for this dataset"))),d=this._render_body_ok();d.prepend(c);return d},_render_body_ok:function(){var c=this,e=$(a.templates.body(this.model.toJSON())),d=[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers);e.find(".dataset-actions .left").append(_.map(d,function(f){return f.call(c)}));if(this.model.isDeletedOrPurged()){return e}return e},events:{"click .dataset-title-bar":"toggleBodyVisibility","keydown .dataset-title-bar":"toggleBodyVisibility","click .dataset-selector":"toggleSelect",},toggleBodyVisibility:function(f,d){var c=32,e=13;if(f&&(f.type==="keydown")&&!(f.keyCode===c||f.keyCode===e)){return true}var g=this.$el.find(".dataset-body");d=(d===undefined)?(!g.is(":visible")):(d);if(d){this.expandBody()}else{this.collapseBody()}return false},expandBody:function(){var c=this;function d(){c.$el.children(".dataset-body").replaceWith(c._render_body());c.$el.children(".dataset-body").slideDown(c.fxSpeed,function(){c.expanded=true;c.trigger("body-expanded",c.model.get("id"))})}if(this.model.inReadyState()&&!this.model.hasDetails()){this.model.fetch({silent:true}).always(function(e){d()})}else{d()}},collapseBody:function(){var c=this;this.$el.children(".dataset-body").slideUp(c.fxSpeed,function(){c.expanded=false;c.trigger("body-collapsed",c.model.get("id"))})},showSelector:function(e){if(this.$el.find(".dataset-selector").css("display")!=="none"){return}e=(e!==undefined)?(e):(this.fxSpeed);if(this.selected){this.select(null,true)}var d=this,c=32;if(e){this.$el.queue("fx",function(f){$(this).find(".dataset-primary-actions").fadeOut(e,f)});this.$el.queue("fx",function(f){$(this).find(".dataset-selector").show().animate({width:c},e,f);$(this).find(".dataset-title-bar").animate({"margin-left":c},e,f);d.selectable=true;d.trigger("selectable",true,d)})}else{this.$el.find(".dataset-primary-actions").hide();this.$el.find(".dataset-selector").show().css({width:c});this.$el.find(".dataset-title-bar").show().css({"margin-left":c});d.selectable=true;d.trigger("selectable",true,d)}},hideSelector:function(c){c=(c!==undefined)?(c):(this.fxSpeed);this.selectable=false;this.trigger("selectable",false,this);if(c){this.$el.queue("fx",function(d){$(this).find(".dataset-title-bar").show().css({"margin-left":"0"});$(this).find(".dataset-selector").animate({width:"0px"},c,function(){$(this).hide();d()})});this.$el.queue("fx",function(d){$(this).find(".dataset-primary-actions").fadeIn(c,d)})}else{$(this).find(".dataset-selector").css({width:"0px"}).hide();$(this).find(".dataset-primary-actions").show()}},toggleSelector:function(c){if(!this.$el.find(".dataset-selector").is(":visible")){this.showSelector(c)}else{this.hideSelector(c)}},select:function(c){this.$el.find(".dataset-selector span").removeClass("fa-square-o").addClass("fa-check-square-o");if(!this.selected){this.trigger("selected",this);this.selected=true}return false},deselect:function(c){this.$el.find(".dataset-selector span").removeClass("fa-check-square-o").addClass("fa-square-o");if(this.selected){this.trigger("de-selected",this);this.selected=false}return false},toggleSelect:function(c){if(this.selected){this.deselect(c)}else{this.select(c)}},draggableOn:function(){this.draggable=true;this.dragStartHandler=_.bind(this._dragStartHandler,this);this.dragEndHandler=_.bind(this._dragEndHandler,this);var c=this.$el.find(".dataset-title-bar").attr("draggable",true).get(0);c.addEventListener("dragstart",this.dragStartHandler,false);c.addEventListener("dragend",this.dragEndHandler,false)},draggableOff:function(){this.draggable=false;var c=this.$el.find(".dataset-title-bar").attr("draggable",false).get(0);c.removeEventListener("dragstart",this.dragStartHandler,false);c.removeEventListener("dragend",this.dragEndHandler,false)},toggleDraggable:function(){if(this.draggable){this.draggableOff()}else{this.draggableOn()}},_dragStartHandler:function(c){this.trigger("dragstart",this);c.dataTransfer.effectAllowed="move";c.dataTransfer.setData("text",JSON.stringify(this.model.toJSON()));return false},_dragEndHandler:function(c){this.trigger("dragend",this);return false},remove:function(d){var c=this;this.$el.fadeOut(c.fxSpeed,function(){c.$el.remove();c.off();if(d){d()}})},toString:function(){var c=(this.model)?(this.model+""):("(no model)");return"HDABaseView("+c+")"}});a.templates={skeleton:Handlebars.templates["template-hda-skeleton"],body:Handlebars.templates["template-hda-body"]};return{HDABaseView:a}});
\ No newline at end of file
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d static/scripts/packed/mvc/dataset/hda-edit.js
--- a/static/scripts/packed/mvc/dataset/hda-edit.js
+++ b/static/scripts/packed/mvc/dataset/hda-edit.js
@@ -1,1 +1,1 @@
-define(["mvc/dataset/hda-model","mvc/dataset/hda-base"],function(d,a){var f=a.HDABaseView.extend(LoggableMixin).extend({initialize:function(g){a.HDABaseView.prototype.initialize.call(this,g);this.hasUser=g.hasUser;this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton,this._render_rerunButton];this.tagsEditorShown=g.tagsEditorShown||false;this.annotationEditorShown=g.annotationEditorShown||false},_render_titleButtons:function(){return a.HDABaseView.prototype._render_titleButtons.call(this).concat([this._render_editButton(),this._render_deleteButton()])},_render_editButton:function(){if((this.model.get("state")===d.HistoryDatasetAssociation.STATES.NEW)||(this.model.get("state")===d.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===d.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var i=this.model.get("purged"),g=this.model.get("deleted"),h={title:_l("Edit Attributes"),href:this.urls.edit,target:this.linkTarget,classes:"dataset-edit"};if(g||i){h.disabled=true;if(i){h.title=_l("Cannot edit attributes of datasets removed from disk")}else{if(g){h.title=_l("Undelete dataset to edit attributes")}}}else{if(this.model.get("state")===d.HistoryDatasetAssociation.STATES.UPLOAD){h.disabled=true;h.title=_l("This dataset must finish uploading before it can be edited")}}h.faIcon="fa-pencil";return faIconButton(h)},_render_deleteButton:function(){if((this.model.get("state")===d.HistoryDatasetAssociation.STATES.NEW)||(this.model.get("state")===d.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var g=this,h={title:_l("Delete"),classes:"dataset-delete",onclick:function(){g.$el.find(".icon-btn.dataset-delete").trigger("mouseout");g.model["delete"]()}};if(this.model.get("deleted")||this.model.get("purged")){h={title:_l("Dataset is already deleted"),disabled:true}}h.faIcon="fa-times";return faIconButton(h)},_render_errButton:function(){if(this.model.get("state")!==d.HistoryDatasetAssociation.STATES.ERROR){return null}return faIconButton({title:_l("View or report this error"),href:this.urls.report_error,target:this.linkTarget,faIcon:"fa-bug"})},_render_rerunButton:function(){return faIconButton({title:_l("Run this job again"),href:this.urls.rerun,target:this.linkTarget,faIcon:"fa-refresh"})},_render_visualizationsButton:function(){var n=this.model.get("visualizations");if((!this.hasUser)||(!this.model.hasData())||(_.isEmpty(n))){return null}if(_.isObject(n[0])){return this._render_visualizationsFrameworkButton(n)}if(!this.urls.visualization){return null}var k=this.model.get("dbkey"),g=this.urls.visualization,j={},h={dataset_id:this.model.get("id"),hda_ldda:"hda"};if(k){h.dbkey=k}var l=faIconButton({title:_l("Visualize"),faIcon:"fa-bar-chart-o"});var m=this;function i(p){switch(p){case"trackster":return b(g,h,k);case"scatterplot":return e(g,h,m.linkTarget);default:return function(){Galaxy.frame.add({title:"Visualization",type:"url",content:g+"/"+p+"?"+$.param(h)})}}}function o(p){return p.charAt(0).toUpperCase()+p.slice(1)}if(n.length===1){l.attr("data-original-title",_l("Visualize in ")+_l(o(n[0])));l.click(i(n[0]))}else{_.each(n,function(p){j[_l(o(p))]=i(p)});make_popupmenu(l,j)}return l},_render_visualizationsFrameworkButton:function(g){if(!(this.model.hasData())||!(g&&!_.isEmpty(g))){return null}var i=faIconButton({title:_l("Visualize"),faIcon:"fa-bar-chart-o"});i.addClass("visualize-icon");if(_.keys(g).length===1){i.attr("title",_.keys(g)[0]);i.attr("href",_.values(g)[0])}else{var j=[];_.each(g,function(k){j.push(k)});var h=new PopupMenu(i,j)}return i},_render_body_failed_metadata:function(){var h=$("<a/>").attr({href:this.urls.edit,target:this.linkTarget}).text(_l("set it manually or retry auto-detection")),g=$("<span/>").text(". "+_l("You may be able to")+" ").append(h),i=a.HDABaseView.prototype._render_body_failed_metadata.call(this);i.find(".warningmessagesmall strong").append(g);return i},_render_body_error:function(){var g=a.HDABaseView.prototype._render_body_error.call(this);g.find(".dataset-actions .left").prepend(this._render_errButton());return g},_render_body_ok:function(){var g=a.HDABaseView.prototype._render_body_ok.call(this);if(this.model.isDeletedOrPurged()){return g}this.makeDbkeyEditLink(g);if(this.hasUser){g.find(".dataset-actions .left").append(this._render_visualizationsButton());this._renderTags(g);this._renderAnnotation(g)}return g},_renderTags:function(g){var h=this;this.tagsEditor=new TagsEditor({model:this.model,el:g.find(".tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){h.tagsEditorShown=true},onhide:function(){h.tagsEditorShown=false},$activator:faIconButton({title:_l("Edit dataset tags"),classes:"dataset-tag-btn",faIcon:"fa-tags"}).appendTo(g.find(".dataset-actions .right"))});if(this.tagsEditorShown){this.tagsEditor.toggle(true)}},_renderAnnotation:function(g){var h=this;this.annotationEditor=new AnnotationEditor({model:this.model,el:g.find(".annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){h.annotationEditorShown=true},onhide:function(){h.annotationEditorShown=false},$activator:faIconButton({title:_l("Edit dataset annotation"),classes:"dataset-annotate-btn",faIcon:"fa-comment"}).appendTo(g.find(".dataset-actions .right"))});if(this.annotationEditorShown){this.annotationEditor.toggle(true)}},makeDbkeyEditLink:function(g){if(this.model.get("metadata_dbkey")==="?"&&!this.model.isDeletedOrPurged()){g.find(".dataset-dbkey .value").replaceWith($('<a target="'+this.linkTarget+'">?</a>').attr("href",this.urls.edit))}},events:_.extend(_.clone(a.HDABaseView.prototype.events),{"click .dataset-undelete":function(g){this.model.undelete();return false},"click .dataset-unhide":function(g){this.model.unhide();return false},"click .dataset-purge":"confirmPurge"}),confirmPurge:function c(g){this.model.purge();return false},toString:function(){var g=(this.model)?(this.model+""):("(no model)");return"HDAView("+g+")"}});function e(g,i,h){action=function(){Galaxy.frame.add({title:"Scatterplot",type:"url",content:g+"/scatterplot?"+$.param(i),target:h,scratchbook:true});$("div.popmenu-wrapper").remove();return false};return action}function b(g,i,h){return function(){var j={};if(h){j["f-dbkey"]=h}$.ajax({url:g+"/list_tracks?"+$.param(j),dataType:"html",error:function(){alert(("Could not add this dataset to browser")+".")},success:function(k){var l=window.parent;l.Galaxy.modal.show({title:"View Data in a New or Saved Visualization",buttons:{Cancel:function(){l.Galaxy.modal.hide()},"View in saved visualization":function(){l.Galaxy.modal.show({title:"Add Data to Saved Visualization",body:k,buttons:{Cancel:function(){l.Galaxy.modal.hide()},"Add to visualization":function(){$(l.document).find("input[name=id]:checked").each(function(){l.Galaxy.modal.hide();var m=$(this).val();i.id=m;l.Galaxy.frame.add({title:"Trackster",type:"url",content:g+"/trackster?"+$.param(i),scratchbook:true})})}}})},"View in new visualization":function(){l.Galaxy.modal.hide();var m=g+"/trackster?"+$.param(i);l.Galaxy.frame.add({title:"Trackster",type:"url",content:m,scratchbook:true})}}})}});return false}}return{HDAEditView:f}});
\ No newline at end of file
+define(["mvc/dataset/hda-model","mvc/dataset/hda-base"],function(d,a){var f=a.HDABaseView.extend(LoggableMixin).extend({initialize:function(g){a.HDABaseView.prototype.initialize.call(this,g);this.hasUser=g.hasUser;this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton,this._render_rerunButton];this.tagsEditorShown=g.tagsEditorShown||false;this.annotationEditorShown=g.annotationEditorShown||false},_render_titleButtons:function(){return a.HDABaseView.prototype._render_titleButtons.call(this).concat([this._render_editButton(),this._render_deleteButton()])},_render_editButton:function(){if((this.model.get("state")===d.HistoryDatasetAssociation.STATES.NEW)||(this.model.get("state")===d.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===d.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var i=this.model.get("purged"),g=this.model.get("deleted"),h={title:_l("Edit Attributes"),href:this.urls.edit,target:this.linkTarget,classes:"dataset-edit"};if(g||i){h.disabled=true;if(i){h.title=_l("Cannot edit attributes of datasets removed from disk")}else{if(g){h.title=_l("Undelete dataset to edit attributes")}}}else{if(this.model.get("state")===d.HistoryDatasetAssociation.STATES.UPLOAD){h.disabled=true;h.title=_l("This dataset must finish uploading before it can be edited")}}h.faIcon="fa-pencil";return faIconButton(h)},_render_deleteButton:function(){if((this.model.get("state")===d.HistoryDatasetAssociation.STATES.NEW)||(this.model.get("state")===d.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(!this.model.get("accessible"))){return null}var g=this,h={title:_l("Delete"),classes:"dataset-delete",onclick:function(){g.$el.find(".icon-btn.dataset-delete").trigger("mouseout");g.model["delete"]()}};if(this.model.get("deleted")||this.model.get("purged")){h={title:_l("Dataset is already deleted"),disabled:true}}h.faIcon="fa-times";return faIconButton(h)},_render_errButton:function(){if(this.model.get("state")!==d.HistoryDatasetAssociation.STATES.ERROR){return null}return faIconButton({title:_l("View or report this error"),href:this.urls.report_error,target:this.linkTarget,faIcon:"fa-bug"})},_render_rerunButton:function(){return faIconButton({title:_l("Run this job again"),href:this.urls.rerun,target:this.linkTarget,faIcon:"fa-refresh"})},_render_visualizationsButton:function(){var n=this.model.get("visualizations");if((!this.hasUser)||(!this.model.hasData())||(_.isEmpty(n))){return null}if(_.isObject(n[0])){return this._render_visualizationsFrameworkButton(n)}if(!this.urls.visualization){return null}var k=this.model.get("dbkey"),g=this.urls.visualization,j={},h={dataset_id:this.model.get("id"),hda_ldda:"hda"};if(k){h.dbkey=k}var l=faIconButton({title:_l("Visualize"),faIcon:"fa-bar-chart-o"});var m=this;function i(p){switch(p){case"trackster":return b(g,h,k);case"scatterplot":return e(g,h,m.linkTarget);default:return function(){Galaxy.frame.add({title:"Visualization",type:"url",content:g+"/"+p+"?"+$.param(h)})}}}function o(p){return p.charAt(0).toUpperCase()+p.slice(1)}if(n.length===1){l.attr("data-original-title",_l("Visualize in ")+_l(o(n[0])));l.click(i(n[0]))}else{_.each(n,function(p){j[_l(o(p))]=i(p)});make_popupmenu(l,j)}return l},_render_visualizationsFrameworkButton:function(g){if(!(this.model.hasData())||!(g&&!_.isEmpty(g))){return null}var i=faIconButton({title:_l("Visualize"),faIcon:"fa-bar-chart-o"});i.addClass("visualize-icon");if(_.keys(g).length===1){i.attr("title",_.keys(g)[0]);i.attr("href",_.values(g)[0])}else{var j=[];_.each(g,function(k){j.push(k)});var h=new PopupMenu(i,j)}return i},_buildNewRender:function(){var g=a.HDABaseView.prototype._buildNewRender.call(this);g.find(".dataset-deleted-msg").append(_l('Click <a href="javascript:void(0);" class="dataset-undelete">here</a> to undelete it or <a href="javascript:void(0);" class="dataset-purge">here</a> to immediately remove it from disk'));g.find(".dataset-hidden-msg").append(_l('Click <a href="javascript:void(0);" class="dataset-unhide">here</a> to unhide it'));return g},_render_body_failed_metadata:function(){var h=$("<a/>").attr({href:this.urls.edit,target:this.linkTarget}).text(_l("set it manually or retry auto-detection")),g=$("<span/>").text(". "+_l("You may be able to")+" ").append(h),i=a.HDABaseView.prototype._render_body_failed_metadata.call(this);i.find(".warningmessagesmall strong").append(g);return i},_render_body_error:function(){var g=a.HDABaseView.prototype._render_body_error.call(this);g.find(".dataset-actions .left").prepend(this._render_errButton());return g},_render_body_ok:function(){var g=a.HDABaseView.prototype._render_body_ok.call(this);if(this.model.isDeletedOrPurged()){return g}this.makeDbkeyEditLink(g);if(this.hasUser){g.find(".dataset-actions .left").append(this._render_visualizationsButton());this._renderTags(g);this._renderAnnotation(g)}return g},_renderTags:function(g){var h=this;this.tagsEditor=new TagsEditor({model:this.model,el:g.find(".tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){h.tagsEditorShown=true},onhide:function(){h.tagsEditorShown=false},$activator:faIconButton({title:_l("Edit dataset tags"),classes:"dataset-tag-btn",faIcon:"fa-tags"}).appendTo(g.find(".dataset-actions .right"))});if(this.tagsEditorShown){this.tagsEditor.toggle(true)}},_renderAnnotation:function(g){var h=this;this.annotationEditor=new AnnotationEditor({model:this.model,el:g.find(".annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){h.annotationEditorShown=true},onhide:function(){h.annotationEditorShown=false},$activator:faIconButton({title:_l("Edit dataset annotation"),classes:"dataset-annotate-btn",faIcon:"fa-comment"}).appendTo(g.find(".dataset-actions .right"))});if(this.annotationEditorShown){this.annotationEditor.toggle(true)}},makeDbkeyEditLink:function(g){if(this.model.get("metadata_dbkey")==="?"&&!this.model.isDeletedOrPurged()){g.find(".dataset-dbkey .value").replaceWith($('<a target="'+this.linkTarget+'">?</a>').attr("href",this.urls.edit))}},events:_.extend(_.clone(a.HDABaseView.prototype.events),{"click .dataset-undelete":function(g){this.model.undelete();return false},"click .dataset-unhide":function(g){this.model.unhide();return false},"click .dataset-purge":"confirmPurge"}),confirmPurge:function c(g){this.model.purge();return false},toString:function(){var g=(this.model)?(this.model+""):("(no model)");return"HDAView("+g+")"}});function e(g,i,h){action=function(){Galaxy.frame.add({title:"Scatterplot",type:"url",content:g+"/scatterplot?"+$.param(i),target:h,scratchbook:true});$("div.popmenu-wrapper").remove();return false};return action}function b(g,i,h){return function(){var j={};if(h){j["f-dbkey"]=h}$.ajax({url:g+"/list_tracks?"+$.param(j),dataType:"html",error:function(){alert(("Could not add this dataset to browser")+".")},success:function(k){var l=window.parent;l.Galaxy.modal.show({title:"View Data in a New or Saved Visualization",buttons:{Cancel:function(){l.Galaxy.modal.hide()},"View in saved visualization":function(){l.Galaxy.modal.show({title:"Add Data to Saved Visualization",body:k,buttons:{Cancel:function(){l.Galaxy.modal.hide()},"Add to visualization":function(){$(l.document).find("input[name=id]:checked").each(function(){l.Galaxy.modal.hide();var m=$(this).val();i.id=m;l.Galaxy.frame.add({title:"Trackster",type:"url",content:g+"/trackster?"+$.param(i),scratchbook:true})})}}})},"View in new visualization":function(){l.Galaxy.modal.hide();var m=g+"/trackster?"+$.param(i);l.Galaxy.frame.add({title:"Trackster",type:"url",content:m,scratchbook:true})}}})}});return false}}return{HDAEditView:f}});
\ No newline at end of file
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d static/scripts/packed/mvc/ui.js
--- a/static/scripts/packed/mvc/ui.js
+++ b/static/scripts/packed/mvc/ui.js
@@ -1,1 +1,1 @@
-var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("<div/>")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((!a.disabled&&a.target)?(' target="'+a.target+'"'):("")),' href="',((!a.disabled&&a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};(function(){function a(j,p){var d=27,m=13,c=$(j),e=true,g={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(q){},minSearchLen:0,escWillClear:true,oninit:function(){}};function i(q){var r=$(this).parent().children("input");r.val("");r.trigger("clear:searchInput");p.onclear()}function o(r,q){$(this).trigger("search:searchInput",q);if(typeof p.onfirstsearch==="function"&&e){e=false;p.onfirstsearch(q)}else{p.onsearch(q)}}function f(){return['<input type="text" name="',p.name,'" placeholder="',p.placeholder,'" ','class="search-query ',p.classes,'" ',"/>"].join("")}function l(){return $(f()).focus(function(q){$(this).select()}).keyup(function(r){if(r.which===d&&p.escWillClear){i.call(this,r)}else{var q=$(this).val();if((r.which===m)||(p.minSearchLen&&q.length>=p.minSearchLen)){o.call(this,r,q)}else{if(!q.length){i.call(this,r)}}}}).val(p.initialVal)}function k(){return $(['<span class="search-clear fa fa-times-circle" ','title="',_l("clear search (esc)"),'"></span>'].join("")).tooltip({placement:"bottom"}).click(function(q){i.call(this,q)})}function n(){return $(['<span class="search-loading fa fa-spinner fa-spin" ','title="',_l("loading..."),'"></span>'].join("")).hide().tooltip({placement:"bottom"})}function h(){c.find(".search-loading").toggle();c.find(".search-clear").toggle()}if(jQuery.type(p)==="string"){if(p==="toggle-loading"){h()}return c}if(jQuery.type(p)==="object"){p=jQuery.extend(true,g,p)}return c.addClass("search-input").prepend([l(),k(),n()])}jQuery.fn.extend({searchInput:function b(c){return this.each(function(){return a(this,c)})}})}());(function(){function b(m,l){this.currModeIndex=0;return this.init(m,l)}b.prototype.DATA_KEY="mode-button";b.prototype.defaults={modes:[{mode:"default"}]};b.prototype.init=function f(m,l){l=l||{};this.$element=$(m);this.options=jQuery.extend(true,this.defaults,l);var o=this;this.$element.click(function n(p){o.callModeFn();o._incModeIndex();$(this).html(o.options.modes[o.currModeIndex].html)});this.currModeIndex=0;if(this.options.initialMode){this.currModeIndex=this._getModeIndex(this.options.initialMode)}return this};b.prototype._getModeIndex=function j(l){for(var m=0;m<this.options.modes.length;m+=1){if(this.options.modes[m].mode===l){return m}}throw new Error("mode not found: "+l)};b.prototype.getCurrMode=function a(){return this.options.modes[this.currModeIndex]};b.prototype.getMode=function g(l){if(!l){return this.getCurrMode()}return this.options.modes[(this._getModeIndex(l))]};b.prototype.hasMode=function k(l){return !!this.getMode(l)};b.prototype.currentMode=function e(){return this.options.modes[this.currModeIndex]};b.prototype.setMode=function c(m){var l=this.getMode(m);this.$element.html(l.html||null);return this};b.prototype._incModeIndex=function d(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};b.prototype.callModeFn=function h(l){var m=this.getMode(l).onclick;if(m&&jQuery.type(m==="function")){return m.call(this)}return undefined};jQuery.fn.extend({modeButton:function i(m){var l=jQuery.makeArray(arguments).slice(1);return this.each(function(){var p=$(this),o=p.data("mode-button");if(jQuery.type(m)==="object"){o=new b(p,m);p.data("mode-button",o)}if(o&&jQuery.type(m)==="string"){var n=o[m];if(jQuery.type(n)==="function"){return n.apply(o,l)}}return this})}})}());function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b};
\ No newline at end of file
+var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("<div/>")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((!a.disabled&&a.target)?(' target="'+a.target+'"'):("")),' href="',((!a.disabled&&a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};(function(){function a(j,p){var d=27,m=13,c=$(j),e=true,g={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(q){},minSearchLen:0,escWillClear:true,oninit:function(){}};function i(q){var r=$(this).parent().children("input");r.val("");r.trigger("clear:searchInput");p.onclear()}function o(r,q){$(this).trigger("search:searchInput",q);if(typeof p.onfirstsearch==="function"&&e){e=false;p.onfirstsearch(q)}else{p.onsearch(q)}}function f(){return['<input type="text" name="',p.name,'" placeholder="',p.placeholder,'" ','class="search-query ',p.classes,'" ',"/>"].join("")}function l(){return $(f()).focus(function(q){$(this).select()}).keyup(function(r){if(r.which===d&&p.escWillClear){i.call(this,r)}else{var q=$(this).val();if((r.which===m)||(p.minSearchLen&&q.length>=p.minSearchLen)){o.call(this,r,q)}else{if(!q.length){i.call(this,r)}}}}).val(p.initialVal)}function k(){return $(['<span class="search-clear fa fa-times-circle" ','title="',_l("clear search (esc)"),'"></span>'].join("")).tooltip({placement:"bottom"}).click(function(q){i.call(this,q)})}function n(){return $(['<span class="search-loading fa fa-spinner fa-spin" ','title="',_l("loading..."),'"></span>'].join("")).hide().tooltip({placement:"bottom"})}function h(){c.find(".search-loading").toggle();c.find(".search-clear").toggle()}if(jQuery.type(p)==="string"){if(p==="toggle-loading"){h()}return c}if(jQuery.type(p)==="object"){p=jQuery.extend(true,g,p)}return c.addClass("search-input").prepend([l(),k(),n()])}jQuery.fn.extend({searchInput:function b(c){return this.each(function(){return a(this,c)})}})}());(function(){function b(m,l){this.currModeIndex=0;return this.init(m,l)}b.prototype.DATA_KEY="mode-button";b.prototype.defaults={modes:[{mode:"default"}]};b.prototype.init=function f(m,l){l=l||{};this.$element=$(m);this.options=jQuery.extend(true,{},this.defaults,l);var o=this;this.$element.click(function n(p){o.callModeFn();o._incModeIndex();$(this).html(o.options.modes[o.currModeIndex].html)});this.currModeIndex=0;if(this.options.initialMode){this.currModeIndex=this._getModeIndex(this.options.initialMode)}return this};b.prototype._getModeIndex=function j(l){for(var m=0;m<this.options.modes.length;m+=1){if(this.options.modes[m].mode===l){return m}}throw new Error("mode not found: "+l)};b.prototype.getCurrMode=function a(){return this.options.modes[this.currModeIndex]};b.prototype.getMode=function g(l){if(!l){return this.getCurrMode()}return this.options.modes[(this._getModeIndex(l))]};b.prototype.hasMode=function k(l){return !!this.getMode(l)};b.prototype.currentMode=function e(){return this.options.modes[this.currModeIndex]};b.prototype.setMode=function c(m){var l=this.getMode(m);this.$element.html(l.html||null);return this};b.prototype._incModeIndex=function d(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};b.prototype.callModeFn=function h(l){var m=this.getMode(l).onclick;if(m&&jQuery.type(m==="function")){return m.call(this)}return undefined};jQuery.fn.extend({modeButton:function i(m){var l=jQuery.makeArray(arguments).slice(1);return this.map(function(){var p=$(this),o=p.data("mode-button");if(jQuery.type(m)==="object"){o=new b(p,m);p.data("mode-button",o)}else{if(o&&jQuery.type(m)==="string"){var n=o[m];if(jQuery.type(n)==="function"){return n.apply(o,l)}}else{if(o){return o}}}return this})}})}());function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b};
\ No newline at end of file
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d static/scripts/packed/templates/compiled/history-templates.js
--- a/static/scripts/packed/templates/compiled/history-templates.js
+++ b/static/scripts/packed/templates/compiled/history-templates.js
@@ -1,1 +1,1 @@
-(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-body"]=b(function(g,r,p,k,z){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);z=z||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(D,C){var A="",B;A+='\n <div class="dataset-summary">\n ';if(B=p.body){B=B.call(D,{hash:{},data:C})}else{B=D.body;B=typeof B===e?B.apply(D):B}if(B||B===0){A+=B}A+='\n </div>\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';return A}function m(D,C){var A="",B;A+='\n <div class="dataset-summary">\n ';B=p["if"].call(D,D.misc_blurb,{hash:{},inverse:o.noop,fn:o.program(4,l,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p["if"].call(D,D.data_type,{hash:{},inverse:o.noop,fn:o.program(6,j,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p["if"].call(D,D.metadata_dbkey,{hash:{},inverse:o.noop,fn:o.program(9,f,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p["if"].call(D,D.misc_info,{hash:{},inverse:o.noop,fn:o.program(12,x,C),data:C});if(B||B===0){A+=B}A+='\n </div>\n\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';B=p.unless.call(D,D.deleted,{hash:{},inverse:o.noop,fn:o.program(14,w,C),data:C});if(B||B===0){A+=B}A+="\n\n ";return A}function l(D,C){var A="",B;A+='\n <div class="dataset-blurb">\n <span class="value">';if(B=p.misc_blurb){B=B.call(D,{hash:{},data:C})}else{B=D.misc_blurb;B=typeof B===e?B.apply(D):B}A+=d(B)+"</span>\n </div>\n ";return A}function j(E,D){var A="",C,B;A+='\n <div class="dataset-datatype">\n <label class="prompt">';B={hash:{},inverse:o.noop,fn:o.program(7,i,D),data:D};if(C=p.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!p.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+='</label>\n <span class="value">';if(C=p.data_type){C=C.call(E,{hash:{},data:D})}else{C=E.data_type;C=typeof C===e?C.apply(E):C}A+=d(C)+"</span>\n </div>\n ";return A}function i(B,A){return"format"}function f(E,D){var A="",C,B;A+='\n <div class="dataset-dbkey">\n <label class="prompt">';B={hash:{},inverse:o.noop,fn:o.program(10,y,D),data:D};if(C=p.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!p.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+='</label>\n <span class="value">\n ';if(C=p.metadata_dbkey){C=C.call(E,{hash:{},data:D})}else{C=E.metadata_dbkey;C=typeof C===e?C.apply(E):C}A+=d(C)+"\n </span>\n </div>\n ";return A}function y(B,A){return"database"}function x(D,C){var A="",B;A+='\n <div class="dataset-info">\n <span class="value">';if(B=p.misc_info){B=B.call(D,{hash:{},data:C})}else{B=D.misc_info;B=typeof B===e?B.apply(D):B}A+=d(B)+"</span>\n </div>\n ";return A}function w(D,C){var A="",B;A+='\n <div class="tags-display"></div>\n <div class="annotation-display"></div>\n\n <div class="dataset-display-applications">\n ';B=p.each.call(D,D.display_apps,{hash:{},inverse:o.noop,fn:o.program(15,v,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p.each.call(D,D.display_types,{hash:{},inverse:o.noop,fn:o.program(15,v,C),data:C});if(B||B===0){A+=B}A+='\n </div>\n\n <div class="dataset-peek">\n ';B=p["if"].call(D,D.peek,{hash:{},inverse:o.noop,fn:o.program(19,s,C),data:C});if(B||B===0){A+=B}A+="\n </div>\n\n ";return A}function v(D,C){var A="",B;A+='\n <div class="display-application">\n <span class="display-application-location">';if(B=p.label){B=B.call(D,{hash:{},data:C})}else{B=D.label;B=typeof B===e?B.apply(D):B}A+=d(B)+'</span>\n <span class="display-application-links">\n ';B=p.each.call(D,D.links,{hash:{},inverse:o.noop,fn:o.program(16,u,C),data:C});if(B||B===0){A+=B}A+="\n </span>\n </div>\n ";return A}function u(E,D){var A="",C,B;A+='\n <a target="';if(C=p.target){C=C.call(E,{hash:{},data:D})}else{C=E.target;C=typeof C===e?C.apply(E):C}A+=d(C)+'" href="';if(C=p.href){C=C.call(E,{hash:{},data:D})}else{C=E.href;C=typeof C===e?C.apply(E):C}A+=d(C)+'">';B={hash:{},inverse:o.noop,fn:o.program(17,t,D),data:D};if(C=p.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!p.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+="</a>\n ";return A}function t(C,B){var A;if(A=p.text){A=A.call(C,{hash:{},data:B})}else{A=C.text;A=typeof A===e?A.apply(C):A}return d(A)}function s(D,C){var A="",B;A+='\n <pre class="peek">';if(B=p.peek){B=B.call(D,{hash:{},data:C})}else{B=D.peek;B=typeof B===e?B.apply(D):B}if(B||B===0){A+=B}A+="</pre>\n ";return A}q+='<div class="dataset-body">\n ';h=p["if"].call(r,r.body,{hash:{},inverse:o.program(3,m,z),fn:o.program(1,n,z),data:z});if(h||h===0){q+=h}q+="\n</div>";return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-skeleton"]=b(function(f,r,p,k,w){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,f.helpers);w=w||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var x="",z,y;x+='\n <div class="errormessagesmall">\n ';y={hash:{},inverse:o.noop,fn:o.program(2,m,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+=":\n ";y={hash:{},inverse:o.noop,fn:o.program(4,l,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </div>\n ";return x}function m(y,x){return"There was an error getting the data for this dataset"}function l(z,y){var x;if(x=p.error){x=x.call(z,{hash:{},data:y})}else{x=z.error;x=typeof x===e?x.apply(z):x}return d(x)}function j(A,z){var x="",y;x+="\n ";y=p["if"].call(A,A.purged,{hash:{},inverse:o.program(10,v,z),fn:o.program(7,i,z),data:z});if(y||y===0){x+=y}x+="\n ";return x}function i(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(8,g,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n\n ";return x}function g(y,x){return"This dataset has been deleted and removed from disk."}function v(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(11,u,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n \n Click <a href="javascript:void(0);" class="dataset-undelete">here</a> to undelete it\n or <a href="javascript:void(0);" class="dataset-purge">here</a> to immediately remove it from disk\n </strong></div>\n ';return x}function u(y,x){return"This dataset has been deleted."}function t(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(14,s,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n Click <a href="javascript:void(0);" class="dataset-unhide">here</a> to unhide it\n </strong></div>\n ';return x}function s(y,x){return"This dataset has been hidden."}q+='<div class="dataset hda">\n <div class="dataset-warnings">\n ';h=p["if"].call(r,r.error,{hash:{},inverse:o.noop,fn:o.program(1,n,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(6,j,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p.unless.call(r,r.visible,{hash:{},inverse:o.noop,fn:o.program(13,t,w),data:w});if(h||h===0){q+=h}q+='\n </div>\n\n <div class="dataset-selector"><span class="fa fa-2x fa-square-o"></span></div>\n <div class="dataset-primary-actions"></div>\n \n <div class="dataset-title-bar clear" tabindex="0">\n <span class="dataset-state-icon state-icon"></span>\n <div class="dataset-title">\n <span class="hda-hid">';if(h=p.hid){h=h.call(r,{hash:{},data:w})}else{h=r.hid;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n <span class="dataset-name">';if(h=p.name){h=h.call(r,{hash:{},data:w})}else{h=r.name;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n </div>\n </div>\n\n <div class="dataset-body"></div>\n</div>';return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-skeleton"]=b(function(f,r,p,k,w){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,f.helpers);w=w||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var x="",z,y;x+='\n <div class="errormessagesmall">\n ';y={hash:{},inverse:o.noop,fn:o.program(2,m,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+=":\n ";y={hash:{},inverse:o.noop,fn:o.program(4,l,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </div>\n ";return x}function m(y,x){return"There was an error getting the data for this dataset"}function l(z,y){var x;if(x=p.error){x=x.call(z,{hash:{},data:y})}else{x=z.error;x=typeof x===e?x.apply(z):x}return d(x)}function j(A,z){var x="",y;x+="\n ";y=p["if"].call(A,A.purged,{hash:{},inverse:o.program(10,v,z),fn:o.program(7,i,z),data:z});if(y||y===0){x+=y}x+="\n ";return x}function i(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(8,g,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n\n ";return x}function g(y,x){return"This dataset has been deleted and removed from disk."}function v(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(11,u,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n \n Click <a href="javascript:void(0);" class="dataset-undelete">here</a> to undelete it\n or <a href="javascript:void(0);" class="dataset-purge">here</a> to immediately remove it from disk\n </strong></div>\n ';return x}function u(y,x){return"This dataset has been deleted."}function t(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(14,s,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n Click <a href="javascript:void(0);" class="dataset-unhide">here</a> to unhide it\n </strong></div>\n ';return x}function s(y,x){return"This dataset has been hidden."}q+='<div class="dataset hda">\n <div class="dataset-warnings">\n ';h=p["if"].call(r,r.error,{hash:{},inverse:o.noop,fn:o.program(1,n,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(6,j,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p.unless.call(r,r.visible,{hash:{},inverse:o.noop,fn:o.program(13,t,w),data:w});if(h||h===0){q+=h}q+='\n </div>\n\n <div class="dataset-selector"><span class="fa fa-2x fa-square-o"></span></div>\n <div class="dataset-primary-actions"></div>\n <div class="dataset-title-bar clear" tabindex="0">\n <span class="dataset-state-icon state-icon"></span>\n <div class="dataset-title">\n <span class="hda-hid">';if(h=p.hid){h=h.call(r,{hash:{},data:w})}else{h=r.hid;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n <span class="dataset-name">';if(h=p.name){h=h.call(r,{hash:{},data:w})}else{h=r.name;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n </div>\n </div>\n\n <div class="dataset-body"></div>\n</div>';return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel-anon"]=b(function(h,r,p,l,t){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,h.helpers);t=t||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(x,w){var u="",v;u+='\n <div class="history-name">\n ';if(v=p.name){v=v.call(x,{hash:{},data:w})}else{v=x.name;v=typeof v===e?v.apply(x):v}u+=d(v)+"\n </div>\n ";return u}function m(x,w){var u="",v;u+='\n <div class="history-size">';if(v=p.nice_size){v=v.call(x,{hash:{},data:w})}else{v=x.nice_size;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function k(x,w){var u="",v;u+='\n \n <div class="';if(v=p.status){v=v.call(x,{hash:{},data:w})}else{v=x.status;v=typeof v===e?v.apply(x):v}u+=d(v)+'message">';if(v=p.message){v=v.call(x,{hash:{},data:w})}else{v=x.message;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function j(v,u){return"You are over your disk quota"}function g(v,u){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function s(v,u){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls"></div>\n\n <div class="history-title">\n \n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,t),data:t});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(5,k,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(7,j,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(9,g,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(11,s,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(g,r,p,l,x){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);x=x||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var y="",z;y+='\n <div class="history-name">\n ';if(z=p.name){z=z.call(B,{hash:{},data:A})}else{z=B.name;z=typeof z===e?z.apply(B):z}y+=d(z)+"\n </div>\n ";return y}function m(B,A){var y="",z;y+='\n <div class="history-size">';if(z=p.nice_size){z=z.call(B,{hash:{},data:A})}else{z=B.nice_size;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function k(C,B){var y="",A,z;y+='\n <div class="warningmessagesmall"><strong>\n ';z={hash:{},inverse:o.noop,fn:o.program(6,j,B),data:B};if(A=p.local){A=A.call(C,z)}else{A=C.local;A=typeof A===e?A.apply(C):A}if(!p.local){A=c.call(C,A,z)}if(A||A===0){y+=A}y+="\n </strong></div>\n ";return y}function j(z,y){return"You are currently viewing a deleted history!"}function h(B,A){var y="",z;y+='\n \n <div class="';if(z=p.status){z=z.call(B,{hash:{},data:A})}else{z=B.status;z=typeof z===e?z.apply(B):z}y+=d(z)+'message">';if(z=p.message){z=z.call(B,{hash:{},data:A})}else{z=B.message;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function w(z,y){return"You are over your disk quota"}function v(z,y){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function u(z,y){return"Select all"}function t(z,y){return"For all selected"}function s(z,y){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls"></div>\n\n <div class="history-title">\n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n ';i=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(5,k,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(8,h,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(10,w,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(12,v,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n \n <div class="tags-display"></div>\n <div class="annotation-display"></div>\n\n <div class="history-dataset-actions">\n <button class="history-select-all-datasets-btn btn btn-default"\n data-mode="select">';f={hash:{},inverse:o.noop,fn:o.program(14,u,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='</button>\n <button class="history-dataset-action-popup-btn btn btn-default"\n >';f={hash:{},inverse:o.noop,fn:o.program(16,t,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='...</button>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(18,s,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-body"]=b(function(g,r,p,k,z){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);z=z||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(D,C){var A="",B;A+='\n <div class="dataset-summary">\n ';if(B=p.body){B=B.call(D,{hash:{},data:C})}else{B=D.body;B=typeof B===e?B.apply(D):B}if(B||B===0){A+=B}A+='\n </div>\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';return A}function m(D,C){var A="",B;A+='\n <div class="dataset-summary">\n ';B=p["if"].call(D,D.misc_blurb,{hash:{},inverse:o.noop,fn:o.program(4,l,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p["if"].call(D,D.data_type,{hash:{},inverse:o.noop,fn:o.program(6,j,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p["if"].call(D,D.metadata_dbkey,{hash:{},inverse:o.noop,fn:o.program(9,f,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p["if"].call(D,D.misc_info,{hash:{},inverse:o.noop,fn:o.program(12,x,C),data:C});if(B||B===0){A+=B}A+='\n </div>\n\n <div class="dataset-actions clear">\n <div class="left"></div>\n <div class="right"></div>\n </div>\n\n ';B=p.unless.call(D,D.deleted,{hash:{},inverse:o.noop,fn:o.program(14,w,C),data:C});if(B||B===0){A+=B}A+="\n\n ";return A}function l(D,C){var A="",B;A+='\n <div class="dataset-blurb">\n <span class="value">';if(B=p.misc_blurb){B=B.call(D,{hash:{},data:C})}else{B=D.misc_blurb;B=typeof B===e?B.apply(D):B}A+=d(B)+"</span>\n </div>\n ";return A}function j(E,D){var A="",C,B;A+='\n <div class="dataset-datatype">\n <label class="prompt">';B={hash:{},inverse:o.noop,fn:o.program(7,i,D),data:D};if(C=p.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!p.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+='</label>\n <span class="value">';if(C=p.data_type){C=C.call(E,{hash:{},data:D})}else{C=E.data_type;C=typeof C===e?C.apply(E):C}A+=d(C)+"</span>\n </div>\n ";return A}function i(B,A){return"format"}function f(E,D){var A="",C,B;A+='\n <div class="dataset-dbkey">\n <label class="prompt">';B={hash:{},inverse:o.noop,fn:o.program(10,y,D),data:D};if(C=p.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!p.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+='</label>\n <span class="value">\n ';if(C=p.metadata_dbkey){C=C.call(E,{hash:{},data:D})}else{C=E.metadata_dbkey;C=typeof C===e?C.apply(E):C}A+=d(C)+"\n </span>\n </div>\n ";return A}function y(B,A){return"database"}function x(D,C){var A="",B;A+='\n <div class="dataset-info">\n <span class="value">';if(B=p.misc_info){B=B.call(D,{hash:{},data:C})}else{B=D.misc_info;B=typeof B===e?B.apply(D):B}A+=d(B)+"</span>\n </div>\n ";return A}function w(D,C){var A="",B;A+='\n <div class="tags-display"></div>\n <div class="annotation-display"></div>\n\n <div class="dataset-display-applications">\n ';B=p.each.call(D,D.display_apps,{hash:{},inverse:o.noop,fn:o.program(15,v,C),data:C});if(B||B===0){A+=B}A+="\n\n ";B=p.each.call(D,D.display_types,{hash:{},inverse:o.noop,fn:o.program(15,v,C),data:C});if(B||B===0){A+=B}A+='\n </div>\n\n <div class="dataset-peek">\n ';B=p["if"].call(D,D.peek,{hash:{},inverse:o.noop,fn:o.program(19,s,C),data:C});if(B||B===0){A+=B}A+="\n </div>\n\n ";return A}function v(D,C){var A="",B;A+='\n <div class="display-application">\n <span class="display-application-location">';if(B=p.label){B=B.call(D,{hash:{},data:C})}else{B=D.label;B=typeof B===e?B.apply(D):B}A+=d(B)+'</span>\n <span class="display-application-links">\n ';B=p.each.call(D,D.links,{hash:{},inverse:o.noop,fn:o.program(16,u,C),data:C});if(B||B===0){A+=B}A+="\n </span>\n </div>\n ";return A}function u(E,D){var A="",C,B;A+='\n <a target="';if(C=p.target){C=C.call(E,{hash:{},data:D})}else{C=E.target;C=typeof C===e?C.apply(E):C}A+=d(C)+'" href="';if(C=p.href){C=C.call(E,{hash:{},data:D})}else{C=E.href;C=typeof C===e?C.apply(E):C}A+=d(C)+'">';B={hash:{},inverse:o.noop,fn:o.program(17,t,D),data:D};if(C=p.local){C=C.call(E,B)}else{C=E.local;C=typeof C===e?C.apply(E):C}if(!p.local){C=c.call(E,C,B)}if(C||C===0){A+=C}A+="</a>\n ";return A}function t(C,B){var A;if(A=p.text){A=A.call(C,{hash:{},data:B})}else{A=C.text;A=typeof A===e?A.apply(C):A}return d(A)}function s(D,C){var A="",B;A+='\n <pre class="peek">';if(B=p.peek){B=B.call(D,{hash:{},data:C})}else{B=D.peek;B=typeof B===e?B.apply(D):B}if(B||B===0){A+=B}A+="</pre>\n ";return A}q+='<div class="dataset-body">\n ';h=p["if"].call(r,r.body,{hash:{},inverse:o.program(3,m,z),fn:o.program(1,n,z),data:z});if(h||h===0){q+=h}q+="\n</div>";return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-skeleton"]=b(function(f,r,p,k,w){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,f.helpers);w=w||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var x="",z,y;x+='\n <div class="errormessagesmall">\n ';y={hash:{},inverse:o.noop,fn:o.program(2,m,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+=":\n ";y={hash:{},inverse:o.noop,fn:o.program(4,l,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </div>\n ";return x}function m(y,x){return"There was an error getting the data for this dataset"}function l(z,y){var x;if(x=p.error){x=x.call(z,{hash:{},data:y})}else{x=z.error;x=typeof x===e?x.apply(z):x}return d(x)}function j(A,z){var x="",y;x+="\n ";y=p["if"].call(A,A.purged,{hash:{},inverse:o.program(10,v,z),fn:o.program(7,i,z),data:z});if(y||y===0){x+=y}x+="\n ";return x}function i(B,A){var x="",z,y;x+='\n <div class="dataset-purged-msg warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(8,g,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n\n ";return x}function g(y,x){return"This dataset has been deleted and removed from disk."}function v(B,A){var x="",z,y;x+='\n <div class="dataset-deleted-msg warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(11,u,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n ";return x}function u(y,x){return"This dataset has been deleted."}function t(B,A){var x="",z,y;x+='\n <div class="dataset-hidden-msg warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(14,s,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n ";return x}function s(y,x){return"This dataset has been hidden."}q+='<div class="dataset hda">\n <div class="dataset-warnings">\n ';h=p["if"].call(r,r.error,{hash:{},inverse:o.noop,fn:o.program(1,n,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(6,j,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p.unless.call(r,r.visible,{hash:{},inverse:o.noop,fn:o.program(13,t,w),data:w});if(h||h===0){q+=h}q+='\n </div>\n\n <div class="dataset-selector"><span class="fa fa-2x fa-square-o"></span></div>\n <div class="dataset-primary-actions"></div>\n \n <div class="dataset-title-bar clear" tabindex="0">\n <span class="dataset-state-icon state-icon"></span>\n <div class="dataset-title">\n <span class="hda-hid">';if(h=p.hid){h=h.call(r,{hash:{},data:w})}else{h=r.hid;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n <span class="dataset-name">';if(h=p.name){h=h.call(r,{hash:{},data:w})}else{h=r.name;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n </div>\n </div>\n\n <div class="dataset-body"></div>\n</div>';return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel-anon"]=b(function(h,r,p,l,t){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,h.helpers);t=t||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(x,w){var u="",v;u+='\n <div class="history-name">\n ';if(v=p.name){v=v.call(x,{hash:{},data:w})}else{v=x.name;v=typeof v===e?v.apply(x):v}u+=d(v)+"\n </div>\n ";return u}function m(x,w){var u="",v;u+='\n <div class="history-size">';if(v=p.nice_size){v=v.call(x,{hash:{},data:w})}else{v=x.nice_size;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function k(x,w){var u="",v;u+='\n \n <div class="';if(v=p.status){v=v.call(x,{hash:{},data:w})}else{v=x.status;v=typeof v===e?v.apply(x):v}u+=d(v)+'message">';if(v=p.message){v=v.call(x,{hash:{},data:w})}else{v=x.message;v=typeof v===e?v.apply(x):v}u+=d(v)+"</div>\n ";return u}function j(v,u){return"You are over your disk quota"}function g(v,u){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function s(v,u){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls"></div>\n\n <div class="history-title">\n \n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,t),data:t});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(5,k,t),data:t});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(7,j,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(9,g,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(11,s,t),data:t};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-history-historyPanel"]=b(function(g,r,p,l,x){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,g.helpers);x=x||{};var q="",i,f,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var y="",z;y+='\n <div class="history-name">\n ';if(z=p.name){z=z.call(B,{hash:{},data:A})}else{z=B.name;z=typeof z===e?z.apply(B):z}y+=d(z)+"\n </div>\n ";return y}function m(B,A){var y="",z;y+='\n <div class="history-size">';if(z=p.nice_size){z=z.call(B,{hash:{},data:A})}else{z=B.nice_size;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function k(C,B){var y="",A,z;y+='\n <div class="warningmessagesmall"><strong>\n ';z={hash:{},inverse:o.noop,fn:o.program(6,j,B),data:B};if(A=p.local){A=A.call(C,z)}else{A=C.local;A=typeof A===e?A.apply(C):A}if(!p.local){A=c.call(C,A,z)}if(A||A===0){y+=A}y+="\n </strong></div>\n ";return y}function j(z,y){return"You are currently viewing a deleted history!"}function h(B,A){var y="",z;y+='\n \n <div class="';if(z=p.status){z=z.call(B,{hash:{},data:A})}else{z=B.status;z=typeof z===e?z.apply(B):z}y+=d(z)+'message">';if(z=p.message){z=z.call(B,{hash:{},data:A})}else{z=B.message;z=typeof z===e?z.apply(B):z}y+=d(z)+"</div>\n ";return y}function w(z,y){return"You are over your disk quota"}function v(z,y){return"Tool execution is on hold until your disk usage drops below your allocated quota"}function u(z,y){return"Select all"}function t(z,y){return"For all selected"}function s(z,y){return"Your history is empty. Click 'Get Data' on the left pane to start"}q+='<div class="history-controls">\n <div class="history-search-controls"></div>\n\n <div class="history-title">\n ';i=p["if"].call(r,r.name,{hash:{},inverse:o.noop,fn:o.program(1,n,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="history-subtitle clear">\n ';i=p["if"].call(r,r.nice_size,{hash:{},inverse:o.noop,fn:o.program(3,m,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="history-secondary-actions"></div>\n </div>\n\n ';i=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(5,k,x),data:x});if(i||i===0){q+=i}q+='\n\n <div class="message-container">\n ';i=p["if"].call(r,r.message,{hash:{},inverse:o.noop,fn:o.program(8,h,x),data:x});if(i||i===0){q+=i}q+='\n </div>\n\n <div class="quota-message errormessage">\n ';f={hash:{},inverse:o.noop,fn:o.program(10,w,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+=".\n ";f={hash:{},inverse:o.noop,fn:o.program(12,v,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='.\n </div>\n \n <div class="tags-display"></div>\n <div class="annotation-display"></div>\n\n <div class="history-dataset-actions">\n <button class="history-select-all-datasets-btn btn btn-default"\n data-mode="select">';f={hash:{},inverse:o.noop,fn:o.program(14,u,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='</button>\n <button class="history-dataset-action-popup-btn btn btn-default"\n >';f={hash:{},inverse:o.noop,fn:o.program(16,t,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+='...</button>\n </div>\n\n </div>\n\n \n <div class="datasets-list"></div>\n\n <div class="empty-history-message infomessagesmall">\n ';f={hash:{},inverse:o.noop,fn:o.program(18,s,x),data:x};if(i=p.local){i=i.call(r,f)}else{i=r.local;i=typeof i===e?i.apply(r):i}if(!p.local){i=c.call(r,i,f)}if(i||i===0){q+=i}q+="\n </div>";return q})})();
\ No newline at end of file
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d static/scripts/packed/templates/compiled/template-hda-skeleton.js
--- a/static/scripts/packed/templates/compiled/template-hda-skeleton.js
+++ b/static/scripts/packed/templates/compiled/template-hda-skeleton.js
@@ -1,1 +1,1 @@
-(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-skeleton"]=b(function(f,r,p,k,w){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,f.helpers);w=w||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var x="",z,y;x+='\n <div class="errormessagesmall">\n ';y={hash:{},inverse:o.noop,fn:o.program(2,m,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+=":\n ";y={hash:{},inverse:o.noop,fn:o.program(4,l,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </div>\n ";return x}function m(y,x){return"There was an error getting the data for this dataset"}function l(z,y){var x;if(x=p.error){x=x.call(z,{hash:{},data:y})}else{x=z.error;x=typeof x===e?x.apply(z):x}return d(x)}function j(A,z){var x="",y;x+="\n ";y=p["if"].call(A,A.purged,{hash:{},inverse:o.program(10,v,z),fn:o.program(7,i,z),data:z});if(y||y===0){x+=y}x+="\n ";return x}function i(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(8,g,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n\n ";return x}function g(y,x){return"This dataset has been deleted and removed from disk."}function v(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(11,u,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n \n Click <a href="javascript:void(0);" class="dataset-undelete">here</a> to undelete it\n or <a href="javascript:void(0);" class="dataset-purge">here</a> to immediately remove it from disk\n </strong></div>\n ';return x}function u(y,x){return"This dataset has been deleted."}function t(B,A){var x="",z,y;x+='\n <div class="warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(14,s,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+='\n \n Click <a href="javascript:void(0);" class="dataset-unhide">here</a> to unhide it\n </strong></div>\n ';return x}function s(y,x){return"This dataset has been hidden."}q+='<div class="dataset hda">\n <div class="dataset-warnings">\n ';h=p["if"].call(r,r.error,{hash:{},inverse:o.noop,fn:o.program(1,n,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(6,j,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p.unless.call(r,r.visible,{hash:{},inverse:o.noop,fn:o.program(13,t,w),data:w});if(h||h===0){q+=h}q+='\n </div>\n\n <div class="dataset-selector"><span class="fa fa-2x fa-square-o"></span></div>\n <div class="dataset-primary-actions"></div>\n \n <div class="dataset-title-bar clear" tabindex="0">\n <span class="dataset-state-icon state-icon"></span>\n <div class="dataset-title">\n <span class="hda-hid">';if(h=p.hid){h=h.call(r,{hash:{},data:w})}else{h=r.hid;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n <span class="dataset-name">';if(h=p.name){h=h.call(r,{hash:{},data:w})}else{h=r.name;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n </div>\n </div>\n\n <div class="dataset-body"></div>\n</div>';return q})})();
\ No newline at end of file
+(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a["template-hda-skeleton"]=b(function(f,r,p,k,w){this.compilerInfo=[4,">= 1.0.0"];p=this.merge(p,f.helpers);w=w||{};var q="",h,e="function",d=this.escapeExpression,o=this,c=p.blockHelperMissing;function n(B,A){var x="",z,y;x+='\n <div class="errormessagesmall">\n ';y={hash:{},inverse:o.noop,fn:o.program(2,m,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+=":\n ";y={hash:{},inverse:o.noop,fn:o.program(4,l,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </div>\n ";return x}function m(y,x){return"There was an error getting the data for this dataset"}function l(z,y){var x;if(x=p.error){x=x.call(z,{hash:{},data:y})}else{x=z.error;x=typeof x===e?x.apply(z):x}return d(x)}function j(A,z){var x="",y;x+="\n ";y=p["if"].call(A,A.purged,{hash:{},inverse:o.program(10,v,z),fn:o.program(7,i,z),data:z});if(y||y===0){x+=y}x+="\n ";return x}function i(B,A){var x="",z,y;x+='\n <div class="dataset-purged-msg warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(8,g,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n\n ";return x}function g(y,x){return"This dataset has been deleted and removed from disk."}function v(B,A){var x="",z,y;x+='\n <div class="dataset-deleted-msg warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(11,u,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n ";return x}function u(y,x){return"This dataset has been deleted."}function t(B,A){var x="",z,y;x+='\n <div class="dataset-hidden-msg warningmessagesmall"><strong>\n ';y={hash:{},inverse:o.noop,fn:o.program(14,s,A),data:A};if(z=p.local){z=z.call(B,y)}else{z=B.local;z=typeof z===e?z.apply(B):z}if(!p.local){z=c.call(B,z,y)}if(z||z===0){x+=z}x+="\n </strong></div>\n ";return x}function s(y,x){return"This dataset has been hidden."}q+='<div class="dataset hda">\n <div class="dataset-warnings">\n ';h=p["if"].call(r,r.error,{hash:{},inverse:o.noop,fn:o.program(1,n,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p["if"].call(r,r.deleted,{hash:{},inverse:o.noop,fn:o.program(6,j,w),data:w});if(h||h===0){q+=h}q+="\n\n ";h=p.unless.call(r,r.visible,{hash:{},inverse:o.noop,fn:o.program(13,t,w),data:w});if(h||h===0){q+=h}q+='\n </div>\n\n <div class="dataset-selector"><span class="fa fa-2x fa-square-o"></span></div>\n <div class="dataset-primary-actions"></div>\n \n <div class="dataset-title-bar clear" tabindex="0">\n <span class="dataset-state-icon state-icon"></span>\n <div class="dataset-title">\n <span class="hda-hid">';if(h=p.hid){h=h.call(r,{hash:{},data:w})}else{h=r.hid;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n <span class="dataset-name">';if(h=p.name){h=h.call(r,{hash:{},data:w})}else{h=r.name;h=typeof h===e?h.apply(r):h}q+=d(h)+'</span>\n </div>\n </div>\n\n <div class="dataset-body"></div>\n</div>';return q})})();
\ No newline at end of file
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d static/scripts/templates/compiled/history-templates.js
--- a/static/scripts/templates/compiled/history-templates.js
+++ b/static/scripts/templates/compiled/history-templates.js
@@ -228,7 +228,7 @@
function program7(depth0,data) {
var buffer = "", stack1, options;
- buffer += "\n <div class=\"warningmessagesmall\"><strong>\n ";
+ buffer += "\n <div class=\"dataset-purged-msg warningmessagesmall\"><strong>\n ";
options = {hash:{},inverse:self.noop,fn:self.program(8, program8, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
@@ -246,15 +246,13 @@
function program10(depth0,data) {
var buffer = "", stack1, options;
- buffer += "\n <div class=\"warningmessagesmall\"><strong>\n ";
+ buffer += "\n <div class=\"dataset-deleted-msg warningmessagesmall\"><strong>\n ";
options = {hash:{},inverse:self.noop,fn:self.program(11, program11, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n "
- + "\n "
- + "\n Click <a href=\"javascript:void(0);\" class=\"dataset-undelete\">here</a> to undelete it\n or <a href=\"javascript:void(0);\" class=\"dataset-purge\">here</a> to immediately remove it from disk\n </strong></div>\n ";
+ buffer += "\n </strong></div>\n ";
return buffer;
}
function program11(depth0,data) {
@@ -266,14 +264,13 @@
function program13(depth0,data) {
var buffer = "", stack1, options;
- buffer += "\n <div class=\"warningmessagesmall\"><strong>\n ";
+ buffer += "\n <div class=\"dataset-hidden-msg warningmessagesmall\"><strong>\n ";
options = {hash:{},inverse:self.noop,fn:self.program(14, program14, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n "
- + "\n Click <a href=\"javascript:void(0);\" class=\"dataset-unhide\">here</a> to unhide it\n </strong></div>\n ";
+ buffer += "\n </strong></div>\n ";
return buffer;
}
function program14(depth0,data) {
@@ -305,131 +302,6 @@
});
})();(function() {
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
-templates['template-hda-skeleton'] = template(function (Handlebars,depth0,helpers,partials,data) {
- this.compilerInfo = [4,'>= 1.0.0'];
-helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
- var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this, blockHelperMissing=helpers.blockHelperMissing;
-
-function program1(depth0,data) {
-
- var buffer = "", stack1, options;
- buffer += "\n <div class=\"errormessagesmall\">\n ";
- options = {hash:{},inverse:self.noop,fn:self.program(2, program2, data),data:data};
- if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
- else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += ":\n ";
- options = {hash:{},inverse:self.noop,fn:self.program(4, program4, data),data:data};
- if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
- else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n </div>\n ";
- return buffer;
- }
-function program2(depth0,data) {
-
-
- return "There was an error getting the data for this dataset";
- }
-
-function program4(depth0,data) {
-
- var stack1;
- if (stack1 = helpers.error) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
- else { stack1 = depth0.error; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
- return escapeExpression(stack1);
- }
-
-function program6(depth0,data) {
-
- var buffer = "", stack1;
- buffer += "\n ";
- stack1 = helpers['if'].call(depth0, depth0.purged, {hash:{},inverse:self.program(10, program10, data),fn:self.program(7, program7, data),data:data});
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n ";
- return buffer;
- }
-function program7(depth0,data) {
-
- var buffer = "", stack1, options;
- buffer += "\n <div class=\"warningmessagesmall\"><strong>\n ";
- options = {hash:{},inverse:self.noop,fn:self.program(8, program8, data),data:data};
- if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
- else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n </strong></div>\n\n ";
- return buffer;
- }
-function program8(depth0,data) {
-
-
- return "This dataset has been deleted and removed from disk.";
- }
-
-function program10(depth0,data) {
-
- var buffer = "", stack1, options;
- buffer += "\n <div class=\"warningmessagesmall\"><strong>\n ";
- options = {hash:{},inverse:self.noop,fn:self.program(11, program11, data),data:data};
- if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
- else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n "
- + "\n "
- + "\n Click <a href=\"javascript:void(0);\" class=\"dataset-undelete\">here</a> to undelete it\n or <a href=\"javascript:void(0);\" class=\"dataset-purge\">here</a> to immediately remove it from disk\n </strong></div>\n ";
- return buffer;
- }
-function program11(depth0,data) {
-
-
- return "This dataset has been deleted.";
- }
-
-function program13(depth0,data) {
-
- var buffer = "", stack1, options;
- buffer += "\n <div class=\"warningmessagesmall\"><strong>\n ";
- options = {hash:{},inverse:self.noop,fn:self.program(14, program14, data),data:data};
- if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
- else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
- if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n "
- + "\n Click <a href=\"javascript:void(0);\" class=\"dataset-unhide\">here</a> to unhide it\n </strong></div>\n ";
- return buffer;
- }
-function program14(depth0,data) {
-
-
- return "This dataset has been hidden.";
- }
-
- buffer += "<div class=\"dataset hda\">\n <div class=\"dataset-warnings\">\n ";
- stack1 = helpers['if'].call(depth0, depth0.error, {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n\n ";
- stack1 = helpers['if'].call(depth0, depth0.deleted, {hash:{},inverse:self.noop,fn:self.program(6, program6, data),data:data});
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n\n ";
- stack1 = helpers.unless.call(depth0, depth0.visible, {hash:{},inverse:self.noop,fn:self.program(13, program13, data),data:data});
- if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n </div>\n\n <div class=\"dataset-selector\"><span class=\"fa fa-2x fa-square-o\"></span></div>\n <div class=\"dataset-primary-actions\"></div>\n <div class=\"dataset-title-bar clear\" tabindex=\"0\">\n <span class=\"dataset-state-icon state-icon\"></span>\n <div class=\"dataset-title\">\n <span class=\"hda-hid\">";
- if (stack1 = helpers.hid) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
- else { stack1 = depth0.hid; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
- buffer += escapeExpression(stack1)
- + "</span>\n <span class=\"dataset-name\">";
- if (stack1 = helpers.name) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
- else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
- buffer += escapeExpression(stack1)
- + "</span>\n </div>\n </div>\n\n <div class=\"dataset-body\"></div>\n</div>";
- return buffer;
- });
-})();(function() {
- var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates['template-history-historyPanel-anon'] = template(function (Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d static/scripts/templates/compiled/template-hda-skeleton.js
--- a/static/scripts/templates/compiled/template-hda-skeleton.js
+++ b/static/scripts/templates/compiled/template-hda-skeleton.js
@@ -49,7 +49,7 @@
function program7(depth0,data) {
var buffer = "", stack1, options;
- buffer += "\n <div class=\"warningmessagesmall\"><strong>\n ";
+ buffer += "\n <div class=\"dataset-purged-msg warningmessagesmall\"><strong>\n ";
options = {hash:{},inverse:self.noop,fn:self.program(8, program8, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
@@ -67,15 +67,13 @@
function program10(depth0,data) {
var buffer = "", stack1, options;
- buffer += "\n <div class=\"warningmessagesmall\"><strong>\n ";
+ buffer += "\n <div class=\"dataset-deleted-msg warningmessagesmall\"><strong>\n ";
options = {hash:{},inverse:self.noop,fn:self.program(11, program11, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n "
- + "\n "
- + "\n Click <a href=\"javascript:void(0);\" class=\"dataset-undelete\">here</a> to undelete it\n or <a href=\"javascript:void(0);\" class=\"dataset-purge\">here</a> to immediately remove it from disk\n </strong></div>\n ";
+ buffer += "\n </strong></div>\n ";
return buffer;
}
function program11(depth0,data) {
@@ -87,14 +85,13 @@
function program13(depth0,data) {
var buffer = "", stack1, options;
- buffer += "\n <div class=\"warningmessagesmall\"><strong>\n ";
+ buffer += "\n <div class=\"dataset-hidden-msg warningmessagesmall\"><strong>\n ";
options = {hash:{},inverse:self.noop,fn:self.program(14, program14, data),data:data};
if (stack1 = helpers.local) { stack1 = stack1.call(depth0, options); }
else { stack1 = depth0.local; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
if (!helpers.local) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
if(stack1 || stack1 === 0) { buffer += stack1; }
- buffer += "\n "
- + "\n Click <a href=\"javascript:void(0);\" class=\"dataset-unhide\">here</a> to unhide it\n </strong></div>\n ";
+ buffer += "\n </strong></div>\n ";
return buffer;
}
function program14(depth0,data) {
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d static/scripts/templates/hda-templates.html
--- a/static/scripts/templates/hda-templates.html
+++ b/static/scripts/templates/hda-templates.html
@@ -11,26 +11,20 @@
{{#if deleted}}
{{#if purged}}
- <div class="warningmessagesmall"><strong>
+ <div class="dataset-purged-msg warningmessagesmall"><strong>
{{#local}}This dataset has been deleted and removed from disk.{{/local}}
</strong></div>
{{else}}{{! deleted, not purged }}
- <div class="warningmessagesmall"><strong>
+ <div class="dataset-deleted-msg warningmessagesmall"><strong>
{{#local}}This dataset has been deleted.{{/local}}
- {{! edit-only }}
- {{! how in the hell would you localize this? }}
- Click <a href="javascript:void(0);" class="dataset-undelete">here</a> to undelete it
- or <a href="javascript:void(0);" class="dataset-purge">here</a> to immediately remove it from disk
</strong></div>
{{/if}}
{{/if}}
{{#unless visible}}
- <div class="warningmessagesmall"><strong>
+ <div class="dataset-hidden-msg warningmessagesmall"><strong>
{{#local}}This dataset has been hidden.{{/local}}
- {{! edit-only }}
- Click <a href="javascript:void(0);" class="dataset-unhide">here</a> to unhide it
</strong></div>
{{/unless}}
</div>
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d templates/webapps/galaxy/history/history_panel.mako
--- a/templates/webapps/galaxy/history/history_panel.mako
+++ b/templates/webapps/galaxy/history/history_panel.mako
@@ -92,12 +92,14 @@
${h.js(
"utils/localization",
"mvc/base-mvc",
- "mvc/tags", "mvc/annotations"
+ "mvc/tags",
+ "mvc/annotations"
)}
##TODO: concat these
${h.templates(
- "history-templates"
+ "history-templates",
+ "helpers-common-templates"
)}
${localize_js_strings([
@@ -148,9 +150,9 @@
//require([ "/static/scripts/history-panel.min.js" ], function( historyPanel ){
require([ "mvc/history/history-panel" ], function( historyPanel ){
- onhistoryready.resolve( historyPanel )
+ $(function(){
+ onhistoryready.resolve( historyPanel )
+ });
});
</script></%def>
-
-
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d templates/webapps/galaxy/history/original_view.mako
--- /dev/null
+++ b/templates/webapps/galaxy/history/original_view.mako
@@ -0,0 +1,173 @@
+<%namespace file="/display_common.mako" import="get_history_link, get_controller_name" />
+<%namespace file="/root/history_common.mako" import="render_dataset" />
+<%namespace file="/tagging_common.mako" import="render_individual_tagging_element, render_community_tagging_element" />
+
+<%!
+ def inherit(context):
+ if context.get('use_panels'):
+ return '/webapps/galaxy/base_panels.mako'
+ else:
+ return '/base.mako'
+%>
+<%inherit file="${inherit(context)}"/>
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${h.js( "libs/jquery/jstorage" )}
+ <script type="text/javascript">
+ $(function() {
+ init_history_items( $("div.historyItemWrapper"), false, "nochanges" );
+ $( '#switch-to-link' ).click( function( event ){
+ var galaxy = window.Galaxy || window.parent.Galaxy;
+ if( galaxy ){
+ galaxy.currHistoryPanel.switchToHistory( '${ trans.security.encode_id( history.id ) }' );
+ }
+ });
+ $( '#refresh' ).click( function( event ){ window.location.reload( true ); })
+ });
+ </script>
+</%def>
+
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "history", "autocomplete_tagging" )}
+
+ <style type="text/css">
+
+ /* these don't appear to be used? */
+ .page-body
+ {
+ padding: 10px;
+ float: left;
+ width: 65%;
+ }
+ .page-meta
+ {
+ float: right;
+ width: 27%;
+ padding: 0.5em;
+ margin: 0.25em;
+ vertical-align: text-top;
+ border: 2px solid #DDDDDD;
+ border-top: 4px solid #DDDDDD;
+ }
+
+
+ body {
+ padding: 0px;
+ margin: 0px;
+ }
+
+ div.unified-panel-body {
+ position: absolute;
+ top: 0px;
+ width: 100%;
+ }
+
+ #history-name-area {
+ margin: 12px 0px 0px 16px;
+ font-size: 120%;
+ }
+ #top-links {
+ margin: 4px 0px 8px 16px;
+ }
+
+ .historyItemContainer {
+ /*padding-right: 3px;*/
+ }
+ .historyItemBody {
+ display: none;
+ }
+ div.historyItemWrapper {
+ margin: 0px 4px 0px 4px ;
+ border-left: 1px solid #999999;
+ border-right: 1px solid #999999;
+ }
+ /* TODO: unify with other history css and into .less */
+ </style>
+
+ <noscript>
+ <style>
+ .historyItemBody {
+ display: block;
+ }
+ </style>
+ </noscript>
+</%def>
+
+<%def name="init()">
+<%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.message_box_visible=False
+%>
+</%def>
+
+<%def name="body()">
+ ${center_panel()}
+</%def>
+
+<%def name="center_panel()">
+ ## Get URL to other histories owned by user that owns this history.
+ <%
+ ##TODO: is there a better way to create this URL? Can't use 'f-username' as a key b/c it's not a valid identifier.
+ href_to_published_histories = h.url_for( controller='/history', action='list_published')
+ if history.user is not None:
+ href_to_user_histories = h.url_for( controller='/history', action='list_published', xxx=history.user.username).replace( 'xxx', 'f-username')
+ else:
+ href_to_user_histories = h.url_for( controller='/history', action='list_published' )##should this instead be be None or empty string?
+ %>
+
+ <div class="unified-panel-body">
+ <div style="overflow: auto; height: 100%;">
+ ## Render view of history.
+ <div id="history-name-area" class="historyLinks" style="color: gray; font-weight: bold; padding: 0px 0px 5px 0px">
+ <div id="history-name">${history.get_display_name()}</div>
+ </div>
+
+ <div id="top-links" class="historyLinks" style="padding: 0px 0px 5px 0px">
+ %if not history.purged and history.user != trans.user:
+ ##TODO: need to remove _top
+ <a href="${h.url_for(controller='history', action='imp', id=trans.security.encode_id(history.id) )}"
+ >import and start using history</a> |
+ <a id="refresh" href="javascript:void(0)" >${_('refresh')}</a> |
+ %endif
+ %if not history.purged and history.user == trans.user:
+ <a id="switch-to-link" href="javascript:void(0)">${_('switch to this history')}</a> |
+ <a id="refresh" href="javascript:void(0)" >${_('refresh')}</a> |
+ %endif
+ %if show_deleted:
+ <a href="${h.url_for(controller='history', action='view', id=trans.security.encode_id(history.id), show_deleted=False, use_panels=use_panels )}">${_('hide deleted')}</a> |
+ %else:
+ <a href="${h.url_for(controller='history', action='view', id=trans.security.encode_id(history.id), show_deleted=True, use_panels=use_panels )}">${_('show deleted')}</a> |
+ %endif
+ <a href="#" class="toggle">collapse all</a>
+ </div>
+
+ %if history.deleted:
+ <div class="warningmessagesmall">
+ ${_('You are currently viewing a deleted history!')}
+ </div>
+ <p></p>
+ %endif
+
+ %if not datasets:
+ <div class="infomessagesmall" id="emptyHistoryMessage">
+
+ %else:
+ ## Render requested datasets, ordered from newest to oldest
+ %for data in datasets:
+ %if data.visible:
+ <div class="historyItemContainer visible-right-border" id="historyItemContainer-${data.id}">
+ ${render_dataset( data, data.hid, show_deleted_on_refresh = show_deleted, for_editing=False )}
+ </div>
+ %endif
+ %endfor
+
+ <div class="infomessagesmall" id="emptyHistoryMessage" style="display:none;">
+ %endif
+ ${_("Your history is empty. Click 'Get Data' on the left pane to start")}
+ </div>
+ </div>
+ </div>
+</%def>
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d templates/webapps/galaxy/history/view.mako
--- a/templates/webapps/galaxy/history/view.mako
+++ b/templates/webapps/galaxy/history/view.mako
@@ -1,7 +1,7 @@
-<%namespace file="/display_common.mako" import="get_history_link, get_controller_name" />
-<%namespace file="/root/history_common.mako" import="render_dataset" />
-<%namespace file="/tagging_common.mako" import="render_individual_tagging_element, render_community_tagging_element" />
+<%namespace file="/history/history_panel.mako" import="history_panel_javascripts" />
+<%namespace file="/galaxy.masthead.mako" import="get_user_json" />
+## ----------------------------------------------------------------------------
<%!
def inherit(context):
if context.get('use_panels'):
@@ -11,90 +11,6 @@
%><%inherit file="${inherit(context)}"/>
-<%def name="javascripts()">
- ${parent.javascripts()}
- ${h.js( "libs/jquery/jstorage" )}
- <script type="text/javascript">
- $(function() {
- init_history_items( $("div.historyItemWrapper"), false, "nochanges" );
- $( '#switch-to-link' ).click( function( event ){
- var galaxy = window.Galaxy || window.parent.Galaxy;
- if( galaxy ){
- galaxy.currHistoryPanel.switchToHistory( '${ trans.security.encode_id( history.id ) }' );
- }
- });
- $( '#refresh' ).click( function( event ){ window.location.reload( true ); })
- });
- </script>
-</%def>
-
-<%def name="stylesheets()">
- ${parent.stylesheets()}
- ${h.css( "history", "autocomplete_tagging" )}
-
- <style type="text/css">
-
- /* these don't appear to be used? */
- .page-body
- {
- padding: 10px;
- float: left;
- width: 65%;
- }
- .page-meta
- {
- float: right;
- width: 27%;
- padding: 0.5em;
- margin: 0.25em;
- vertical-align: text-top;
- border: 2px solid #DDDDDD;
- border-top: 4px solid #DDDDDD;
- }
-
-
- body {
- padding: 0px;
- margin: 0px;
- }
-
- div.unified-panel-body {
- position: absolute;
- top: 0px;
- width: 100%;
- }
-
- #history-name-area {
- margin: 12px 0px 0px 16px;
- font-size: 120%;
- }
- #top-links {
- margin: 4px 0px 8px 16px;
- }
-
- .historyItemContainer {
- /*padding-right: 3px;*/
- }
- .historyItemBody {
- display: none;
- }
- div.historyItemWrapper {
- margin: 0px 4px 0px 4px ;
- border-left: 1px solid #999999;
- border-right: 1px solid #999999;
- }
- /* TODO: unify with other history css and into .less */
- </style>
-
- <noscript>
- <style>
- .historyItemBody {
- display: block;
- }
- </style>
- </noscript>
-</%def>
-
<%def name="init()"><%
self.has_left_panel=False
@@ -103,71 +19,171 @@
%></%def>
+<%def name="center_panel()">
+</%def>
+
+## ----------------------------------------------------------------------------
<%def name="body()">
${center_panel()}
</%def>
+## ----------------------------------------------------------------------------
+<%def name="title()">
+ ${history[ 'name' ]}
+</%def>
+
+## ----------------------------------------------------------------------------
+<%def name="stylesheets()">
+${parent.stylesheets()}
+<style>
+%if not use_panels:
+ body, html {
+ margin: 0px;
+ padding: 0px;
+ }
+%endif
+#header {
+ background-color: white;
+ border-bottom: 1px solid #DDD;
+ width: 100%;
+ height: 48px;
+}
+#history-view-controls {
+ margin: 10px 10px 10px 10px;
+}
+.history-panel {
+ /* this and the height of #header above are way too tweaky */
+ margin-top: 18px;
+}
+.history-title {
+ font-size: 120%;
+}
+.history-title input {
+ font-size: 100%;
+}
+a.btn {
+ text-decoration: none;
+}
+</style>
+</%def>
+
+## ----------------------------------------------------------------------------
+<%def name="javascripts()">
+${parent.javascripts()}
+${history_panel_javascripts()}
+
+%if not use_panels:
+ ${h.js( 'mvc/user/user-model' )}
+%endif
+
+<script type="text/javascript">
+ var using_panels = ${ 'false' if not use_panels else 'true' };
+ %if not use_panels:
+ window.Galaxy = {};
+ Galaxy.currUser = new User(${h.to_json_string( get_user_json() )});
+ %endif
+</script>
+</%def>
+
+## ----------------------------------------------------------------------------
<%def name="center_panel()">
- ## Get URL to other histories owned by user that owns this history.
- <%
- ##TODO: is there a better way to create this URL? Can't use 'f-username' as a key b/c it's not a valid identifier.
- href_to_published_histories = h.url_for( controller='/history', action='list_published')
- if history.user is not None:
- href_to_user_histories = h.url_for( controller='/history', action='list_published', xxx=history.user.username).replace( 'xxx', 'f-username')
- else:
- href_to_user_histories = h.url_for( controller='/history', action='list_published' )##should this instead be be None or empty string?
- %>
-
- <div class="unified-panel-body">
- <div style="overflow: auto; height: 100%;">
- ## Render view of history.
- <div id="history-name-area" class="historyLinks" style="color: gray; font-weight: bold; padding: 0px 0px 5px 0px">
- <div id="history-name">${history.get_display_name()}</div>
- </div>
+<div id="header" class="clear">
+ <div id="history-view-controls" class="pull-right">
+ <%
+ show_deleted = context.get( 'show_deleted', None )
+ show_hidden = context.get( 'show_hidden', None )
- <div id="top-links" class="historyLinks" style="padding: 0px 0px 5px 0px">
- %if not history.purged and history.user != trans.user:
- ##TODO: need to remove _top
- <a href="${h.url_for(controller='history', action='imp', id=trans.security.encode_id(history.id) )}"
- >import and start using history</a> |
- <a id="refresh" href="javascript:void(0)" >${_('refresh')}</a> |
- %endif
- %if not history.purged and history.user == trans.user:
- <a id="switch-to-link" href="javascript:void(0)">${_('switch to this history')}</a> |
- <a id="refresh" href="javascript:void(0)" >${_('refresh')}</a> |
- %endif
- %if show_deleted:
- <a href="${h.url_for(controller='history', action='view', id=trans.security.encode_id(history.id), show_deleted=False, use_panels=use_panels )}">${_('hide deleted')}</a> |
- %else:
- <a href="${h.url_for(controller='history', action='view', id=trans.security.encode_id(history.id), show_deleted=True, use_panels=use_panels )}">${_('show deleted')}</a> |
- %endif
- <a href="#" class="toggle">collapse all</a>
- </div>
+ show_deleted_js = 'true' if show_deleted == True else ( 'null' if show_deleted == None else 'false' )
+ show_hidden_js = 'true' if show_hidden == True else ( 'null' if show_hidden == None else 'false' )
+ %>
+ %if not history[ 'purged' ]:
+ <a id="import" class="btn btn-default" style="display: none;"
+ href="${h.url_for( controller='history', action='imp', id=history['id'], include_deleted=show_deleted )}">
+ ${_('Import and start using history')}
+ </a>
+ %endif
+ <button id="toggle-deleted" class="btn btn-default">
+ ${_('Exclude deleted') if show_deleted else _('Include deleted')}
+ </button>
+ <button id="toggle-hidden" class="btn btn-default">
+ ${_('Exclude hidden') if show_hidden else _('Include hidden')}
+ </button>
+ </div>
+</div>
- %if history.deleted:
- <div class="warningmessagesmall">
- ${_('You are currently viewing a deleted history!')}
- </div>
- <p></p>
- %endif
+<div id="history-${ history[ 'id' ] }" class="history-panel unified-panel-body" style="overflow: auto;"></div>
- %if not datasets:
- <div class="infomessagesmall" id="emptyHistoryMessage">
+<script type="text/javascript">
+ var debugging = JSON.parse( sessionStorage.getItem( 'debugging' ) ) || false,
+ historyJSON = ${h.to_json_string( history )},
+ hdaJSON = ${h.to_json_string( hdas )};
+ window.hdaJSON = hdaJSON;
- %else:
- ## Render requested datasets, ordered from newest to oldest
- %for data in datasets:
- %if data.visible:
- <div class="historyItemContainer visible-right-border" id="historyItemContainer-${data.id}">
- ${render_dataset( data, data.hid, show_deleted_on_refresh = show_deleted, for_editing=False )}
- </div>
- %endif
- %endfor
+ $( '#toggle-deleted' ).modeButton({
+ initialMode : (${ show_deleted_js })?( 'exclude' ):( 'include' ),
+ modes: [
+ { mode: 'exclude', html: _l( 'Exclude deleted' ) },
+ { mode: 'include', html: _l( 'Include deleted' ) }
+ ]
+ }).click( function(){
+ // allow the 'include/exclude deleted' button to control whether the 'import' button includes deleted datasets
+ // when importing or not; when deleted datasets are shown, they'll be imported
+ var $importBtn = $( '#import' );
+ if( $importBtn.size() ){
+ // a bit hacky
+ var href = $importBtn.attr( 'href' ),
+ includeDeleted = $( this ).modeButton()[0].getMode().mode === 'exclude';
+ href = href.replace( /include_deleted=True|False/, ( includeDeleted )?( 'True' ):( 'False' ) );
+ $importBtn.attr( 'href', href );
+ $importBtn.text( includeDeleted ? _l( 'Import with deleted datasets and start using history' )
+ : _l( 'Import and start using history' ) );
+ }
+ });
- <div class="infomessagesmall" id="emptyHistoryMessage" style="display:none;">
- %endif
- ${_("Your history is empty. Click 'Get Data' on the left pane to start")}
- </div>
- </div>
- </div>
+ $( '#toggle-hidden' ).modeButton({
+ initialMode : (${ show_hidden_js })?( 'exclude' ):( 'include' ),
+ modes: [
+ { mode: 'exclude', html: _l( 'Exclude hidden' ) },
+ { mode: 'include', html: _l( 'Include hidden' ) }
+ ]
+ });
+
+ ##TODO: move to mako
+ if( Galaxy.currUser.id !== historyJSON.user_id ){
+ $( '#import' ).show();
+ }
+
+ require.config({
+ baseUrl : "${h.url_for( '/static/scripts' )}"
+ });
+ require([ "mvc/history/history-panel" ], function( historyPanel ){
+ // history module is already in the dpn chain from the panel. We can re-scope it here.
+ var historyModel = require( 'mvc/history/history-model' ),
+ hdaBaseView = require( 'mvc/dataset/hda-base' );
+
+ var history = new historyModel.History( historyJSON, hdaJSON, {
+ logger: ( debugging )?( console ):( null )
+ });
+
+ window.historyPanel = new historyPanel.HistoryPanel({
+ HDAViewClass : ( Galaxy.currUser.id === historyJSON.user_id )?
+ ( hdaBaseView.HDAEditView ):( hdaBaseView.HDABaseView ),
+ show_deleted : ${show_deleted_js},
+ show_hidden : ${show_hidden_js},
+ el : $( "#history-" + historyJSON.id ),
+ model : history,
+ onready : function(){
+ var panel = this;
+ $( '#toggle-deleted' ).on( 'click', function(){
+ panel.toggleShowDeleted();
+ });
+ $( '#toggle-hidden' ).on( 'click', function(){
+ panel.toggleShowHidden();
+ });
+ this.render();
+ }
+ });
+ });
+</script>
+
</%def>
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 49394b5fc1e92322cc84d645d1de213658453a3d templates/webapps/galaxy/root/history.mako
--- a/templates/webapps/galaxy/root/history.mako
+++ b/templates/webapps/galaxy/root/history.mako
@@ -21,13 +21,10 @@
## -----------------------------------------------------------------------------
<%def name="javascripts()">
${parent.javascripts()}
+${history_panel_javascripts()}
${h.js(
- "libs/require",
- "mvc/base-mvc",
- "utils/localization",
"mvc/user/user-model"
)}
-${h.templates( "helpers-common-templates" )}
<script type="text/javascript">
if( !window.Galaxy ){
@@ -40,8 +37,6 @@
});
</script>
-${history_panel_javascripts()}
-
<script type="text/javascript">
onhistoryready.done( function( historyPanel ){
https://bitbucket.org/galaxy/galaxy-central/commits/7ebebaa348c3/
Changeset: 7ebebaa348c3
User: carlfeberhard
Date: 2014-01-06 19:50:13
Summary: Merge
Affected #: 2 files
diff -r 49394b5fc1e92322cc84d645d1de213658453a3d -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 test/tool_shed/functional/test_0520_import_export_circular_dependencies.py
--- /dev/null
+++ b/test/tool_shed/functional/test_0520_import_export_circular_dependencies.py
@@ -0,0 +1,84 @@
+from tool_shed.base.twilltestcase import ShedTwillTestCase, common
+
+import logging
+log = logging.getLogger( __name__ )
+
+filtering_repository_name = 'filtering_0520'
+filtering_repository_description = "Galaxy's filtering tool for test 0520"
+filtering_repository_long_description = "Long description of Galaxy's filtering tool for test 0520"
+freebayes_repository_name = 'freebayes_0520'
+freebayes_repository_description = "Galaxy's freebayes tool"
+freebayes_repository_long_description = "Long description of Galaxy's freebayes tool for test 0520"
+
+category_name = 'Test 0520 Circular Dependency Import Export'
+category_description = 'Test script 0520 for importing and exporting repositories with circular dependencies.'
+
+'''
+1) Upload a capsule with 2 repositories that define simple repository dependencies on each other, resulting in circular
+ dependencies to a tool shed.
+2) Make sure each repository contains an invalid repository dependency after the capsule has been uploaded (they should
+ be invalid because their toolshed and changeset_revision attributes could not be auto-populated).
+3) Make sure each repository's repository_metadata record has the downloadable column marked as False.
+'''
+
+
+class TestExportImportRepository( ShedTwillTestCase ):
+ '''Test exporting and importing repositories.'''
+
+ def test_0000_initiate_users( self ):
+ """Create necessary user accounts and login as an admin user."""
+ self.logout()
+ self.login( email=common.test_user_1_email, username=common.test_user_1_name )
+ test_user_1 = self.test_db_util.get_user( common.test_user_1_email )
+ assert test_user_1 is not None, 'Problem retrieving user with email %s from the database' % common.test_user_1_email
+ test_user_1_private_role = self.test_db_util.get_private_role( test_user_1 )
+ self.logout()
+ self.login( email=common.admin_email, username=common.admin_username )
+ admin_user = self.test_db_util.get_user( common.admin_email )
+ assert admin_user is not None, 'Problem retrieving user with email %s from the database' % common.admin_email
+ admin_user_private_role = self.test_db_util.get_private_role( admin_user )
+
+ def test_0005_import_repository_capsule( self ):
+ """Import the filter_0520 repository capsule with dependencies."""
+ '''
+ This is step 1 - Upload a capsule with 2 repositories that define simple repository dependencies on each other, resulting in
+ circular dependencies to a tool shed.
+ '''
+ self.logout()
+ self.login( email=common.admin_email, username=common.admin_username )
+ self.create_category( name=category_name, description=category_description )
+ self.logout()
+ self.login( email=common.test_user_1_email, username=common.test_user_1_name )
+ self.import_capsule( self.get_filename( 'repository_capsules/0520_filtering.tar.gz' ),
+ strings_displayed=[ filtering_repository_name, freebayes_repository_name, '<b>Exists' ],
+ strings_not_displayed=[ ' Exists' ],
+ strings_displayed_after_submit=[ 'Repository <b>filtering_0520</b> has been created.' ],
+ strings_not_displayed_after_submit=[ 'Import not necessary' ] )
+
+ def test_0010_verify_invalid_dependency( self ):
+ '''Verify that the repository dependencies are marked as invalid.'''
+ '''
+ This is step 2 - Make sure each repository contains an invalid repository dependency after the capsule has been uploaded
+ (they should be invalid because their toolshed and changeset_revision attributes could not be auto-populated).
+ '''
+ freebayes_repository = self.test_db_util.get_repository_by_name_and_owner( freebayes_repository_name, common.test_user_1_name )
+ filtering_repository = self.test_db_util.get_repository_by_name_and_owner( filtering_repository_name, common.test_user_1_name )
+ strings_displayed = [ 'Ignoring repository dependency definition', self.url, 'filtering_0520', 'name is invalid' ]
+ self.display_manage_repository_page( freebayes_repository,
+ strings_displayed=strings_displayed,
+ strings_not_displayed=[ 'Repository dependencies' ] )
+ self.display_manage_repository_page( filtering_repository,
+ strings_displayed=[ 'Repository dependencies', self.get_repository_tip( freebayes_repository ) ],
+ strings_not_displayed=[] )
+
+ def test_0015_verify_repository_metadata( self ):
+ '''Verify that the repositories are not marked as downloadable.'''
+ '''
+ This is step 3 - Make sure each repository's repository_metadata record has the downloadable column marked as False.
+ '''
+ freebayes_repository = self.test_db_util.get_repository_by_name_and_owner( freebayes_repository_name, common.test_user_1_name )
+ filtering_repository = self.test_db_util.get_repository_by_name_and_owner( filtering_repository_name, common.test_user_1_name )
+ freebayes_metadata = self.get_repository_metadata_by_changeset_revision( freebayes_repository, self.get_repository_tip( freebayes_repository ) )
+ filtering_metadata = self.get_repository_metadata_by_changeset_revision( filtering_repository, self.get_repository_tip( filtering_repository ) )
+ assert not filtering_metadata.downloadable, 'Repository filtering_0520 is incorrectly marked as downloadable.'
+ assert not freebayes_metadata.downloadable, 'Repository freebayes_0520 is incorrectly marked as downloadable.'
diff -r 49394b5fc1e92322cc84d645d1de213658453a3d -r 7ebebaa348c316203ff7aee7ffa67fdc2db3ff88 test/tool_shed/test_data/repository_capsules/0520_filtering.tar.gz
Binary file test/tool_shed/test_data/repository_capsules/0520_filtering.tar.gz has changed
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: Functional test for importing an exported repository capsule where a circular repository dependency has been defined.
by commits-noreply@bitbucket.org 06 Jan '14
by commits-noreply@bitbucket.org 06 Jan '14
06 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/5c6c916b0b98/
Changeset: 5c6c916b0b98
User: inithello
Date: 2014-01-06 19:48:17
Summary: Functional test for importing an exported repository capsule where a circular repository dependency has been defined.
Affected #: 2 files
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 5c6c916b0b9862cf572af3a70e87a1f20546e6fe test/tool_shed/functional/test_0520_import_export_circular_dependencies.py
--- /dev/null
+++ b/test/tool_shed/functional/test_0520_import_export_circular_dependencies.py
@@ -0,0 +1,84 @@
+from tool_shed.base.twilltestcase import ShedTwillTestCase, common
+
+import logging
+log = logging.getLogger( __name__ )
+
+filtering_repository_name = 'filtering_0520'
+filtering_repository_description = "Galaxy's filtering tool for test 0520"
+filtering_repository_long_description = "Long description of Galaxy's filtering tool for test 0520"
+freebayes_repository_name = 'freebayes_0520'
+freebayes_repository_description = "Galaxy's freebayes tool"
+freebayes_repository_long_description = "Long description of Galaxy's freebayes tool for test 0520"
+
+category_name = 'Test 0520 Circular Dependency Import Export'
+category_description = 'Test script 0520 for importing and exporting repositories with circular dependencies.'
+
+'''
+1) Upload a capsule with 2 repositories that define simple repository dependencies on each other, resulting in circular
+ dependencies to a tool shed.
+2) Make sure each repository contains an invalid repository dependency after the capsule has been uploaded (they should
+ be invalid because their toolshed and changeset_revision attributes could not be auto-populated).
+3) Make sure each repository's repository_metadata record has the downloadable column marked as False.
+'''
+
+
+class TestExportImportRepository( ShedTwillTestCase ):
+ '''Test exporting and importing repositories.'''
+
+ def test_0000_initiate_users( self ):
+ """Create necessary user accounts and login as an admin user."""
+ self.logout()
+ self.login( email=common.test_user_1_email, username=common.test_user_1_name )
+ test_user_1 = self.test_db_util.get_user( common.test_user_1_email )
+ assert test_user_1 is not None, 'Problem retrieving user with email %s from the database' % common.test_user_1_email
+ test_user_1_private_role = self.test_db_util.get_private_role( test_user_1 )
+ self.logout()
+ self.login( email=common.admin_email, username=common.admin_username )
+ admin_user = self.test_db_util.get_user( common.admin_email )
+ assert admin_user is not None, 'Problem retrieving user with email %s from the database' % common.admin_email
+ admin_user_private_role = self.test_db_util.get_private_role( admin_user )
+
+ def test_0005_import_repository_capsule( self ):
+ """Import the filter_0520 repository capsule with dependencies."""
+ '''
+ This is step 1 - Upload a capsule with 2 repositories that define simple repository dependencies on each other, resulting in
+ circular dependencies to a tool shed.
+ '''
+ self.logout()
+ self.login( email=common.admin_email, username=common.admin_username )
+ self.create_category( name=category_name, description=category_description )
+ self.logout()
+ self.login( email=common.test_user_1_email, username=common.test_user_1_name )
+ self.import_capsule( self.get_filename( 'repository_capsules/0520_filtering.tar.gz' ),
+ strings_displayed=[ filtering_repository_name, freebayes_repository_name, '<b>Exists' ],
+ strings_not_displayed=[ ' Exists' ],
+ strings_displayed_after_submit=[ 'Repository <b>filtering_0520</b> has been created.' ],
+ strings_not_displayed_after_submit=[ 'Import not necessary' ] )
+
+ def test_0010_verify_invalid_dependency( self ):
+ '''Verify that the repository dependencies are marked as invalid.'''
+ '''
+ This is step 2 - Make sure each repository contains an invalid repository dependency after the capsule has been uploaded
+ (they should be invalid because their toolshed and changeset_revision attributes could not be auto-populated).
+ '''
+ freebayes_repository = self.test_db_util.get_repository_by_name_and_owner( freebayes_repository_name, common.test_user_1_name )
+ filtering_repository = self.test_db_util.get_repository_by_name_and_owner( filtering_repository_name, common.test_user_1_name )
+ strings_displayed = [ 'Ignoring repository dependency definition', self.url, 'filtering_0520', 'name is invalid' ]
+ self.display_manage_repository_page( freebayes_repository,
+ strings_displayed=strings_displayed,
+ strings_not_displayed=[ 'Repository dependencies' ] )
+ self.display_manage_repository_page( filtering_repository,
+ strings_displayed=[ 'Repository dependencies', self.get_repository_tip( freebayes_repository ) ],
+ strings_not_displayed=[] )
+
+ def test_0015_verify_repository_metadata( self ):
+ '''Verify that the repositories are not marked as downloadable.'''
+ '''
+ This is step 3 - Make sure each repository's repository_metadata record has the downloadable column marked as False.
+ '''
+ freebayes_repository = self.test_db_util.get_repository_by_name_and_owner( freebayes_repository_name, common.test_user_1_name )
+ filtering_repository = self.test_db_util.get_repository_by_name_and_owner( filtering_repository_name, common.test_user_1_name )
+ freebayes_metadata = self.get_repository_metadata_by_changeset_revision( freebayes_repository, self.get_repository_tip( freebayes_repository ) )
+ filtering_metadata = self.get_repository_metadata_by_changeset_revision( filtering_repository, self.get_repository_tip( filtering_repository ) )
+ assert not filtering_metadata.downloadable, 'Repository filtering_0520 is incorrectly marked as downloadable.'
+ assert not freebayes_metadata.downloadable, 'Repository freebayes_0520 is incorrectly marked as downloadable.'
diff -r 1d57caac677cbbd8465c149b6a091684b21af1f8 -r 5c6c916b0b9862cf572af3a70e87a1f20546e6fe test/tool_shed/test_data/repository_capsules/0520_filtering.tar.gz
Binary file test/tool_shed/test_data/repository_capsules/0520_filtering.tar.gz has changed
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: Improved logging in the tool shed's install and test framework.
by commits-noreply@bitbucket.org 05 Jan '14
by commits-noreply@bitbucket.org 05 Jan '14
05 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/1d57caac677c/
Changeset: 1d57caac677c
User: greg
Date: 2014-01-05 03:25:53
Summary: Improved logging in the tool shed's install and test framework.
Affected #: 3 files
diff -r cd8ca90e93a1f896ee6ff3ac56665e64373ace00 -r 1d57caac677cbbd8465c149b6a091684b21af1f8 test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -547,13 +547,18 @@
tool_test_results_dict[ 'installation_errors' ][ 'repository_dependencies' ]\
.append( missing_repository_dependency_info_dict )
# Record the status of this repository in the tool shed.
- # TODO: do something useful with response_dict
+ log.debug('=============================================================' )
+ log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( str( repository.changeset_revision ), str( repository.name ), str( repository.owner ), str( tool_test_results_dict ) ) )
response_dict = register_test_result( galaxy_tool_shed_url,
tool_test_results_dicts,
tool_test_results_dict,
repository_dict,
params,
can_update_tool_shed )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( str( repository.changeset_revision ), str( repository.name ), str( repository.owner ), str( response_dict ) ) )
+ log.debug('=============================================================' )
def initialize_install_and_test_statistics_dict( test_framework ):
# Initialize a dictionary for the summary that will be printed to stdout.
@@ -818,6 +823,7 @@
if can_update_tool_shed:
metadata_revision_id = repository_dict.get( 'id', None )
if metadata_revision_id is not None:
+ log.debug( 'Updating tool_test_results for repository_metadata id %s.' % str( metadata_revision_id ) )
tool_test_results_dicts.insert( 0, tool_test_results_dict )
params[ 'tool_test_results' ] = tool_test_results_dicts
# Set the time_last_tested entry so that the repository_metadata.time_last_tested will be set in the tool shed.
@@ -826,7 +832,8 @@
try:
return update( tool_shed_api_key, url, params, return_formatted=False )
except Exception, e:
- log.exception( 'Error attempting to register test results: %s' % str( e ) )
+ log.exception( 'Error updating tool_test_results for repository_metadata id %s:\n%s' % \
+ ( str( metadata_revision_id ), str( e ) ) )
return {}
else:
return {}
diff -r cd8ca90e93a1f896ee6ff3ac56665e64373ace00 -r 1d57caac677cbbd8465c149b6a091684b21af1f8 test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -189,18 +189,23 @@
changeset_revision,
encoded_repository_metadata_id )
if is_excluded:
+ log.debug( "Not testing revision %s of repository %s owned by %s because it is in the exclude list for this test run." % \
+ ( changeset_revision, name, owner ) )
# If this repository is being skipped, register the reason.
tool_test_results_dict[ 'not_tested' ] = dict( reason=reason )
params = dict( do_not_test=False )
- # TODO: do something useful with response_dict
+ log.debug('=============================================================' )
+ log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
tool_test_results_dicts,
tool_test_results_dict,
repository_dict,
params,
can_update_tool_shed )
- log.debug( "Not testing revision %s of repository %s owned by %s because it is in the exclude list for this test run." % \
- ( changeset_revision, name, owner ) )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('=============================================================' )
else:
# See if the repository was installed in a previous test.
repository = install_and_test_base_util.get_repository( name, owner, changeset_revision )
@@ -215,13 +220,18 @@
install_and_test_statistics_dict[ 'repositories_with_installation_error' ].append( repository_identifier_dict )
tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = error_message
params = dict( test_install_error=True )
- # TODO: do something useful with response_dict
+ log.debug('=============================================================' )
+ log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
tool_test_results_dicts,
tool_test_results_dict,
repository_dict,
params,
can_update_tool_shed )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('=============================================================' )
else:
# The repository was successfully installed.
log.debug( 'Installation succeeded for revision %s of repository %s owned by %s.' % \
@@ -277,7 +287,9 @@
install_and_test_statistics_dict[ 'at_least_one_test_failed' ].append( repository_identifier_dict )
# Record the status of this repository in the tool shed.
params[ 'tools_functionally_correct' ] = False
- # TODO: do something useful with response_dict
+ log.debug('=============================================================' )
+ log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
response_dict = \
install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
tool_test_results_dicts,
@@ -285,6 +297,9 @@
repository_dict,
params,
can_update_tool_shed )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('=============================================================' )
else:
log.debug( 'Skipped attempt to install revision %s of repository %s owned by %s because ' % \
( changeset_revision, name, owner ) )
@@ -627,6 +642,8 @@
if result.wasSuccessful():
# This repository's tools passed all functional tests. Use the ReportResults nose plugin to get a list
# of tests that passed.
+ log.debug( 'Revision %s of repository %s owned by %s installed and passed functional tests.' % \
+ ( changeset_revision, name, owner ) )
for plugin in test_plugins:
if hasattr( plugin, 'getTestStatus' ):
test_identifier = '%s/%s' % ( owner, name )
@@ -647,17 +664,22 @@
# Call the register_test_result() method to execute a PUT request to the repository_revisions API
# controller with the status of the test. This also sets the do_not_test and tools_functionally
# correct flags and updates the time_last_tested field to today's date.
- # TODO: do something useful with response_dict
+ log.debug('=============================================================' )
+ log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
tool_test_results_dicts,
tool_test_results_dict,
repository_dict,
params,
can_update_tool_shed )
- log.debug( 'Revision %s of repository %s owned by %s installed and passed functional tests.' % \
- ( changeset_revision, name, owner ) )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('=============================================================' )
else:
# The get_failed_test_dicts() method returns a list.
+ log.debug( 'Revision %s of repository %s owned by %s installed successfully but did not pass functional tests.' % \
+ ( changeset_revision, name, owner ) )
failed_test_dicts = get_failed_test_dicts( result, from_tool_test=True )
tool_test_results_dict[ 'failed_tests' ] = failed_test_dicts
failed_repository_dict = repository_identifier_dict
@@ -668,15 +690,18 @@
params = dict( tools_functionally_correct=False,
test_install_error=False,
do_not_test=str( set_do_not_test ) )
- # TODO: do something useful with response_dict
+ log.debug('=============================================================' )
+ log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
tool_test_results_dicts,
tool_test_results_dict,
repository_dict,
params,
can_update_tool_shed )
- log.debug( 'Revision %s of repository %s owned by %s installed successfully but did not pass functional tests.' % \
- ( changeset_revision, name, owner ) )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('=============================================================' )
# Remove the just-executed tests so twill will not find and re-test them along with the tools
# contained in the next repository.
remove_tests( app )
diff -r cd8ca90e93a1f896ee6ff3ac56665e64373ace00 -r 1d57caac677cbbd8465c149b6a091684b21af1f8 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -124,17 +124,22 @@
encoded_repository_metadata_id )
if is_excluded:
# If this repository is being skipped, register the reason.
+ log.debug( "Not testing revision %s of repository %s owned by %s because it is in the exclude list for this test run." % \
+ ( changeset_revision, name, owner ) )
tool_test_results_dict[ 'not_tested' ] = dict( reason=reason )
params = dict( do_not_test=False )
- # TODO: do something useful with response_dict
+ log.debug('=============================================================' )
+ log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
tool_test_results_dicts,
tool_test_results_dict,
repository_dict,
params,
can_update_tool_shed )
- log.debug( "Not testing revision %s of repository %s owned by %s because it is in the exclude list for this test run." % \
- ( changeset_revision, name, owner ) )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('=============================================================' )
else:
# See if the repository was installed in a previous test.
repository = install_and_test_base_util.get_repository( name, owner, changeset_revision )
@@ -149,13 +154,18 @@
install_and_test_statistics_dict[ 'repositories_with_installation_error' ].append( repository_identifier_dict )
tool_test_results_dict[ 'installation_errors' ][ 'current_repository' ] = error_message
params = dict( test_install_error=True )
- # TODO: do something useful with response_dict
+ log.debug('=============================================================' )
+ log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
tool_test_results_dicts,
tool_test_results_dict,
repository_dict,
params,
can_update_tool_shed )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('=============================================================' )
else:
# The repository was successfully installed.
log.debug( 'Installation succeeded for revision %s of repository %s owned by %s.' % \
@@ -166,13 +176,18 @@
repository_identifier_dict,
install_and_test_statistics_dict,
tool_test_results_dict )
- # TODO: do something useful with response_dict
+ log.debug('=============================================================' )
+ log.debug( 'Inserting the following into tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( tool_test_results_dict ) ) )
response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
tool_test_results_dicts,
tool_test_results_dict,
repository_dict,
params,
can_update_tool_shed )
+ log.debug( 'Result of inserting tool_test_results for revision %s of repository %s owned by %s:\n%s' % \
+ ( changeset_revision, name, owner, str( response_dict ) ) )
+ log.debug('=============================================================' )
else:
log.debug( 'Skipped attempt to install revision %s of repository %s owned by %s because ' % \
( changeset_revision, name, owner ) )
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: Set a repository and its dependent repositories created from a capsule to be defined as installable only if its creation resulted in no errors.
by commits-noreply@bitbucket.org 05 Jan '14
by commits-noreply@bitbucket.org 05 Jan '14
05 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/cd8ca90e93a1/
Changeset: cd8ca90e93a1
User: greg
Date: 2014-01-05 01:25:43
Summary: Set a repository and its dependent repositories created from a capsule to be defined as installable only if its creation resulted in no errors.
Affected #: 7 files
diff -r f11a729d42cb66753b8b1616ad625c1b2a306b3d -r cd8ca90e93a1f896ee6ff3ac56665e64373ace00 lib/galaxy/webapps/tool_shed/api/repositories.py
--- a/lib/galaxy/webapps/tool_shed/api/repositories.py
+++ b/lib/galaxy/webapps/tool_shed/api/repositories.py
@@ -219,11 +219,12 @@
import_results_tups = repository_maintenance_util.create_repository_and_import_archive( trans,
repository_status_info_dict,
import_results_tups )
+ import_util.check_status_and_reset_downloadable( trans, import_results_tups )
suc.remove_dir( file_path )
# NOTE: the order of installation is defined in import_results_tups, but order will be lost when transferred to return_dict.
return_dict = {}
for import_results_tup in import_results_tups:
- name_owner, message = import_results_tup
+ ok, name_owner, message = import_results_tup
name, owner = name_owner
key = 'Archive of repository "%s" owned by "%s"' % ( str( name ), str( owner ) )
val = message.replace( '<b>', '"' ).replace( '</b>', '"' )
diff -r f11a729d42cb66753b8b1616ad625c1b2a306b3d -r cd8ca90e93a1f896ee6ff3ac56665e64373ace00 lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -1883,9 +1883,11 @@
# Add the capsule_file_name and encoded_file_path to the repository_status_info_dict.
repository_status_info_dict[ 'capsule_file_name' ] = capsule_file_name
repository_status_info_dict[ 'encoded_file_path' ] = encoded_file_path
- import_results_tups = repository_maintenance_util.create_repository_and_import_archive( trans,
- repository_status_info_dict,
- import_results_tups )
+ import_results_tups = \
+ repository_maintenance_util.create_repository_and_import_archive( trans,
+ repository_status_info_dict,
+ import_results_tups )
+ import_util.check_status_and_reset_downloadable( trans, import_results_tups )
suc.remove_dir( file_path )
return trans.fill_template( '/webapps/tool_shed/repository/import_capsule_results.mako',
export_info_dict=export_info_dict,
diff -r f11a729d42cb66753b8b1616ad625c1b2a306b3d -r cd8ca90e93a1f896ee6ff3ac56665e64373ace00 lib/tool_shed/util/import_util.py
--- a/lib/tool_shed/util/import_util.py
+++ b/lib/tool_shed/util/import_util.py
@@ -21,6 +21,37 @@
log = logging.getLogger( __name__ )
+def check_status_and_reset_downloadable( trans, import_results_tups ):
+ """Check the status of each imported repository and set downloadable to False if errors."""
+ flush = False
+ for import_results_tup in import_results_tups:
+ ok, name_owner, message = import_results_tup
+ name, owner = name_owner
+ if not ok:
+ repository = suc.get_repository_by_name_and_owner( trans.app, name, owner )
+ # Do not allow the repository to be automatically installed if population resulted in errors.
+ tip_changeset_revision = repository.tip( trans.app )
+ repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans,
+ trans.security.encode_id( repository.id ),
+ tip_changeset_revision )
+ if repository_metadata:
+ if repository_metadata.downloadable:
+ repository_metadata.downloadable = False
+ trans.sa_session.add( repository_metadata )
+ if not flush:
+ flush = True
+ # Do not allow dependent repository revisions to be automatically installed if population
+ # resulted in errors.
+ dependent_downloadable_revisions = suc.get_dependent_downloadable_revisions( trans, repository_metadata )
+ for dependent_downloadable_revision in dependent_downloadable_revisions:
+ if dependent_downloadable_revision.downloadable:
+ dependent_downloadable_revision.downloadable = False
+ trans.sa_session.add( dependent_downloadable_revision )
+ if not flush:
+ flush = True
+ if flush:
+ trans.sa_session.flush()
+
def extract_capsule_files( trans, **kwd ):
"""Extract the uploaded capsule archive into a temporary location for inspection, validation and potential import."""
return_dict = {}
@@ -243,13 +274,16 @@
commit_message,
undesirable_dirs_removed,
undesirable_files_removed )
+ if error_message:
+ results_dict[ 'ok' ] = False
+ results_dict[ 'error_message' ] += error_message
try:
- metadata_util.set_repository_metadata_due_to_new_tip( trans, repository, content_alert_str=content_alert_str )
+ metadata_util.set_repository_metadata_due_to_new_tip( trans,
+ repository,
+ content_alert_str=content_alert_str )
except Exception, e:
log.debug( "Error setting metadata on repository %s created from imported archive %s: %s" % \
( str( repository.name ), str( archive_file_name ), str( e ) ) )
- results_dict[ 'ok' ] = ok
- results_dict[ 'error_message' ] += error_message
else:
archive.close()
results_dict[ 'ok' ] = False
diff -r f11a729d42cb66753b8b1616ad625c1b2a306b3d -r cd8ca90e93a1f896ee6ff3ac56665e64373ace00 lib/tool_shed/util/metadata_util.py
--- a/lib/tool_shed/util/metadata_util.py
+++ b/lib/tool_shed/util/metadata_util.py
@@ -296,14 +296,19 @@
includes_tool_dependencies = True
if 'workflows' in metadata_dict:
includes_workflows = True
- if has_repository_dependencies or has_repository_dependencies_only_if_compiling_contained_td or includes_datatypes or \
- includes_tools or includes_tool_dependencies or includes_workflows:
+ if has_repository_dependencies or \
+ has_repository_dependencies_only_if_compiling_contained_td or \
+ includes_datatypes or \
+ includes_tools or \
+ includes_tool_dependencies or \
+ includes_workflows:
downloadable = True
else:
downloadable = False
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, id, changeset_revision )
if repository_metadata:
- # A repository metadata record already exists with the received changeset_revision, so we don't need to check the skip_tool_test table.
+ # A repository metadata record already exists with the received changeset_revision, so we don't need to
+ # check the skip_tool_test table.
check_skip_tool_test = False
repository_metadata.metadata = metadata_dict
repository_metadata.downloadable = downloadable
@@ -313,7 +318,8 @@
repository_metadata.includes_tool_dependencies = includes_tool_dependencies
repository_metadata.includes_workflows = includes_workflows
else:
- # No repository_metadata record exists for the received changeset_revision, so we may need to update the skip_tool_test table.
+ # No repository_metadata record exists for the received changeset_revision, so we may need to update the
+ # skip_tool_test table.
check_skip_tool_test = True
repository_metadata = trans.model.RepositoryMetadata( repository_id=repository.id,
changeset_revision=changeset_revision,
@@ -324,7 +330,8 @@
includes_tools=includes_tools,
includes_tool_dependencies=includes_tool_dependencies,
includes_workflows=includes_workflows )
- # Always set the default values for the following columns. When resetting all metadata on a repository, this will reset the values.
+ # Always set the default values for the following columns. When resetting all metadata on a repository
+ # this will reset the values.
repository_metadata.tools_functionally_correct = False
repository_metadata.missing_test_components = False
repository_metadata.test_install_error = False
@@ -1738,7 +1745,8 @@
# NO_METADATA - no metadata for either ancestor or current, so continue from current
# EQUAL - ancestor metadata is equivalent to current metadata, so continue from current
# SUBSET - ancestor metadata is a subset of current metadata, so continue from current
- # NOT_EQUAL_AND_NOT_SUBSET - ancestor metadata is neither equal to nor a subset of current metadata, so persist ancestor metadata.
+ # NOT_EQUAL_AND_NOT_SUBSET - ancestor metadata is neither equal to nor a subset of current
+ # metadata, so persist ancestor metadata.
comparison = compare_changeset_revisions( trans,
ancestor_changeset_revision,
ancestor_metadata_dict,
@@ -1750,7 +1758,11 @@
elif comparison == NOT_EQUAL_AND_NOT_SUBSET:
metadata_changeset_revision = ancestor_changeset_revision
metadata_dict = ancestor_metadata_dict
- repository_metadata = create_or_update_repository_metadata( trans, id, repository, metadata_changeset_revision, metadata_dict )
+ repository_metadata = create_or_update_repository_metadata( trans,
+ id,
+ repository,
+ metadata_changeset_revision,
+ metadata_dict )
changeset_revisions.append( metadata_changeset_revision )
ancestor_changeset_revision = current_changeset_revision
ancestor_metadata_dict = current_metadata_dict
@@ -1762,7 +1774,11 @@
metadata_changeset_revision = current_changeset_revision
metadata_dict = current_metadata_dict
# We're at the end of the change log.
- repository_metadata = create_or_update_repository_metadata( trans, id, repository, metadata_changeset_revision, metadata_dict )
+ repository_metadata = create_or_update_repository_metadata( trans,
+ id,
+ repository,
+ metadata_changeset_revision,
+ metadata_dict )
changeset_revisions.append( metadata_changeset_revision )
ancestor_changeset_revision = None
ancestor_metadata_dict = None
@@ -1770,14 +1786,20 @@
# We reach here only if current_metadata_dict is empty and ancestor_metadata_dict is not.
if not ctx.children():
# We're at the end of the change log.
- repository_metadata = create_or_update_repository_metadata( trans, id, repository, metadata_changeset_revision, metadata_dict )
+ repository_metadata = create_or_update_repository_metadata( trans,
+ id,
+ repository,
+ metadata_changeset_revision,
+ metadata_dict )
changeset_revisions.append( metadata_changeset_revision )
ancestor_changeset_revision = None
ancestor_metadata_dict = None
suc.remove_dir( work_dir )
- # Delete all repository_metadata records for this repository that do not have a changeset_revision value in changeset_revisions.
+ # Delete all repository_metadata records for this repository that do not have a changeset_revision
+ # value in changeset_revisions.
clean_repository_metadata( trans, id, changeset_revisions )
- # Set tool version information for all downloadable changeset revisions. Get the list of changeset revisions from the changelog.
+ # Set tool version information for all downloadable changeset revisions. Get the list of changeset
+ # revisions from the changelog.
reset_all_tool_versions( trans, id, repo )
# Reset the tool_data_tables by loading the empty tool_data_table_conf.xml file.
tool_util.reset_tool_data_tables( trans.app )
@@ -1785,8 +1807,8 @@
def reset_metadata_on_selected_repositories( trans, **kwd ):
"""
- Inspect the repository changelog to reset metadata for all appropriate changeset revisions. This method is called from both Galaxy and the
- Tool Shed.
+ Inspect the repository changelog to reset metadata for all appropriate changeset revisions.
+ This method is called from both Galaxy and the Tool Shed.
"""
repository_ids = util.listify( kwd.get( 'repository_ids', None ) )
message = ''
@@ -1879,7 +1901,11 @@
tip_only = isinstance( repository_type_class, TipOnly )
if not tip_only and new_metadata_required_for_utilities( trans, repository, metadata_dict ):
# Create a new repository_metadata table row.
- repository_metadata = create_or_update_repository_metadata( trans, encoded_id, repository, repository.tip( trans.app ), metadata_dict )
+ repository_metadata = create_or_update_repository_metadata( trans,
+ encoded_id,
+ repository,
+ repository.tip( trans.app ),
+ metadata_dict )
# If this is the first record stored for this repository, see if we need to send any email alerts.
if len( repository.downloadable_revisions ) == 1:
suc.handle_email_alerts( trans, repository, content_alert_str='', new_repo_alert=True, admin_only=False )
@@ -1896,8 +1922,8 @@
repository_metadata.includes_datatypes = True
else:
repository_metadata.includes_datatypes = False
- # We don't store information about the special type of repository dependency that is needed only for compiling a tool dependency
- # defined for the dependent repository.
+ # We don't store information about the special type of repository dependency that is needed only for
+ # compiling a tool dependency defined for the dependent repository.
repository_dependencies_dict = metadata_dict.get( 'repository_dependencies', {} )
repository_dependencies = repository_dependencies_dict.get( 'repository_dependencies', [] )
has_repository_dependencies, has_repository_dependencies_only_if_compiling_contained_td = \
@@ -1924,9 +1950,14 @@
trans.sa_session.flush()
else:
# There are no metadata records associated with the repository.
- repository_metadata = create_or_update_repository_metadata( trans, encoded_id, repository, repository.tip( trans.app ), metadata_dict )
+ repository_metadata = create_or_update_repository_metadata( trans,
+ encoded_id,
+ repository,
+ repository.tip( trans.app ),
+ metadata_dict )
if 'tools' in metadata_dict and repository_metadata and status != 'error':
- # Set tool versions on the new downloadable change set. The order of the list of changesets is critical, so we use the repo's changelog.
+ # Set tool versions on the new downloadable change set. The order of the list of changesets is
+ # critical, so we use the repo's changelog.
changeset_revisions = []
for changeset in repo.changelog:
changeset_revision = str( repo.changectx( changeset ) )
@@ -1945,8 +1976,12 @@
return message, status
def set_repository_metadata_due_to_new_tip( trans, repository, content_alert_str=None, **kwd ):
- """Set metadata on the repository tip in the tool shed - this method is not called from Galaxy."""
- error_message, status = set_repository_metadata( trans, repository, content_alert_str=content_alert_str, **kwd )
+ """Set metadata on the repository tip in the tool shed."""
+ # This method is not called from Galaxy.
+ error_message, status = set_repository_metadata( trans,
+ repository,
+ content_alert_str=content_alert_str,
+ **kwd )
if error_message:
# FIXME: This probably should not redirect since this method is called from the upload controller as well as the repository controller.
# If there is an error, display it.
@@ -1994,8 +2029,12 @@
break
if new_dependency_name and new_dependency_type and new_dependency_version:
# Update all attributes of the tool_dependency record in the database.
- log.debug( "Updating tool dependency '%s' with type '%s' and version '%s' to have new type '%s' and version '%s'." % \
- ( str( tool_dependency.name ), str( tool_dependency.type ), str( tool_dependency.version ), str( new_dependency_type ), str( new_dependency_version ) ) )
+ log.debug( "Updating version %s of tool dependency %s %s to have new version %s and type %s." % \
+ ( str( tool_dependency.version ),
+ str( tool_dependency.type ),
+ str( tool_dependency.name ),
+ str( new_dependency_version ),
+ str( new_dependency_type ) ) )
tool_dependency.type = new_dependency_type
tool_dependency.version = new_dependency_version
tool_dependency.status = app.install_model.ToolDependency.installation_status.UNINSTALLED
@@ -2004,9 +2043,10 @@
context.flush()
new_tool_dependency = tool_dependency
else:
- # We have no new tool dependency definition based on a matching dependency name, so remove the existing tool dependency record from the database.
- log.debug( "Deleting tool dependency with name '%s', type '%s' and version '%s' from the database since it is no longer defined." % \
- ( str( tool_dependency.name ), str( tool_dependency.type ), str( tool_dependency.version ) ) )
+ # We have no new tool dependency definition based on a matching dependency name, so remove
+ # the existing tool dependency record from the database.
+ log.debug( "Deleting version %s of tool dependency %s %s from the database since it is no longer defined." % \
+ ( str( tool_dependency.version ), str( tool_dependency.type ), str( tool_dependency.name ) ) )
context.delete( tool_dependency )
context.flush()
return new_tool_dependency
diff -r f11a729d42cb66753b8b1616ad625c1b2a306b3d -r cd8ca90e93a1f896ee6ff3ac56665e64373ace00 lib/tool_shed/util/repository_maintenance_util.py
--- a/lib/tool_shed/util/repository_maintenance_util.py
+++ b/lib/tool_shed/util/repository_maintenance_util.py
@@ -102,15 +102,17 @@
def create_repository_and_import_archive( trans, repository_archive_dict, import_results_tups ):
"""
- Create a new repository in the tool shed and populate it with the contents of a gzip compressed tar archive that was exported
- as part or all of the contents of a capsule.
+ Create a new repository in the tool shed and populate it with the contents of a gzip compressed tar archive
+ that was exported as part or all of the contents of a capsule.
"""
results_message = ''
name = repository_archive_dict.get( 'name', None )
username = repository_archive_dict.get( 'owner', None )
if name is None or username is None:
- results_message += 'Import failed: required repository name <b>%s</b> or owner <b>%s</b> is missing.' % ( str( name ), str( username ))
- import_results_tups.append( ( ( str( name ), str( username ) ), results_message ) )
+ ok = False
+ results_message += 'Import failed: required repository name <b>%s</b> or owner <b>%s</b> is missing.' % \
+ ( str( name ), str( username ))
+ import_results_tups.append( ( ok, ( str( name ), str( username ) ), results_message ) )
else:
if repository_archive_dict[ 'status' ] is None:
# The repository does not yet exist in this Tool Shed and the current user is authorized to import
@@ -122,8 +124,9 @@
# the exported repository archive.
user = suc.get_user_by_username( trans.app, username )
if user is None:
+ ok = False
results_message += 'Import failed: repository owner <b>%s</b> does not have an account in this Tool Shed.' % str( username )
- import_results_tups.append( ( ( str( name ), str( username ) ), results_message ) )
+ import_results_tups.append( ( ok, ( str( name ), str( username ) ), results_message ) )
else:
user_id = user.id
# The categories entry in the repository_archive_dict is a list of category names. If a name does not
@@ -134,8 +137,8 @@
for category_name in category_names:
category = suc.get_category_by_name( trans, category_name )
if category is None:
- results_message += 'This Tool Shed does not have the category <b>%s</b> so it will not be associated with this repository.' % \
- str( category_name )
+ results_message += 'This Tool Shed does not have the category <b>%s</b> so it ' % str( category_name )
+ results_message += 'will not be associated with this repository.'
else:
category_ids.append( trans.security.encode_id( category.id ) )
# Create the repository record in the database.
@@ -150,11 +153,14 @@
results_message += create_message
# Populate the new repository with the contents of exported repository archive.
results_dict = import_util.import_repository_archive( trans, repository, repository_archive_dict )
- import_results_tups.append( ( ( str( name ), str( username ) ), results_message ) )
+ ok = results_dict.get( 'ok', False )
+ import_results_tups.append( ( ok, ( str( name ), str( username ) ), results_message ) )
else:
# The repository either already exists in this Tool Shed or the current user is not authorized to create it.
- results_message += 'Import not necessary: repository status for this Tool Shed is: %s.' % str( repository_archive_dict[ 'status' ] )
- import_results_tups.append( ( ( str( name ), str( username ) ), results_message ) )
+ ok = True
+ results_message += 'Import not necessary: repository status for this Tool Shed is: %s.' % \
+ str( repository_archive_dict[ 'status' ] )
+ import_results_tups.append( ( ok, ( str( name ), str( username ) ), results_message ) )
return import_results_tups
def validate_repository_name( app, name, user ):
diff -r f11a729d42cb66753b8b1616ad625c1b2a306b3d -r cd8ca90e93a1f896ee6ff3ac56665e64373ace00 lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -559,12 +559,65 @@
return repository_metadata
return None
+def get_dependent_downloadable_revisions( trans, repository_metadata ):
+ """
+ Return all repository_metadata records that are downloadable and that depend upon the received
+ repository_metadata record.
+ """
+ # This method is called only from the tool shed.
+ rm_changeset_revision = repository_metadata.changeset_revision
+ rm_repository = repository_metadata.repository
+ rm_repository_name = str( rm_repository.name )
+ rm_repository_owner = str( rm_repository.user.username )
+ dependent_downloadable_revisions = []
+ for repository in trans.sa_session.query( trans.model.Repository ) \
+ .filter( and_( trans.model.Repository.table.c.id != rm_repository.id,
+ trans.model.Repository.table.c.deleted == False,
+ trans.model.Repository.table.c.deprecated == False ) ):
+ downloadable_revisions = repository.downloadable_revisions
+ if downloadable_revisions:
+ for downloadable_revision in downloadable_revisions:
+ if downloadable_revision.has_repository_dependencies:
+ metadata = downloadable_revision.metadata
+ if metadata:
+ repository_dependencies_dict = metadata.get( 'repository_dependencies', {} )
+ repository_dependencies_tups = repository_dependencies_dict.get( 'repository_dependencies', [] )
+ for repository_dependencies_tup in repository_dependencies_tups:
+ tool_shed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
+ common_util.parse_repository_dependency_tuple( repository_dependencies_tup )
+ if name == rm_repository_name and owner == rm_repository_owner:
+ # We've discovered a repository revision that depends upon the repository associated
+ # with the received repository_metadata record, but we need to make sure it depends
+ # upon the revision.
+ if changeset_revision == rm_changeset_revision:
+ dependent_downloadable_revisions.append( downloadable_revision )
+ else:
+ # Make sure the defined changeset_revision is current.
+ defined_repository_metadata = \
+ trans.sa_session.query( trans.model.RepositoryMetadata ) \
+ .filter( trans.model.RepositoryMetadata.table.c.changeset_revision == changeset_revision ) \
+ .first()
+ if defined_repository_metadata is None:
+ # The defined changeset_revision is not associated with a repository_metadata
+ # record, so updates must be necessary.
+ defined_repository = get_repository_by_name_and_owner( trans.app, name, owner )
+ defined_repo_dir = defined_repository.repo_path( trans.app )
+ defined_repo = hg.repository( get_configured_ui(), defined_repo_dir )
+ updated_changeset_revision = \
+ get_next_downloadable_changeset_revision( defined_repository,
+ defined_repo,
+ changeset_revision )
+ if updated_changeset_revision == rm_changeset_revision:
+ dependent_downloadable_revisions.append( downloadable_revision )
+ return dependent_downloadable_revisions
+
def get_file_context_from_ctx( ctx, filename ):
"""Return the mercurial file context for a specified file."""
- # We have to be careful in determining if we found the correct file because multiple files with the same name may be in different directories
- # within ctx if the files were moved within the change set. For example, in the following ctx.files() list, the former may have been moved to
- # the latter: ['tmap_wrapper_0.0.19/tool_data_table_conf.xml.sample', 'tmap_wrapper_0.3.3/tool_data_table_conf.xml.sample']. Another scenario
- # is that the file has been deleted.
+ # We have to be careful in determining if we found the correct file because multiple files with
+ # the same name may be in different directories within ctx if the files were moved within the change
+ # set. For example, in the following ctx.files() list, the former may have been moved to the latter:
+ # ['tmap_wrapper_0.0.19/tool_data_table_conf.xml.sample', 'tmap_wrapper_0.3.3/tool_data_table_conf.xml.sample'].
+ # Another scenario is that the file has been deleted.
deleted = False
filename = strip_path( filename )
for ctx_file in ctx.files():
diff -r f11a729d42cb66753b8b1616ad625c1b2a306b3d -r cd8ca90e93a1f896ee6ff3ac56665e64373ace00 templates/webapps/tool_shed/repository/import_capsule_results.mako
--- a/templates/webapps/tool_shed/repository/import_capsule_results.mako
+++ b/templates/webapps/tool_shed/repository/import_capsule_results.mako
@@ -70,7 +70,7 @@
<table class="grid">
%for import_results_tup in import_results_tups:
<%
- name_owner_tup, results_message = import_results_tup
+ ok, name_owner_tup, results_message = import_results_tup
name, owner = name_owner_tup
%><tr><td>Archive of repository <b>${name}</b> owned by <b>${owner}</b><br/>${results_message}</td></tr>
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: martenson: fix for bug with hid starting at 2 instead of 1, introduced by myself at 2bb7df0
by commits-noreply@bitbucket.org 03 Jan '14
by commits-noreply@bitbucket.org 03 Jan '14
03 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/f11a729d42cb/
Changeset: f11a729d42cb
User: martenson
Date: 2014-01-03 22:01:54
Summary: fix for bug with hid starting at 2 instead of 1, introduced by myself at 2bb7df0
Affected #: 1 file
diff -r 720ad6d7b18324740eaf8727e2ed41ee24c68b45 -r f11a729d42cb66753b8b1616ad625c1b2a306b3d lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1852,16 +1852,21 @@
def db_next_hid( self ):
"""
- Override __next_hid to generate from the database in a concurrency
- safe way.
+ db_next_hid( self )
+
+ Override __next_hid to generate from the database in a concurrency safe way.
+ Loads the next history ID from the DB and returns it.
+ It also saves the future next_id into the DB.
+
+ :rtype: int
+ :returns: the next history id
"""
conn = object_session( self ).connection()
table = self.table
trans = conn.begin()
try:
- current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
- next_hid = current_hid + 1
- table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
+ next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
+ table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
trans.commit()
return next_hid
except:
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: martenson: library - stop event propagation if the folder row was clicked
by commits-noreply@bitbucket.org 03 Jan '14
by commits-noreply@bitbucket.org 03 Jan '14
03 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/720ad6d7b183/
Changeset: 720ad6d7b183
User: martenson
Date: 2014-01-03 20:41:59
Summary: library - stop event propagation if the folder row was clicked
Affected #: 2 files
diff -r c4a97053aca397ca783079a1e05c0cd2f921fb88 -r 720ad6d7b18324740eaf8727e2ed41ee24c68b45 static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -506,6 +506,7 @@
source = 'td';
}
if (checkbox === '') {event.stopPropagation(); return;} // button in row was clicked
+ if (checkbox === undefined) {event.stopPropagation(); return;} // folder row was clicked
if (checkbox.checked){
if (source==='td'){
@@ -578,7 +579,7 @@
// import all selected datasets into history
importAllIntoHistory : function (){
- //disable the button to rprevent multiple submission
+ //disable the button to prevent multiple submission
this.modal.disableButton('Import');
var history_id = $("select[name=dataset_import_bulk] option:selected").val();
diff -r c4a97053aca397ca783079a1e05c0cd2f921fb88 -r 720ad6d7b18324740eaf8727e2ed41ee24c68b45 static/scripts/packed/galaxy.library.js
--- a/static/scripts/packed/galaxy.library.js
+++ b/static/scripts/packed/galaxy.library.js
@@ -1,1 +1,1 @@
-var view=null;var library_router=null;var responses=[];define(["galaxy.modal","galaxy.masthead","utils/galaxy.utils","libs/toastr"],function(j,k,g,m){var e=Backbone.Model.extend({urlRoot:"/api/libraries"});var n=Backbone.Collection.extend({url:"/api/libraries",model:e});var h=Backbone.Model.extend({urlRoot:"/api/libraries/datasets"});var c=Backbone.Collection.extend({model:h});var d=Backbone.Model.extend({defaults:{folder:new c(),full_path:"unknown",urlRoot:"/api/folders/",id:"unknown"},parse:function(q){this.full_path=q[0].full_path;this.get("folder").reset(q[1].folder_contents);return q}});var b=Backbone.Model.extend({urlRoot:"/api/histories/"});var i=Backbone.Model.extend({url:"/api/histories/"});var o=Backbone.Collection.extend({url:"/api/histories",model:i});var p=Backbone.Router.extend({routes:{"":"libraries","folders/:id":"folder_content","folders/:folder_id/download/:format":"download"}});var l=Backbone.View.extend({el:"#center",progress:0,progressStep:1,lastSelectedHistory:"",modal:null,folders:null,initialize:function(){this.folders=[];this.queue=jQuery.Deferred();this.queue.resolve()},templateFolder:function(){var q=[];q.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');q.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');q.push('<div id="library_folder_toolbar" >');q.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-plus"></span><span class="fa fa-folder-close"></span> folder</button>');q.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-external-link"></span> to history</button>');q.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');q.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');q.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');q.push(" </button>");q.push(' <ul class="dropdown-menu" role="menu">');q.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');q.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');q.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');q.push(" </ul>");q.push(" </div>");q.push("</div>");q.push('<div class="library_breadcrumb">');q.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');q.push("<% _.each(path, function(path_item) { %>");q.push("<% if (path_item[0] != id) { %>");q.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');q.push("<% } else { %>");q.push('<span title="You are in this folder"><%- path_item[1] %></span>');q.push("<% } %>");q.push("<% }); %>");q.push("</div>");q.push('<table id="folder_table" class="table table-condensed">');q.push(" <thead>");q.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');q.push(' <th class="button_heading">view</th>');q.push(" <th>name</th>");q.push(" <th>data type</th>");q.push(" <th>size</th>");q.push(" <th>date</th>");q.push(" </thead>");q.push(" <tbody>");q.push(" <td></td>");q.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');q.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');q.push(" <td></td>");q.push(" <td></td>");q.push(" <td></td>");q.push(" <td></td>");q.push(" </tr>");q.push(" <% _.each(items, function(content_item) { %>");q.push(' <tr class="folder_row light" id="<%- content_item.id %>">');q.push(' <% if (content_item.get("type") === "folder") { %>');q.push(" <td></td>");q.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');q.push(' <span class="fa fa-folder-open"></span> browse</td>');q.push(' <td><%- content_item.get("name") %>');q.push(' <% if (content_item.get("item_count") === 0) { %>');q.push(' <span class="muted">(empty folder)</span>');q.push(" <% } %>");q.push(" </td>");q.push(" <td>folder</td>");q.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>');q.push(" <% } else { %>");q.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');q.push(" <td>");q.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');q.push(' <span class="fa fa-eye"></span> details');q.push(" </button>");q.push(" </td>");q.push(' <td><%- content_item.get("name") %></td>');q.push(' <td><%= _.escape(content_item.get("data_type")) %></td>');q.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>');q.push(" <% } %> ");q.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>');q.push(" </tr>");q.push(" <% }); %>");q.push(" ");q.push(" </tbody>");q.push("</table>");q.push("</div>");return q.join("")},templateDatasetModal:function(){var q=[];q.push('<div id="dataset_info_modal">');q.push(' <table class="table table-striped table-condensed">');q.push(" <tr>");q.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');q.push(' <td><%= _.escape(item.get("name")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Data type</th>');q.push(' <td><%= _.escape(item.get("data_type")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Genome build</th>');q.push(' <td><%= _.escape(item.get("genome_build")) %></td>');q.push(" </tr>");q.push(' <th scope="row">Size</th>');q.push(" <td><%= _.escape(size) %></td>");q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Date uploaded</th>');q.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Uploaded by</th>');q.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');q.push(" </tr>");q.push(' <tr scope="row">');q.push(' <th scope="row">Data Lines</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');q.push(" </tr>");q.push(' <th scope="row">Comment Lines</th>');q.push(' <% if (item.get("metadata_comment_lines") === "") { %>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');q.push(" <% } else { %>");q.push(' <td scope="row">unknown</td>');q.push(" <% } %>");q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Number of Columns</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Column Types</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Miscellaneous information</th>');q.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');q.push(" </tr>");q.push(" </table>");q.push(' <pre class="peek">');q.push(" </pre>");q.push("</div>");return q.join("")},templateHistorySelectInModal:function(){var q=[];q.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');q.push("Select history: ");q.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');q.push(" <% _.each(histories, function(history) { %>");q.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');q.push(" <% }); %>");q.push("</select>");q.push("</span>");return q.join("")},templateBulkImportInModal:function(){var q=[];q.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');q.push("Select history: ");q.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');q.push(" <% _.each(histories, function(history) { %>");q.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');q.push(" <% }); %>");q.push("</select>");q.push("</span>");return q.join("")},size_to_string:function(q){var r="";if(q>=100000000000){q=q/100000000000;r="TB"}else{if(q>=100000000){q=q/100000000;r="GB"}else{if(q>=100000){q=q/100000;r="MB"}else{if(q>=100){q=q/100;r="KB"}else{q=q*10;r="b"}}}}return(Math.round(q)/10)+r},events:{"click #select-all-checkboxes":"selectAll","click .folder_row":"selectClickedRow","click #toolbtn_bulk_import":"modalBulkImport","click #toolbtn_dl":"bulkDownload","click .library-dataset":"showDatasetDetails","click #toolbtn_create_folder":"createFolderModal","click .btn_open_folder":"navigateToFolder"},render:function(q){$("#center").css("overflow","auto");view=this;var s=this;var r=new d({id:q.id});r.url=r.attributes.urlRoot+q.id+"/contents";r.fetch({success:function(t){for(var v=0;v<r.attributes.folder.models.length;v++){var u=r.attributes.folder.models[v];if(u.get("type")==="file"){u.set("readable_size",s.size_to_string(u.get("file_size")))}}var x=r.full_path;var y;if(x.length===1){y=0}else{y=x[x.length-2][0]}var w=_.template(s.templateFolder(),{path:r.full_path,items:r.attributes.folder.models,id:q.id,upper_folder_id:y});s.$el.html(w)}})},navigateToFolder:function(r){var q=$(r.target).attr("data-id");if(typeof q==="undefined"){return false}else{if(q==="0"){library_router.navigate("#",{trigger:true,replace:true})}else{library_router.navigate("folders/"+q,{trigger:true,replace:true})}}},showDatasetDetails:function(t){t.preventDefault();var u=$(t.target).parent().parent().attr("id");var s=new h();var r=new o();s.id=u;var q=this;s.fetch({success:function(v){r.fetch({success:function(w){q.renderModalAfterFetch(v,w)}})}})},renderModalAfterFetch:function(v,s){var t=this.size_to_string(v.get("file_size"));var u=_.template(this.templateDatasetModal(),{item:v,size:t});this.modal=null;var r=this;this.modal=new j.GalaxyModal({title:"Dataset Details",body:u,buttons:{Import:function(){r.importCurrentIntoHistory()},Download:function(){r.downloadCurrent()},Close:function(){r.modal.hide();$(".modal").remove();r.modal=null}}});this.modal.bindEvents(event);$(".peek").html(v.get("peek"));var q=_.template(this.templateHistorySelectInModal(),{histories:s.models});$(this.modal.elMain).find(".buttons").prepend(q);if(r.lastSelectedHistory.length>0){$(this.modal.elMain).find("#dataset_import_single").val(r.lastSelectedHistory)}this.modal.show()},downloadCurrent:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var q=[];q.push($("#id_row").attr("data-id"));var r="/api/libraries/datasets/download/uncompressed";var s={ldda_ids:q};folderContentView.processDownload(r,s);this.modal.enableButton("Import");this.modal.enableButton("Download")},importCurrentIntoHistory:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var s=$(this.modal.elMain).find("select[name=dataset_import_single] option:selected").val();this.lastSelectedHistory=s;var q=$("#id_row").attr("data-id");var t=new b();var r=this;t.url=t.urlRoot+s+"/contents";t.save({content:q,source:"library"},{success:function(){m.success("Dataset imported");r.modal.enableButton("Import");r.modal.enableButton("Download")},error:function(){m.error("An error occured! Dataset not imported. Please try again.");r.modal.enableButton("Import");r.modal.enableButton("Download")}})},selectAll:function(r){var q=r.target.checked;that=this;$(":checkbox").each(function(){this.checked=q;$row=$(this.parentElement.parentElement);(q)?that.makeDarkRow($row):that.makeWhiteRow($row)});this.checkTools()},selectClickedRow:function(r){var t="";var q;var s;if(r.target.localName==="input"){t=r.target;q=$(r.target.parentElement.parentElement);s="input"}else{if(r.target.localName==="td"){t=$("#"+r.target.parentElement.id).find(":checkbox")[0];q=$(r.target.parentElement);s="td"}}if(t===""){r.stopPropagation();return}if(t.checked){if(s==="td"){t.checked="";this.makeWhiteRow(q)}else{if(s==="input"){this.makeDarkRow(q)}}}else{if(s==="td"){t.checked="selected";this.makeDarkRow(q)}else{if(s==="input"){this.makeWhiteRow(q)}}}this.checkTools()},makeDarkRow:function(q){q.removeClass("light");q.find("a").removeClass("light");q.addClass("dark");q.find("a").addClass("dark")},makeWhiteRow:function(q){q.removeClass("dark");q.find("a").removeClass("dark");q.addClass("light");q.find("a").addClass("light")},checkTools:function(){var q=$("#folder_table").find(":checked");if(q.length>0){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}else{$("#toolbtn_bulk_import").hide();$("#toolbtn_dl").hide()}},modalBulkImport:function(){var r=this;var q=new o();q.fetch({success:function(s){var t=_.template(r.templateBulkImportInModal(),{histories:s.models});r.modal=new j.GalaxyModal({title:"Import into History",body:t,buttons:{Import:function(){r.importAllIntoHistory()},Close:function(){r.modal.hide();$(".modal").remove();r.modal=null}}});r.modal.bindEvents(event);r.modal.show()}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var s=$("select[name=dataset_import_bulk] option:selected").val();var w=$("select[name=dataset_import_bulk] option:selected").text();var y=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){y.push(this.parentElement.parentElement.id)}});var x=_.template(this.templateProgressBar(),{history_name:w});$(this.modal.elMain).find(".modal-body").html(x);var t=100/y.length;this.initProgress(t);var q=[];for(var r=y.length-1;r>=0;r--){library_dataset_id=y[r];var u=new b();var v=this;u.url=u.urlRoot+s+"/contents";u.content=library_dataset_id;u.source="library";q.push(u)}this.chainCall(q)},chainCall:function(r){var q=this;var s=r.pop();if(typeof s==="undefined"){m.success("All datasets imported");this.modal.destroy();return}var t=$.when(s.save({content:s.content,source:s.source})).done(function(u){q.updateProgress();responses.push(u);q.chainCall(r)})},initProgress:function(q){this.progress=0;this.progressStep=q},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},templateProgressBar:function(){var q=[];q.push('<div class="import_text">');q.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");q.push("</div>");q.push('<div class="progress">');q.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');q.push(' <span class="completion_span">0% Complete</span>');q.push(" </div>");q.push("</div>");q.push("");return q.join("")},download:function(q,u){var s=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){s.push(this.parentElement.parentElement.id)}});var r="/api/libraries/datasets/download/"+u;var t={ldda_ids:s};this.processDownload(r,t,"get")},processDownload:function(r,s,t){if(r&&s){s=typeof s=="string"?s:$.param(s);var q="";$.each(s.split("&"),function(){var u=this.split("=");q+='<input type="hidden" name="'+u[0]+'" value="'+u[1]+'" />'});$('<form action="'+r+'" method="'+(t||"post")+'">'+q+"</form>").appendTo("body").submit().remove();m.info("Your download will begin soon")}},createFolderModal:function(){m.info("This will create folder...in the future")}});var a=Backbone.View.extend({el:"#center",events:{"click #create_new_library_btn":"show_library_modal"},initialize:function(){},template_library_list:function(){tmpl_array=[];tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');tmpl_array.push("");tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary file ">New Library</a>');tmpl_array.push('<table class="table table-condensed">');tmpl_array.push(" <thead>");tmpl_array.push(' <th class="button_heading"></th>');tmpl_array.push(" <th>name</th>");tmpl_array.push(" <th>description</th>");tmpl_array.push(" <th>synopsis</th> ");tmpl_array.push(" <th>model type</th> ");tmpl_array.push(" </thead>");tmpl_array.push(" <tbody>");tmpl_array.push(" <% _.each(libraries, function(library) { %>");tmpl_array.push(" <tr>");tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');tmpl_array.push(' <td><%- library.get("name") %></td>');tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');tmpl_array.push(" </tr>");tmpl_array.push(" <% }); %>");tmpl_array.push(" </tbody>");tmpl_array.push("</table>");tmpl_array.push("</div>");return tmpl_array.join("")},render:function(){$("#center").css("overflow","auto");var q=this;libraries=new n();libraries.fetch({success:function(r){var s=_.template(q.template_library_list(),{libraries:r.models});q.$el.html(s)},error:function(s,r){if(r.statusCode().status===403){m.error("Please log in first. Redirecting to login page in 3s.");setTimeout(q.redirectToLogin,3000)}else{m.error("An error occured. Please try again.")}}})},redirectToHome:function(){window.location="../"},redirectToLogin:function(){window.location="/user/login"},modal:null,show_library_modal:function(r){r.preventDefault();r.stopPropagation();var q=this;this.modal=new j.GalaxyModal({title:"Create New Library",body:this.template_new_library(),buttons:{Create:function(){q.create_new_library_event()},Close:function(){q.modal.hide()}}});this.modal.show()},create_new_library_event:function(){var s=this.serialize_new_library();if(this.validate_new_library(s)){var r=new e();var q=this;r.save(s,{success:function(t){q.modal.hide();q.clear_library_modal();q.render();m.success("Library created")},error:function(){m.error("An error occured :(")}})}else{m.error("Library's name is missing")}return false},clear_library_modal:function(){$("input[name='Name']").val("");$("input[name='Description']").val("");$("input[name='Synopsis']").val("")},serialize_new_library:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val(),synopsis:$("input[name='Synopsis']").val()}},validate_new_library:function(q){return q.name!==""},template_new_library:function(){tmpl_array=[];tmpl_array.push('<div id="new_library_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var f=Backbone.View.extend({folderContentView:null,galaxyLibraryview:null,initialize:function(){folderContentView=new l();galaxyLibraryview=new a();library_router=new p();library_router.on("route:libraries",function(){galaxyLibraryview.render()});library_router.on("route:folder_content",function(q){folderContentView.render({id:q})});library_router.on("route:download",function(q,r){if($("#center").find(":checked").length===0){library_router.navigate("folders/"+q,{trigger:true,replace:true})}else{folderContentView.download(q,r);library_router.navigate("folders/"+q,{trigger:false,replace:true})}});Backbone.history.start();return this}});return{GalaxyApp:f}});
\ No newline at end of file
+var view=null;var library_router=null;var responses=[];define(["galaxy.modal","galaxy.masthead","utils/galaxy.utils","libs/toastr"],function(j,k,g,m){var e=Backbone.Model.extend({urlRoot:"/api/libraries"});var n=Backbone.Collection.extend({url:"/api/libraries",model:e});var h=Backbone.Model.extend({urlRoot:"/api/libraries/datasets"});var c=Backbone.Collection.extend({model:h});var d=Backbone.Model.extend({defaults:{folder:new c(),full_path:"unknown",urlRoot:"/api/folders/",id:"unknown"},parse:function(q){this.full_path=q[0].full_path;this.get("folder").reset(q[1].folder_contents);return q}});var b=Backbone.Model.extend({urlRoot:"/api/histories/"});var i=Backbone.Model.extend({url:"/api/histories/"});var o=Backbone.Collection.extend({url:"/api/histories",model:i});var p=Backbone.Router.extend({routes:{"":"libraries","folders/:id":"folder_content","folders/:folder_id/download/:format":"download"}});var l=Backbone.View.extend({el:"#center",progress:0,progressStep:1,lastSelectedHistory:"",modal:null,folders:null,initialize:function(){this.folders=[];this.queue=jQuery.Deferred();this.queue.resolve()},templateFolder:function(){var q=[];q.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');q.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');q.push('<div id="library_folder_toolbar" >');q.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-plus"></span><span class="fa fa-folder-close"></span> folder</button>');q.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-external-link"></span> to history</button>');q.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');q.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');q.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');q.push(" </button>");q.push(' <ul class="dropdown-menu" role="menu">');q.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');q.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');q.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');q.push(" </ul>");q.push(" </div>");q.push("</div>");q.push('<div class="library_breadcrumb">');q.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');q.push("<% _.each(path, function(path_item) { %>");q.push("<% if (path_item[0] != id) { %>");q.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');q.push("<% } else { %>");q.push('<span title="You are in this folder"><%- path_item[1] %></span>');q.push("<% } %>");q.push("<% }); %>");q.push("</div>");q.push('<table id="folder_table" class="table table-condensed">');q.push(" <thead>");q.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');q.push(' <th class="button_heading">view</th>');q.push(" <th>name</th>");q.push(" <th>data type</th>");q.push(" <th>size</th>");q.push(" <th>date</th>");q.push(" </thead>");q.push(" <tbody>");q.push(" <td></td>");q.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');q.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');q.push(" <td></td>");q.push(" <td></td>");q.push(" <td></td>");q.push(" <td></td>");q.push(" </tr>");q.push(" <% _.each(items, function(content_item) { %>");q.push(' <tr class="folder_row light" id="<%- content_item.id %>">');q.push(' <% if (content_item.get("type") === "folder") { %>');q.push(" <td></td>");q.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');q.push(' <span class="fa fa-folder-open"></span> browse</td>');q.push(' <td><%- content_item.get("name") %>');q.push(' <% if (content_item.get("item_count") === 0) { %>');q.push(' <span class="muted">(empty folder)</span>');q.push(" <% } %>");q.push(" </td>");q.push(" <td>folder</td>");q.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>');q.push(" <% } else { %>");q.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');q.push(" <td>");q.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');q.push(' <span class="fa fa-eye"></span> details');q.push(" </button>");q.push(" </td>");q.push(' <td><%- content_item.get("name") %></td>');q.push(' <td><%= _.escape(content_item.get("data_type")) %></td>');q.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>');q.push(" <% } %> ");q.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>');q.push(" </tr>");q.push(" <% }); %>");q.push(" ");q.push(" </tbody>");q.push("</table>");q.push("</div>");return q.join("")},templateDatasetModal:function(){var q=[];q.push('<div id="dataset_info_modal">');q.push(' <table class="table table-striped table-condensed">');q.push(" <tr>");q.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');q.push(' <td><%= _.escape(item.get("name")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Data type</th>');q.push(' <td><%= _.escape(item.get("data_type")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Genome build</th>');q.push(' <td><%= _.escape(item.get("genome_build")) %></td>');q.push(" </tr>");q.push(' <th scope="row">Size</th>');q.push(" <td><%= _.escape(size) %></td>");q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Date uploaded</th>');q.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Uploaded by</th>');q.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');q.push(" </tr>");q.push(' <tr scope="row">');q.push(' <th scope="row">Data Lines</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');q.push(" </tr>");q.push(' <th scope="row">Comment Lines</th>');q.push(' <% if (item.get("metadata_comment_lines") === "") { %>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');q.push(" <% } else { %>");q.push(' <td scope="row">unknown</td>');q.push(" <% } %>");q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Number of Columns</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Column Types</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Miscellaneous information</th>');q.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');q.push(" </tr>");q.push(" </table>");q.push(' <pre class="peek">');q.push(" </pre>");q.push("</div>");return q.join("")},templateHistorySelectInModal:function(){var q=[];q.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');q.push("Select history: ");q.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');q.push(" <% _.each(histories, function(history) { %>");q.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');q.push(" <% }); %>");q.push("</select>");q.push("</span>");return q.join("")},templateBulkImportInModal:function(){var q=[];q.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');q.push("Select history: ");q.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');q.push(" <% _.each(histories, function(history) { %>");q.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');q.push(" <% }); %>");q.push("</select>");q.push("</span>");return q.join("")},size_to_string:function(q){var r="";if(q>=100000000000){q=q/100000000000;r="TB"}else{if(q>=100000000){q=q/100000000;r="GB"}else{if(q>=100000){q=q/100000;r="MB"}else{if(q>=100){q=q/100;r="KB"}else{q=q*10;r="b"}}}}return(Math.round(q)/10)+r},events:{"click #select-all-checkboxes":"selectAll","click .folder_row":"selectClickedRow","click #toolbtn_bulk_import":"modalBulkImport","click #toolbtn_dl":"bulkDownload","click .library-dataset":"showDatasetDetails","click #toolbtn_create_folder":"createFolderModal","click .btn_open_folder":"navigateToFolder"},render:function(q){$("#center").css("overflow","auto");view=this;var s=this;var r=new d({id:q.id});r.url=r.attributes.urlRoot+q.id+"/contents";r.fetch({success:function(t){for(var v=0;v<r.attributes.folder.models.length;v++){var u=r.attributes.folder.models[v];if(u.get("type")==="file"){u.set("readable_size",s.size_to_string(u.get("file_size")))}}var x=r.full_path;var y;if(x.length===1){y=0}else{y=x[x.length-2][0]}var w=_.template(s.templateFolder(),{path:r.full_path,items:r.attributes.folder.models,id:q.id,upper_folder_id:y});s.$el.html(w)}})},navigateToFolder:function(r){var q=$(r.target).attr("data-id");if(typeof q==="undefined"){return false}else{if(q==="0"){library_router.navigate("#",{trigger:true,replace:true})}else{library_router.navigate("folders/"+q,{trigger:true,replace:true})}}},showDatasetDetails:function(t){t.preventDefault();var u=$(t.target).parent().parent().attr("id");var s=new h();var r=new o();s.id=u;var q=this;s.fetch({success:function(v){r.fetch({success:function(w){q.renderModalAfterFetch(v,w)}})}})},renderModalAfterFetch:function(v,s){var t=this.size_to_string(v.get("file_size"));var u=_.template(this.templateDatasetModal(),{item:v,size:t});this.modal=null;var r=this;this.modal=new j.GalaxyModal({title:"Dataset Details",body:u,buttons:{Import:function(){r.importCurrentIntoHistory()},Download:function(){r.downloadCurrent()},Close:function(){r.modal.hide();$(".modal").remove();r.modal=null}}});this.modal.bindEvents(event);$(".peek").html(v.get("peek"));var q=_.template(this.templateHistorySelectInModal(),{histories:s.models});$(this.modal.elMain).find(".buttons").prepend(q);if(r.lastSelectedHistory.length>0){$(this.modal.elMain).find("#dataset_import_single").val(r.lastSelectedHistory)}this.modal.show()},downloadCurrent:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var q=[];q.push($("#id_row").attr("data-id"));var r="/api/libraries/datasets/download/uncompressed";var s={ldda_ids:q};folderContentView.processDownload(r,s);this.modal.enableButton("Import");this.modal.enableButton("Download")},importCurrentIntoHistory:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var s=$(this.modal.elMain).find("select[name=dataset_import_single] option:selected").val();this.lastSelectedHistory=s;var q=$("#id_row").attr("data-id");var t=new b();var r=this;t.url=t.urlRoot+s+"/contents";t.save({content:q,source:"library"},{success:function(){m.success("Dataset imported");r.modal.enableButton("Import");r.modal.enableButton("Download")},error:function(){m.error("An error occured! Dataset not imported. Please try again.");r.modal.enableButton("Import");r.modal.enableButton("Download")}})},selectAll:function(r){var q=r.target.checked;that=this;$(":checkbox").each(function(){this.checked=q;$row=$(this.parentElement.parentElement);(q)?that.makeDarkRow($row):that.makeWhiteRow($row)});this.checkTools()},selectClickedRow:function(r){var t="";var q;var s;if(r.target.localName==="input"){t=r.target;q=$(r.target.parentElement.parentElement);s="input"}else{if(r.target.localName==="td"){t=$("#"+r.target.parentElement.id).find(":checkbox")[0];q=$(r.target.parentElement);s="td"}}if(t===""){r.stopPropagation();return}if(t===undefined){r.stopPropagation();return}if(t.checked){if(s==="td"){t.checked="";this.makeWhiteRow(q)}else{if(s==="input"){this.makeDarkRow(q)}}}else{if(s==="td"){t.checked="selected";this.makeDarkRow(q)}else{if(s==="input"){this.makeWhiteRow(q)}}}this.checkTools()},makeDarkRow:function(q){q.removeClass("light");q.find("a").removeClass("light");q.addClass("dark");q.find("a").addClass("dark")},makeWhiteRow:function(q){q.removeClass("dark");q.find("a").removeClass("dark");q.addClass("light");q.find("a").addClass("light")},checkTools:function(){var q=$("#folder_table").find(":checked");if(q.length>0){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}else{$("#toolbtn_bulk_import").hide();$("#toolbtn_dl").hide()}},modalBulkImport:function(){var r=this;var q=new o();q.fetch({success:function(s){var t=_.template(r.templateBulkImportInModal(),{histories:s.models});r.modal=new j.GalaxyModal({title:"Import into History",body:t,buttons:{Import:function(){r.importAllIntoHistory()},Close:function(){r.modal.hide();$(".modal").remove();r.modal=null}}});r.modal.bindEvents(event);r.modal.show()}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var s=$("select[name=dataset_import_bulk] option:selected").val();var w=$("select[name=dataset_import_bulk] option:selected").text();var y=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){y.push(this.parentElement.parentElement.id)}});var x=_.template(this.templateProgressBar(),{history_name:w});$(this.modal.elMain).find(".modal-body").html(x);var t=100/y.length;this.initProgress(t);var q=[];for(var r=y.length-1;r>=0;r--){library_dataset_id=y[r];var u=new b();var v=this;u.url=u.urlRoot+s+"/contents";u.content=library_dataset_id;u.source="library";q.push(u)}this.chainCall(q)},chainCall:function(r){var q=this;var s=r.pop();if(typeof s==="undefined"){m.success("All datasets imported");this.modal.destroy();return}var t=$.when(s.save({content:s.content,source:s.source})).done(function(u){q.updateProgress();responses.push(u);q.chainCall(r)})},initProgress:function(q){this.progress=0;this.progressStep=q},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},templateProgressBar:function(){var q=[];q.push('<div class="import_text">');q.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");q.push("</div>");q.push('<div class="progress">');q.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');q.push(' <span class="completion_span">0% Complete</span>');q.push(" </div>");q.push("</div>");q.push("");return q.join("")},download:function(q,u){var s=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){s.push(this.parentElement.parentElement.id)}});var r="/api/libraries/datasets/download/"+u;var t={ldda_ids:s};this.processDownload(r,t,"get")},processDownload:function(r,s,t){if(r&&s){s=typeof s=="string"?s:$.param(s);var q="";$.each(s.split("&"),function(){var u=this.split("=");q+='<input type="hidden" name="'+u[0]+'" value="'+u[1]+'" />'});$('<form action="'+r+'" method="'+(t||"post")+'">'+q+"</form>").appendTo("body").submit().remove();m.info("Your download will begin soon")}},createFolderModal:function(){m.info("This will create folder...in the future")}});var a=Backbone.View.extend({el:"#center",events:{"click #create_new_library_btn":"show_library_modal"},initialize:function(){},template_library_list:function(){tmpl_array=[];tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');tmpl_array.push("");tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary file ">New Library</a>');tmpl_array.push('<table class="table table-condensed">');tmpl_array.push(" <thead>");tmpl_array.push(' <th class="button_heading"></th>');tmpl_array.push(" <th>name</th>");tmpl_array.push(" <th>description</th>");tmpl_array.push(" <th>synopsis</th> ");tmpl_array.push(" <th>model type</th> ");tmpl_array.push(" </thead>");tmpl_array.push(" <tbody>");tmpl_array.push(" <% _.each(libraries, function(library) { %>");tmpl_array.push(" <tr>");tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');tmpl_array.push(' <td><%- library.get("name") %></td>');tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');tmpl_array.push(" </tr>");tmpl_array.push(" <% }); %>");tmpl_array.push(" </tbody>");tmpl_array.push("</table>");tmpl_array.push("</div>");return tmpl_array.join("")},render:function(){$("#center").css("overflow","auto");var q=this;libraries=new n();libraries.fetch({success:function(r){var s=_.template(q.template_library_list(),{libraries:r.models});q.$el.html(s)},error:function(s,r){if(r.statusCode().status===403){m.error("Please log in first. Redirecting to login page in 3s.");setTimeout(q.redirectToLogin,3000)}else{m.error("An error occured. Please try again.")}}})},redirectToHome:function(){window.location="../"},redirectToLogin:function(){window.location="/user/login"},modal:null,show_library_modal:function(r){r.preventDefault();r.stopPropagation();var q=this;this.modal=new j.GalaxyModal({title:"Create New Library",body:this.template_new_library(),buttons:{Create:function(){q.create_new_library_event()},Close:function(){q.modal.hide()}}});this.modal.show()},create_new_library_event:function(){var s=this.serialize_new_library();if(this.validate_new_library(s)){var r=new e();var q=this;r.save(s,{success:function(t){q.modal.hide();q.clear_library_modal();q.render();m.success("Library created")},error:function(){m.error("An error occured :(")}})}else{m.error("Library's name is missing")}return false},clear_library_modal:function(){$("input[name='Name']").val("");$("input[name='Description']").val("");$("input[name='Synopsis']").val("")},serialize_new_library:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val(),synopsis:$("input[name='Synopsis']").val()}},validate_new_library:function(q){return q.name!==""},template_new_library:function(){tmpl_array=[];tmpl_array.push('<div id="new_library_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var f=Backbone.View.extend({folderContentView:null,galaxyLibraryview:null,initialize:function(){folderContentView=new l();galaxyLibraryview=new a();library_router=new p();library_router.on("route:libraries",function(){galaxyLibraryview.render()});library_router.on("route:folder_content",function(q){folderContentView.render({id:q})});library_router.on("route:download",function(q,r){if($("#center").find(":checked").length===0){library_router.navigate("folders/"+q,{trigger:true,replace:true})}else{folderContentView.download(q,r);library_router.navigate("folders/"+q,{trigger:false,replace:true})}});Backbone.history.start();return this}});return{GalaxyApp:f}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Raise an exception if attempting to install a repository into Galaxy that contains an invalid repository dependency definition (implying a bug in the Tool Shed framework).
by commits-noreply@bitbucket.org 03 Jan '14
by commits-noreply@bitbucket.org 03 Jan '14
03 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/c4a97053aca3/
Changeset: c4a97053aca3
User: greg
Date: 2014-01-03 20:21:29
Summary: Raise an exception if attempting to install a repository into Galaxy that contains an invalid repository dependency definition (implying a bug in the Tool Shed framework).
Affected #: 5 files
diff -r 10b7c04f919ff6d7fc1d03ba88ebb282645fcbba -r c4a97053aca397ca783079a1e05c0cd2f921fb88 lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -1440,16 +1440,17 @@
tool_path, relative_install_dir = repository.get_tool_relative_path( trans.app )
if relative_install_dir:
original_metadata_dict = repository.metadata
- metadata_dict, invalid_file_tups = metadata_util.generate_metadata_for_changeset_revision( app=trans.app,
- repository=repository,
- changeset_revision=repository.changeset_revision,
- repository_clone_url=repository_clone_url,
- shed_config_dict = repository.get_shed_config_dict( trans.app ),
- relative_install_dir=relative_install_dir,
- repository_files_dir=None,
- resetting_all_metadata_on_repository=False,
- updating_installed_repository=False,
- persist=False )
+ metadata_dict, invalid_file_tups = \
+ metadata_util.generate_metadata_for_changeset_revision( app=trans.app,
+ repository=repository,
+ changeset_revision=repository.changeset_revision,
+ repository_clone_url=repository_clone_url,
+ shed_config_dict = repository.get_shed_config_dict( trans.app ),
+ relative_install_dir=relative_install_dir,
+ repository_files_dir=None,
+ resetting_all_metadata_on_repository=False,
+ updating_installed_repository=False,
+ persist=False )
repository.metadata = metadata_dict
if metadata_dict != original_metadata_dict:
suc.update_in_shed_tool_config( trans.app, repository )
@@ -1607,7 +1608,11 @@
changeset_revision = kwd.get( 'changeset_revision', None )
latest_changeset_revision = kwd.get( 'latest_changeset_revision', None )
latest_ctx_rev = kwd.get( 'latest_ctx_rev', None )
- repository = suc.get_tool_shed_repository_by_shed_name_owner_changeset_revision( trans.app, tool_shed_url, name, owner, changeset_revision )
+ repository = suc.get_tool_shed_repository_by_shed_name_owner_changeset_revision( trans.app,
+ tool_shed_url,
+ name,
+ owner,
+ changeset_revision )
if changeset_revision and latest_changeset_revision and latest_ctx_rev:
if changeset_revision == latest_changeset_revision:
message = "The installed repository named '%s' is current, there are no updates available. " % name
@@ -1627,16 +1632,17 @@
if repository.includes_data_managers:
data_manager_util.remove_from_data_manager( trans.app, repository )
# Update the repository metadata.
- metadata_dict, invalid_file_tups = metadata_util.generate_metadata_for_changeset_revision( app=trans.app,
- repository=repository,
- changeset_revision=latest_changeset_revision,
- repository_clone_url=repository_clone_url,
- shed_config_dict = repository.get_shed_config_dict( trans.app ),
- relative_install_dir=relative_install_dir,
- repository_files_dir=None,
- resetting_all_metadata_on_repository=False,
- updating_installed_repository=True,
- persist=True )
+ metadata_dict, invalid_file_tups = \
+ metadata_util.generate_metadata_for_changeset_revision( app=trans.app,
+ repository=repository,
+ changeset_revision=latest_changeset_revision,
+ repository_clone_url=repository_clone_url,
+ shed_config_dict=repository.get_shed_config_dict( trans.app ),
+ relative_install_dir=relative_install_dir,
+ repository_files_dir=None,
+ resetting_all_metadata_on_repository=False,
+ updating_installed_repository=True,
+ persist=True )
repository.metadata = metadata_dict
# Update the repository.changeset_revision column in the database.
repository.changeset_revision = latest_changeset_revision
@@ -1674,10 +1680,15 @@
repository_tools_tups )
# Create tool_dependency records if necessary.
if 'tool_dependencies' in metadata_dict:
- tool_dependencies = tool_dependency_util.create_tool_dependency_objects( trans.app, repository, relative_install_dir, set_status=False )
- message = "The installed repository named '%s' has been updated to change set revision '%s'. " % ( name, latest_changeset_revision )
+ tool_dependencies = tool_dependency_util.create_tool_dependency_objects( trans.app,
+ repository,
+ relative_install_dir,
+ set_status=False )
+ message = "The installed repository named '%s' has been updated to change set revision '%s'. " % \
+ ( name, latest_changeset_revision )
# See if any tool dependencies can be installed.
- shed_tool_conf, tool_path, relative_install_dir = suc.get_tool_panel_config_tool_path_install_dir( trans.app, repository )
+ shed_tool_conf, tool_path, relative_install_dir = \
+ suc.get_tool_panel_config_tool_path_install_dir( trans.app, repository )
if repository.missing_tool_dependencies:
message += "Click the name of one of the missing tool dependencies listed below to install tool dependencies."
else:
diff -r 10b7c04f919ff6d7fc1d03ba88ebb282645fcbba -r c4a97053aca397ca783079a1e05c0cd2f921fb88 lib/tool_shed/galaxy_install/install_manager.py
--- a/lib/tool_shed/galaxy_install/install_manager.py
+++ b/lib/tool_shed/galaxy_install/install_manager.py
@@ -330,12 +330,13 @@
tool_panel_elems.append( elem )
return tool_panel_elems
- def handle_repository_contents( self, tool_shed_repository, repository_clone_url, relative_install_dir, repository_elem, install_dependencies, is_repository_dependency=False ):
+ def handle_repository_contents( self, tool_shed_repository, repository_clone_url, relative_install_dir, repository_elem,
+ install_dependencies, is_repository_dependency=False ):
"""
- Generate the metadata for the installed tool shed repository, among other things. If the installed tool_shed_repository contains tools
- that are loaded into the Galaxy tool panel, this method will automatically eliminate all entries for each of the tools defined in the
- received repository_elem from all non-shed-related tool panel configuration files since the entries are automatically added to the reserved
- migrated_tools_conf.xml file as part of the migration process.
+ Generate the metadata for the installed tool shed repository, among other things. If the installed tool_shed_repository
+ contains tools that are loaded into the Galaxy tool panel, this method will automatically eliminate all entries for each
+ of the tools defined in the received repository_elem from all non-shed-related tool panel configuration files since the
+ entries are automatically added to the reserved migrated_tools_conf.xml file as part of the migration process.
"""
tool_configs_to_filter = []
tool_panel_dict_for_display = odict()
@@ -351,14 +352,16 @@
# See if tool_config is defined inside of a section in self.proprietary_tool_panel_elems.
is_displayed, tool_sections = self.get_containing_tool_sections( tool_config )
if is_displayed:
- tool_panel_dict_for_tool_config = tool_util.generate_tool_panel_dict_for_tool_config( guid, tool_config, tool_sections=tool_sections )
+ tool_panel_dict_for_tool_config = \
+ tool_util.generate_tool_panel_dict_for_tool_config( guid, tool_config, tool_sections=tool_sections )
# The tool-panel_dict has the following structure.
- # {<Tool guid> : [{ tool_config : <tool_config_file>, id: <ToolSection id>, version : <ToolSection version>, name : <TooSection name>}]}
+ # {<Tool guid> : [{ tool_config : <tool_config_file>, id: <ToolSection id>, version : <ToolSection version>,
+ # name : <TooSection name>}]}
for k, v in tool_panel_dict_for_tool_config.items():
tool_panel_dict_for_display[ k ] = v
for tool_panel_dict in v:
- # Keep track of tool config file names associated with entries that have been made to the migrated_tools_conf.xml file so
- # they can be eliminated from all non-shed-related tool panel configs.
+ # Keep track of tool config file names associated with entries that have been made to the
+ # migrated_tools_conf.xml file so they can be eliminated from all non-shed-related tool panel configs.
tool_config_file = tool_panel_dict.get( 'tool_config', None )
if tool_config_file:
if tool_config_file not in tool_configs_to_filter:
@@ -375,23 +378,28 @@
log.exception( "Exception attempting to filter and persist non-shed-related tool panel configs:\n%s" % str( e ) )
finally:
lock.release()
- metadata_dict, invalid_file_tups = metadata_util.generate_metadata_for_changeset_revision( app=self.app,
- repository=tool_shed_repository,
- changeset_revision=tool_shed_repository.changeset_revision,
- repository_clone_url=repository_clone_url,
- shed_config_dict = self.shed_config_dict,
- relative_install_dir=relative_install_dir,
- repository_files_dir=None,
- resetting_all_metadata_on_repository=False,
- updating_installed_repository=False,
- persist=True )
+ metadata_dict, invalid_file_tups = \
+ metadata_util.generate_metadata_for_changeset_revision( app=self.app,
+ repository=tool_shed_repository,
+ changeset_revision=tool_shed_repository.changeset_revision,
+ repository_clone_url=repository_clone_url,
+ shed_config_dict = self.shed_config_dict,
+ relative_install_dir=relative_install_dir,
+ repository_files_dir=None,
+ resetting_all_metadata_on_repository=False,
+ updating_installed_repository=False,
+ persist=True )
tool_shed_repository.metadata = metadata_dict
self.app.install_model.context.add( tool_shed_repository )
self.app.install_model.context.flush()
has_tool_dependencies = self.__has_tool_dependencies( metadata_dict )
if has_tool_dependencies:
- # All tool_dependency objects must be created before the tools are processed even if no tool dependencies will be installed.
- tool_dependencies = tool_dependency_util.create_tool_dependency_objects( self.app, tool_shed_repository, relative_install_dir, set_status=True )
+ # All tool_dependency objects must be created before the tools are processed even if no
+ # tool dependencies will be installed.
+ tool_dependencies = tool_dependency_util.create_tool_dependency_objects( self.app,
+ tool_shed_repository,
+ relative_install_dir,
+ set_status=True )
else:
tool_dependencies = None
if 'tools' in metadata_dict:
diff -r 10b7c04f919ff6d7fc1d03ba88ebb282645fcbba -r c4a97053aca397ca783079a1e05c0cd2f921fb88 lib/tool_shed/galaxy_install/repository_util.py
--- a/lib/tool_shed/galaxy_install/repository_util.py
+++ b/lib/tool_shed/galaxy_install/repository_util.py
@@ -339,23 +339,24 @@
changeset_revision_dict[ 'ctx_rev' ] = None
return changeset_revision_dict
-def handle_repository_contents( trans, tool_shed_repository, tool_path, repository_clone_url, relative_install_dir, tool_shed=None, tool_section=None, shed_tool_conf=None,
- reinstalling=False ):
+def handle_repository_contents( trans, tool_shed_repository, tool_path, repository_clone_url, relative_install_dir,
+ tool_shed=None, tool_section=None, shed_tool_conf=None, reinstalling=False ):
"""
- Generate the metadata for the installed tool shed repository, among other things. This method is called from Galaxy (never the tool shed)
- when an administrator is installing a new repository or reinstalling an uninstalled repository.
+ Generate the metadata for the installed tool shed repository, among other things. This method is called from Galaxy
+ (never the tool shed) when an administrator is installing a new repository or reinstalling an uninstalled repository.
"""
shed_config_dict = trans.app.toolbox.get_shed_config_dict_by_filename( shed_tool_conf )
- metadata_dict, invalid_file_tups = metadata_util.generate_metadata_for_changeset_revision( app=trans.app,
- repository=tool_shed_repository,
- changeset_revision=tool_shed_repository.changeset_revision,
- repository_clone_url=repository_clone_url,
- shed_config_dict=shed_config_dict,
- relative_install_dir=relative_install_dir,
- repository_files_dir=None,
- resetting_all_metadata_on_repository=False,
- updating_installed_repository=False,
- persist=True )
+ metadata_dict, invalid_file_tups = \
+ metadata_util.generate_metadata_for_changeset_revision( app=trans.app,
+ repository=tool_shed_repository,
+ changeset_revision=tool_shed_repository.changeset_revision,
+ repository_clone_url=repository_clone_url,
+ shed_config_dict=shed_config_dict,
+ relative_install_dir=relative_install_dir,
+ repository_files_dir=None,
+ resetting_all_metadata_on_repository=False,
+ updating_installed_repository=False,
+ persist=True )
tool_shed_repository.metadata = metadata_dict
# Update the tool_shed_repository.tool_shed_status column in the database.
tool_shed_status_dict = suc.get_tool_shed_status_for_installed_repository( trans.app, tool_shed_repository )
@@ -364,13 +365,20 @@
trans.install_model.context.add( tool_shed_repository )
trans.install_model.context.flush()
if 'tool_dependencies' in metadata_dict and not reinstalling:
- tool_dependencies = tool_dependency_util.create_tool_dependency_objects( trans.app, tool_shed_repository, relative_install_dir, set_status=True )
+ tool_dependencies = tool_dependency_util.create_tool_dependency_objects( trans.app,
+ tool_shed_repository,
+ relative_install_dir,
+ set_status=True )
if 'sample_files' in metadata_dict:
sample_files = metadata_dict.get( 'sample_files', [] )
tool_index_sample_files = tool_util.get_tool_index_sample_files( sample_files )
- tool_data_table_conf_filename, tool_data_table_elems = tool_util.install_tool_data_tables( trans.app, tool_shed_repository, tool_index_sample_files )
+ tool_data_table_conf_filename, tool_data_table_elems = \
+ tool_util.install_tool_data_tables( trans.app, tool_shed_repository, tool_index_sample_files )
if tool_data_table_elems:
- trans.app.tool_data_tables.add_new_entries_from_config_file( tool_data_table_conf_filename, None, trans.app.config.shed_tool_data_table_config, persist=True )
+ trans.app.tool_data_tables.add_new_entries_from_config_file( tool_data_table_conf_filename,
+ None,
+ trans.app.config.shed_tool_data_table_config,
+ persist=True )
if 'tools' in metadata_dict:
tool_panel_dict = tool_util.generate_tool_panel_dict_for_new_install( metadata_dict[ 'tools' ], tool_section )
sample_files = metadata_dict.get( 'sample_files', [] )
@@ -380,10 +388,19 @@
repository_tools_tups = suc.get_repository_tools_tups( trans.app, metadata_dict )
if repository_tools_tups:
# Handle missing data table entries for tool parameters that are dynamically generated select lists.
- repository_tools_tups = tool_util.handle_missing_data_table_entry( trans.app, relative_install_dir, tool_path, repository_tools_tups )
+ repository_tools_tups = tool_util.handle_missing_data_table_entry( trans.app,
+ relative_install_dir,
+ tool_path,
+ repository_tools_tups )
# Handle missing index files for tool parameters that are dynamically generated select lists.
- repository_tools_tups, sample_files_copied = tool_util.handle_missing_index_file( trans.app, tool_path, sample_files, repository_tools_tups, sample_files_copied )
- # Copy remaining sample files included in the repository to the ~/tool-data directory of the local Galaxy instance.
+ repository_tools_tups, sample_files_copied = \
+ tool_util.handle_missing_index_file( trans.app,
+ tool_path,
+ sample_files,
+ repository_tools_tups,
+ sample_files_copied )
+ # Copy remaining sample files included in the repository to the ~/tool-data directory of the
+ # local Galaxy instance.
tool_util.copy_sample_files( trans.app, sample_files, tool_path=tool_path, sample_files_copied=sample_files_copied )
tool_util.add_to_tool_panel( app=trans.app,
repository_name=tool_shed_repository.name,
@@ -413,16 +430,18 @@
files_dir = os.path.join( shed_config_dict[ 'tool_path' ], files_dir )
datatypes_config = suc.get_config_from_disk( suc.DATATYPES_CONFIG_FILENAME, files_dir )
# Load data types required by tools.
- converter_path, display_path = datatype_util.alter_config_and_load_prorietary_datatypes( trans.app, datatypes_config, files_dir, override=False )
+ converter_path, display_path = \
+ datatype_util.alter_config_and_load_prorietary_datatypes( trans.app, datatypes_config, files_dir, override=False )
if converter_path or display_path:
# Create a dictionary of tool shed repository related information.
- repository_dict = datatype_util.create_repository_dict_for_proprietary_datatypes( tool_shed=tool_shed,
- name=tool_shed_repository.name,
- owner=tool_shed_repository.owner,
- installed_changeset_revision=tool_shed_repository.installed_changeset_revision,
- tool_dicts=metadata_dict.get( 'tools', [] ),
- converter_path=converter_path,
- display_path=display_path )
+ repository_dict = \
+ datatype_util.create_repository_dict_for_proprietary_datatypes( tool_shed=tool_shed,
+ name=tool_shed_repository.name,
+ owner=tool_shed_repository.owner,
+ installed_changeset_revision=tool_shed_repository.installed_changeset_revision,
+ tool_dicts=metadata_dict.get( 'tools', [] ),
+ converter_path=converter_path,
+ display_path=display_path )
if converter_path:
# Load proprietary datatype converters
trans.app.datatypes_registry.load_datatype_converters( trans.app.toolbox, installed_repository_dict=repository_dict )
diff -r 10b7c04f919ff6d7fc1d03ba88ebb282645fcbba -r c4a97053aca397ca783079a1e05c0cd2f921fb88 lib/tool_shed/util/metadata_util.py
--- a/lib/tool_shed/util/metadata_util.py
+++ b/lib/tool_shed/util/metadata_util.py
@@ -568,23 +568,25 @@
tmp_url = suc.clean_repository_clone_url( repository_clone_url )
return '%s/%s/%s/%s' % ( tmp_url, guid_type, obj_id, version )
-def generate_metadata_for_changeset_revision( app, repository, changeset_revision, repository_clone_url, shed_config_dict=None, relative_install_dir=None,
- repository_files_dir=None, resetting_all_metadata_on_repository=False, updating_installed_repository=False,
+def generate_metadata_for_changeset_revision( app, repository, changeset_revision, repository_clone_url,
+ shed_config_dict=None, relative_install_dir=None, repository_files_dir=None,
+ resetting_all_metadata_on_repository=False, updating_installed_repository=False,
persist=False ):
"""
- Generate metadata for a repository using it's files on disk. To generate metadata for changeset revisions older than the repository tip,
- the repository will have been cloned to a temporary location and updated to a specified changeset revision to access that changeset revision's
- disk files, so the value of repository_files_dir will not always be repository.repo_path( app ) (it could be an absolute path to a temporary
- directory containing a clone). If it is an absolute path, the value of relative_install_dir must contain repository.repo_path( app ).
+ Generate metadata for a repository using it's files on disk. To generate metadata for changeset revisions older than
+ the repository tip, the repository will have been cloned to a temporary location and updated to a specified changeset
+ revision to access that changeset revision's disk files, so the value of repository_files_dir will not always be
+ repository.repo_path( app ) (it could be an absolute path to a temporary directory containing a clone). If it is an
+ absolute path, the value of relative_install_dir must contain repository.repo_path( app ).
- The value of persist will be True when the installed repository contains a valid tool_data_table_conf.xml.sample file, in which case the entries
- should ultimately be persisted to the file referred to by app.config.shed_tool_data_table_config.
+ The value of persist will be True when the installed repository contains a valid tool_data_table_conf.xml.sample file,
+ in which case the entries should ultimately be persisted to the file referred to by app.config.shed_tool_data_table_config.
"""
if shed_config_dict is None:
shed_config_dict = {}
if updating_installed_repository:
- # Keep the original tool shed repository metadata if setting metadata on a repository installed into a local Galaxy instance for which
- # we have pulled updates.
+ # Keep the original tool shed repository metadata if setting metadata on a repository installed into a local Galaxy
+ # instance for which we have pulled updates.
original_repository_metadata = repository.metadata
else:
original_repository_metadata = None
@@ -603,11 +605,12 @@
if resetting_all_metadata_on_repository:
if not relative_install_dir:
raise Exception( "The value of repository.repo_path( app ) must be sent when resetting all metadata on a repository." )
- # Keep track of the location where the repository is temporarily cloned so that we can strip the path when setting metadata. The value of
- # repository_files_dir is the full path to the temporary directory to which the repository was cloned.
+ # Keep track of the location where the repository is temporarily cloned so that we can strip the path when setting metadata.
+ # The value of repository_files_dir is the full path to the temporary directory to which the repository was cloned.
work_dir = repository_files_dir
files_dir = repository_files_dir
- # Since we're working from a temporary directory, we can safely copy sample files included in the repository to the repository root.
+ # Since we're working from a temporary directory, we can safely copy sample files included in the repository to the repository
+ # root.
app.config.tool_data_path = repository_files_dir
app.config.tool_data_table_config_path = repository_files_dir
else:
@@ -624,10 +627,11 @@
if datatypes_config:
metadata_dict = generate_datatypes_metadata( app, repository, repository_clone_url, files_dir, datatypes_config, metadata_dict )
# Get the relative path to all sample files included in the repository for storage in the repository's metadata.
- sample_file_metadata_paths, sample_file_copy_paths = get_sample_files_from_disk( repository_files_dir=files_dir,
- tool_path=shed_config_dict.get( 'tool_path' ),
- relative_install_dir=relative_install_dir,
- resetting_all_metadata_on_repository=resetting_all_metadata_on_repository )
+ sample_file_metadata_paths, sample_file_copy_paths = \
+ get_sample_files_from_disk( repository_files_dir=files_dir,
+ tool_path=shed_config_dict.get( 'tool_path' ),
+ relative_install_dir=relative_install_dir,
+ resetting_all_metadata_on_repository=resetting_all_metadata_on_repository )
if sample_file_metadata_paths:
metadata_dict[ 'sample_files' ] = sample_file_metadata_paths
# Copy all sample files included in the repository to a single directory location so we can load tools that depend on them.
@@ -636,10 +640,11 @@
# If the list of sample files includes a tool_data_table_conf.xml.sample file, laad it's table elements into memory.
relative_path, filename = os.path.split( sample_file )
if filename == 'tool_data_table_conf.xml.sample':
- new_table_elems, error_message = app.tool_data_tables.add_new_entries_from_config_file( config_filename=sample_file,
- tool_data_path=app.config.tool_data_path,
- shed_tool_data_table_config=app.config.shed_tool_data_table_config,
- persist=False )
+ new_table_elems, error_message = \
+ app.tool_data_tables.add_new_entries_from_config_file( config_filename=sample_file,
+ tool_data_path=app.config.tool_data_path,
+ shed_tool_data_table_config=app.config.shed_tool_data_table_config,
+ persist=False )
if error_message:
invalid_file_tups.append( ( filename, error_message ) )
for root, dirs, files in os.walk( files_dir ):
@@ -650,7 +655,9 @@
# See if we have a repository dependencies defined.
if name == suc.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME:
path_to_repository_dependencies_config = os.path.join( root, name )
- metadata_dict, error_message = generate_repository_dependency_metadata( app, path_to_repository_dependencies_config, metadata_dict )
+ metadata_dict, error_message = generate_repository_dependency_metadata( app,
+ path_to_repository_dependencies_config,
+ metadata_dict )
if error_message:
invalid_file_tups.append( ( name, error_message ) )
# See if we have one or more READ_ME files.
@@ -666,8 +673,11 @@
elif name not in NOT_TOOL_CONFIGS and name.endswith( '.xml' ):
full_path = str( os.path.abspath( os.path.join( root, name ) ) )
if os.path.getsize( full_path ) > 0:
- if not ( checkers.check_binary( full_path ) or checkers.check_image( full_path ) or checkers.check_gzip( full_path )[ 0 ]
- or checkers.check_bz2( full_path )[ 0 ] or checkers.check_zip( full_path ) ):
+ if not ( checkers.check_binary( full_path ) or
+ checkers.check_image( full_path ) or
+ checkers.check_gzip( full_path )[ 0 ] or
+ checkers.check_bz2( full_path )[ 0 ] or
+ checkers.check_zip( full_path ) ):
# Make sure we're looking at a tool config and not a display application config or something else.
element_tree, error_message = xml_util.parse_xml( full_path )
if element_tree is None:
@@ -676,13 +686,15 @@
element_tree_root = element_tree.getroot()
is_tool = element_tree_root.tag == 'tool'
if is_tool:
- tool, valid, error_message = tool_util.load_tool_from_config( app, app.security.encode_id( repository.id ), full_path )
+ tool, valid, error_message = \
+ tool_util.load_tool_from_config( app, app.security.encode_id( repository.id ), full_path )
if tool is None:
if not valid:
invalid_tool_configs.append( name )
invalid_file_tups.append( ( name, error_message ) )
else:
- invalid_files_and_errors_tups = tool_util.check_tool_input_params( app, files_dir, name, tool, sample_file_copy_paths )
+ invalid_files_and_errors_tups = \
+ tool_util.check_tool_input_params( app, files_dir, name, tool, sample_file_copy_paths )
can_set_metadata = True
for tup in invalid_files_and_errors_tups:
if name in tup:
@@ -690,15 +702,17 @@
invalid_tool_configs.append( name )
break
if can_set_metadata:
- relative_path_to_tool_config = get_relative_path_to_repository_file( root,
- name,
- relative_install_dir,
- work_dir,
- shed_config_dict,
- resetting_all_metadata_on_repository )
-
-
- metadata_dict = generate_tool_metadata( relative_path_to_tool_config, tool, repository_clone_url, metadata_dict )
+ relative_path_to_tool_config = \
+ get_relative_path_to_repository_file( root,
+ name,
+ relative_install_dir,
+ work_dir,
+ shed_config_dict,
+ resetting_all_metadata_on_repository )
+ metadata_dict = generate_tool_metadata( relative_path_to_tool_config,
+ tool,
+ repository_clone_url,
+ metadata_dict )
else:
for tup in invalid_files_and_errors_tups:
invalid_file_tups.append( tup )
@@ -752,8 +766,9 @@
def generate_package_dependency_metadata( app, elem, valid_tool_dependencies_dict, invalid_tool_dependencies_dict ):
"""
- Generate the metadata for a tool dependencies package defined for a repository. The value of package_name must match the value of the "package"
- type in the tool config's <requirements> tag set. This method is called from both Galaxy and the tool shed.
+ Generate the metadata for a tool dependencies package defined for a repository. The value of package_name must
+ match the value of the "package" type in the tool config's <requirements> tag set. This method is called from
+ both Galaxy and the tool shed.
"""
repository_dependency_is_valid = True
repository_dependency_tup = []
@@ -833,22 +848,23 @@
valid_repository_dependencies_dict = dict( description=root.get( 'description' ) )
valid_repository_dependency_tups = []
for repository_elem in root.findall( 'repository' ):
- repository_dependency_tup, repository_dependency_is_valid, error_message = handle_repository_elem( app,
- repository_elem,
- only_if_compiling_contained_td=False )
+ repository_dependency_tup, repository_dependency_is_valid, err_msg = \
+ handle_repository_elem( app, repository_elem, only_if_compiling_contained_td=False )
if repository_dependency_is_valid:
valid_repository_dependency_tups.append( repository_dependency_tup )
else:
# Append the error_message to the repository dependencies tuple.
- toolshed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = repository_dependency_tup
+ toolshed, name, owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td = \
+ repository_dependency_tup
repository_dependency_tup = ( toolshed,
name,
owner,
changeset_revision,
prior_installation_required,
only_if_compiling_contained_td,
- error_message )
+ err_msg )
invalid_repository_dependency_tups.append( repository_dependency_tup )
+ error_message += err_msg
if invalid_repository_dependency_tups:
invalid_repository_dependencies_dict[ 'repository_dependencies' ] = invalid_repository_dependency_tups
metadata_dict[ 'invalid_repository_dependencies' ] = invalid_repository_dependencies_dict
@@ -1142,50 +1158,72 @@
def handle_repository_elem( app, repository_elem, only_if_compiling_contained_td=False ):
"""
- Process the received repository_elem which is a <repository> tag either from a repository_dependencies.xml file or a tool_dependencies.xml file.
- If the former, we're generating repository dependencies metadata for a repository in the tool shed. If the latter, we're generating package
- dependency metadata within Galaxy or the tool shed.
+ Process the received repository_elem which is a <repository> tag either from a repository_dependencies.xml
+ file or a tool_dependencies.xml file. If the former, we're generating repository dependencies metadata for
+ a repository in the tool shed. If the latter, we're generating package dependency metadata within Galaxy or
+ the tool shed.
"""
sa_session = app.model.context.current
is_valid = True
error_message = ''
- toolshed = repository_elem.get( 'toolshed' )
+ toolshed = repository_elem.get( 'toolshed', None )
+ name = repository_elem.get( 'name', None )
+ owner = repository_elem.get( 'owner', None )
+ changeset_revision = repository_elem.get( 'changeset_revision', None )
+ prior_installation_required = str( repository_elem.get( 'prior_installation_required', False ) )
+ if app.name == 'galaxy':
+ # We're installing a repository into Galaxy, so make sure its contained repository dependency definition
+ # is valid.
+ if toolshed is None or name is None or owner is None or changeset_revision is None:
+ # Raise an exception here instead of returning an error_message to keep the installation from
+ # proceeding. Reaching here implies a bug in the Tool Shed framework.
+ error_message = 'Installation halted because the following repository dependency definition is invalid:\n'
+ error_message += xml_util.xml_to_string( repository_elem, use_indent=True )
+ raise Exception( error_message )
if not toolshed:
# Default to the current tool shed.
toolshed = str( url_for( '/', qualified=True ) ).rstrip( '/' )
+ repository_dependency_tup = [ toolshed,
+ name,
+ owner,
+ changeset_revision,
+ prior_installation_required,
+ str( only_if_compiling_contained_td ) ]
cleaned_toolshed = td_common_util.clean_tool_shed_url( toolshed )
- name = repository_elem.get( 'name' )
- owner = repository_elem.get( 'owner' )
- changeset_revision = repository_elem.get( 'changeset_revision' )
- prior_installation_required = str( repository_elem.get( 'prior_installation_required', False ) )
- repository_dependency_tup = [ toolshed, name, owner, changeset_revision, prior_installation_required, str( only_if_compiling_contained_td ) ]
user = None
repository = None
if app.name == 'galaxy':
- # We're in Galaxy. We reach here when we're generating the metadata for a tool dependencies package defined for a repository or when we're
- # generating metadata for an installed repository. See if we can locate the installed repository via the changeset_revision defined in the
- # repository_elem (it may be outdated). If we're successful in locating an installed repository with the attributes defined in the
- # repository_elem, we know it is valid.
+ # We're in Galaxy. We reach here when we're generating the metadata for a tool dependencies package defined
+ # for a repository or when we're generating metadata for an installed repository. See if we can locate the
+ # installed repository via the changeset_revision defined in the repository_elem (it may be outdated). If we're
+ # successful in locating an installed repository with the attributes defined in the repository_elem, we know it
+ # is valid.
repository = suc.get_repository_for_dependency_relationship( app, cleaned_toolshed, name, owner, changeset_revision )
if repository:
return repository_dependency_tup, is_valid, error_message
else:
- # Send a request to the tool shed to retrieve appropriate additional changeset revisions with which the repository may have been installed.
+ # Send a request to the tool shed to retrieve appropriate additional changeset revisions with which the repository
+ # may have been installed.
text = install_util.get_updated_changeset_revisions_from_tool_shed( app, toolshed, name, owner, changeset_revision )
if text:
updated_changeset_revisions = util.listify( text )
for updated_changeset_revision in updated_changeset_revisions:
- repository = suc.get_repository_for_dependency_relationship( app, cleaned_toolshed, name, owner, updated_changeset_revision )
+ repository = suc.get_repository_for_dependency_relationship( app,
+ cleaned_toolshed,
+ name,
+ owner,
+ updated_changeset_revision )
if repository:
return repository_dependency_tup, is_valid, error_message
- # Don't generate an error message for missing repository dependencies that are required only if compiling the dependent repository's
- # tool dependency.
+ # Don't generate an error message for missing repository dependencies that are required only if compiling the
+ # dependent repository's tool dependency.
if not only_if_compiling_contained_td:
- # We'll currently default to setting the repository dependency definition as invalid if an installed repository cannot be found.
- # This may not be ideal because the tool shed may have simply been inaccessible when metadata was being generated for the installed
- # tool shed repository.
- error_message = "Ignoring invalid repository dependency definition for tool shed %s, name %s, owner %s, changeset revision %s "% \
- ( toolshed, name, owner, changeset_revision )
+ # We'll currently default to setting the repository dependency definition as invalid if an installed repository
+ # cannot be found. This may not be ideal because the tool shed may have simply been inaccessible when metadata
+ # was being generated for the installed tool shed repository.
+ error_message = "Ignoring invalid repository dependency definition for tool shed %s, name %s, owner %s, " % \
+ ( toolshed, name, owner )
+ error_message += "changeset revision %s." % changeset_revision
log.debug( error_message )
is_valid = False
return repository_dependency_tup, is_valid, error_message
@@ -1197,9 +1235,9 @@
.filter( app.model.User.table.c.username == owner ) \
.one()
except Exception, e:
- error_message = "Ignoring repository dependency definition for tool shed %s, name %s, owner %s, changeset revision %s "% \
- ( toolshed, name, owner, changeset_revision )
- error_message += "because the owner is invalid. "
+ error_message = "Ignoring repository dependency definition for tool shed %s, name %s, owner %s, " % \
+ ( toolshed, name, owner )
+ error_message += "changeset revision %s because the owner is invalid. " % changeset_revision
log.debug( error_message )
is_valid = False
return repository_dependency_tup, is_valid, error_message
@@ -1209,21 +1247,26 @@
app.model.Repository.table.c.user_id == user.id ) ) \
.one()
except:
- error_message = "Ignoring repository dependency definition for tool shed %s, name %s, owner %s, changeset revision %s "% \
- ( toolshed, name, owner, changeset_revision )
- error_message += "because the name is invalid. "
+ error_message = "Ignoring repository dependency definition for tool shed %s, name %s, owner %s, " % \
+ ( toolshed, name, owner )
+ error_message += "changeset revision %s because the name is invalid. " % changeset_revision
log.debug( error_message )
is_valid = False
return repository_dependency_tup, is_valid, error_message
repo = hg.repository( suc.get_configured_ui(), repository.repo_path( app ) )
- # The received changeset_revision may be None since defining it in the dependency definition is optional. If this is the case,
- # the default will be to set it's value to the repository dependency tip revision. This probably occurs only when handling
- # circular dependency definitions.
+ # The received changeset_revision may be None since defining it in the dependency definition is optional.
+ # If this is the case, the default will be to set it's value to the repository dependency tip revision.
+ # This probably occurs only when handling circular dependency definitions.
tip_ctx = repo.changectx( repo.changelog.tip() )
# Make sure the repo.changlog includes at least 1 revision.
if changeset_revision is None and tip_ctx.rev() >= 0:
changeset_revision = str( tip_ctx )
- repository_dependency_tup = [ toolshed, name, owner, changeset_revision, prior_installation_required, str( only_if_compiling_contained_td ) ]
+ repository_dependency_tup = [ toolshed,
+ name,
+ owner,
+ changeset_revision,
+ prior_installation_required,
+ str( only_if_compiling_contained_td ) ]
return repository_dependency_tup, is_valid, error_message
else:
# Find the specified changeset revision in the repository's changelog to see if it's valid.
@@ -1234,16 +1277,17 @@
found = True
break
if not found:
- error_message = "Ignoring repository dependency definition for tool shed %s, name %s, owner %s, changeset revision %s "% \
- ( toolshed, name, owner, changeset_revision )
- error_message += "because the changeset revision is invalid. "
+ error_message = "Ignoring repository dependency definition for tool shed %s, name %s, owner %s, " % \
+ ( toolshed, name, owner )
+ error_message += "changeset revision %s because the changeset revision is invalid. " % changeset_revision
log.debug( error_message )
is_valid = False
return repository_dependency_tup, is_valid, error_message
else:
# Repository dependencies are currently supported within a single tool shed.
- error_message = "Repository dependencies are currently supported only within the same tool shed. Ignoring repository dependency definition "
- error_message += "for tool shed %s, name %s, owner %s, changeset revision %s. " % ( toolshed, name, owner, changeset_revision )
+ error_message = "Repository dependencies are currently supported only within the same tool shed. Ignoring "
+ error_message += "repository dependency definition for tool shed %s, name %s, owner %s, changeset revision %s. " % \
+ ( toolshed, name, owner, changeset_revision )
log.debug( error_message )
is_valid = False
return repository_dependency_tup, is_valid, error_message
@@ -1811,8 +1855,8 @@
def set_repository_metadata( trans, repository, content_alert_str='', **kwd ):
"""
- Set metadata using the repository's current disk files, returning specific error messages (if any) to alert the repository owner that the changeset
- has problems.
+ Set metadata using the repository's current disk files, returning specific error messages (if any) to alert the
+ repository owner that the changeset has problems.
"""
message = ''
status = 'done'
diff -r 10b7c04f919ff6d7fc1d03ba88ebb282645fcbba -r c4a97053aca397ca783079a1e05c0cd2f921fb88 lib/tool_shed/util/shed_util_common.py
--- a/lib/tool_shed/util/shed_util_common.py
+++ b/lib/tool_shed/util/shed_util_common.py
@@ -945,7 +945,10 @@
return has_repository_dependencies, has_repository_dependencies_only_if_compiling_contained_td
def get_repository_for_dependency_relationship( app, tool_shed, name, owner, changeset_revision ):
- """Return an installed tool_shed_repository database record that is defined by either the current changeset revision or the installed_changeset_revision."""
+ """
+ Return an installed tool_shed_repository database record that is defined by either the current changeset
+ revision or the installed_changeset_revision.
+ """
# This method is used only in Galaxy, not the tool shed.
if tool_shed.endswith( '/' ):
tool_shed = tool_shed.rstrip( '/' )
@@ -1330,8 +1333,8 @@
def get_updated_changeset_revisions( trans, name, owner, changeset_revision ):
"""
- Return a string of comma-separated changeset revision hashes for all available updates to the received changeset revision for the repository
- defined by the received name and owner.
+ Return a string of comma-separated changeset revision hashes for all available updates to the received changeset
+ revision for the repository defined by the received name and owner.
"""
repository = get_repository_by_name_and_owner( trans.app, name, owner )
repo_dir = repository.repo_path( trans.app )
@@ -1351,8 +1354,8 @@
def get_url_from_tool_shed( app, tool_shed ):
"""
- The value of tool_shed is something like: toolshed.g2.bx.psu.edu. We need the URL to this tool shed, which is something like:
- http://toolshed.g2.bx.psu.edu/
+ The value of tool_shed is something like: toolshed.g2.bx.psu.edu. We need the URL to this tool shed, which is
+ something like: http://toolshed.g2.bx.psu.edu/
"""
for shed_name, shed_url in app.tool_shed_registry.tool_sheds.items():
if shed_url.find( tool_shed ) >= 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
commit/galaxy-central: martenson: better handling the JS modal window event binding
by commits-noreply@bitbucket.org 03 Jan '14
by commits-noreply@bitbucket.org 03 Jan '14
03 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/10b7c04f919f/
Changeset: 10b7c04f919f
User: martenson
Date: 2014-01-03 19:40:08
Summary: better handling the JS modal window event binding
Affected #: 4 files
diff -r e92e13e9c103cc1f36dff65e1523479bf5cb17ed -r 10b7c04f919ff6d7fc1d03ba88ebb282645fcbba static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -569,6 +569,7 @@
'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
}
});
+ self.modal.bindEvents(event);
// show the prepared modal
self.modal.show();
}
@@ -577,7 +578,7 @@
// import all selected datasets into history
importAllIntoHistory : function (){
- //disable the button
+ //disable the button to rprevent multiple submission
this.modal.disableButton('Import');
var history_id = $("select[name=dataset_import_bulk] option:selected").val();
@@ -617,9 +618,9 @@
var popped_item = history_item_set.pop();
if (typeof popped_item === "undefined") {
mod_toastr.success('All datasets imported');
- this.modal.hide();
- // enable button again
- self.modal.enableButton('Import');
+
+ //this will destroy other modals too - including those hidden!!!
+ this.modal.destroy();
return
}
var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source})).done(function(a1){
diff -r e92e13e9c103cc1f36dff65e1523479bf5cb17ed -r 10b7c04f919ff6d7fc1d03ba88ebb282645fcbba static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -25,20 +25,18 @@
self = this;
if (options)
this.create(options);
-
- // Bind the hiding events
- // this.bindEvents(event, self);
},
// bind the click-to-hide function
- bindEvents: function(event, that) {
+ bindEvents: function(event) {
// bind the ESC key to hide() function
$(document).on('keyup', function(event){
- if (event.keyCode == 27) { self.hide(); }
+ if (event.keyCode == 27) { self.hide(); $('.modal').remove();}
})
// bind the 'click anywhere' to hide() function...
$('html').on('click', function(event){
self.hide();
+ $('.modal').remove();
})
// ...but don't hide if the click is on modal content
$('.modal-content').on('click', function(event){
@@ -50,11 +48,12 @@
unbindEvents: function(event){
// bind the ESC key to hide() function
$(document).off('keyup', function(event){
- if (event.keyCode == 27) { self.hide(); }
+ if (event.keyCode == 27) { self.hide(); $('.modal').remove();}
})
// unbind the 'click anywhere' to hide() function...
$('html').off('click', function(event){
self.hide();
+ $('.modal').remove();
})
$('.modal-content').off('click', function(event){
event.stopPropagation();
diff -r e92e13e9c103cc1f36dff65e1523479bf5cb17ed -r 10b7c04f919ff6d7fc1d03ba88ebb282645fcbba static/scripts/packed/galaxy.library.js
--- a/static/scripts/packed/galaxy.library.js
+++ b/static/scripts/packed/galaxy.library.js
@@ -1,1 +1,1 @@
-var view=null;var library_router=null;var responses=[];define(["galaxy.modal","galaxy.masthead","utils/galaxy.utils","libs/toastr"],function(j,k,g,m){var e=Backbone.Model.extend({urlRoot:"/api/libraries"});var n=Backbone.Collection.extend({url:"/api/libraries",model:e});var h=Backbone.Model.extend({urlRoot:"/api/libraries/datasets"});var c=Backbone.Collection.extend({model:h});var d=Backbone.Model.extend({defaults:{folder:new c(),full_path:"unknown",urlRoot:"/api/folders/",id:"unknown"},parse:function(q){this.full_path=q[0].full_path;this.get("folder").reset(q[1].folder_contents);return q}});var b=Backbone.Model.extend({urlRoot:"/api/histories/"});var i=Backbone.Model.extend({url:"/api/histories/"});var o=Backbone.Collection.extend({url:"/api/histories",model:i});var p=Backbone.Router.extend({routes:{"":"libraries","folders/:id":"folder_content","folders/:folder_id/download/:format":"download"}});var l=Backbone.View.extend({el:"#center",progress:0,progressStep:1,lastSelectedHistory:"",modal:null,folders:null,initialize:function(){this.folders=[];this.queue=jQuery.Deferred();this.queue.resolve()},templateFolder:function(){var q=[];q.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');q.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');q.push('<div id="library_folder_toolbar" >');q.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-plus"></span><span class="fa fa-folder-close"></span> folder</button>');q.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-external-link"></span> to history</button>');q.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');q.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');q.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');q.push(" </button>");q.push(' <ul class="dropdown-menu" role="menu">');q.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');q.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');q.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');q.push(" </ul>");q.push(" </div>");q.push("</div>");q.push('<div class="library_breadcrumb">');q.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');q.push("<% _.each(path, function(path_item) { %>");q.push("<% if (path_item[0] != id) { %>");q.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');q.push("<% } else { %>");q.push('<span title="You are in this folder"><%- path_item[1] %></span>');q.push("<% } %>");q.push("<% }); %>");q.push("</div>");q.push('<table id="folder_table" class="table table-condensed">');q.push(" <thead>");q.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');q.push(' <th class="button_heading">view</th>');q.push(" <th>name</th>");q.push(" <th>data type</th>");q.push(" <th>size</th>");q.push(" <th>date</th>");q.push(" </thead>");q.push(" <tbody>");q.push(" <td></td>");q.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');q.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');q.push(" <td></td>");q.push(" <td></td>");q.push(" <td></td>");q.push(" <td></td>");q.push(" </tr>");q.push(" <% _.each(items, function(content_item) { %>");q.push(' <tr class="folder_row light" id="<%- content_item.id %>">');q.push(' <% if (content_item.get("type") === "folder") { %>');q.push(" <td></td>");q.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');q.push(' <span class="fa fa-folder-open"></span> browse</td>');q.push(' <td><%- content_item.get("name") %>');q.push(' <% if (content_item.get("item_count") === 0) { %>');q.push(' <span class="muted">(empty folder)</span>');q.push(" <% } %>");q.push(" </td>");q.push(" <td>folder</td>");q.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>');q.push(" <% } else { %>");q.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');q.push(" <td>");q.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');q.push(' <span class="fa fa-eye"></span> details');q.push(" </button>");q.push(" </td>");q.push(' <td><%- content_item.get("name") %></td>');q.push(' <td><%= _.escape(content_item.get("data_type")) %></td>');q.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>');q.push(" <% } %> ");q.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>');q.push(" </tr>");q.push(" <% }); %>");q.push(" ");q.push(" </tbody>");q.push("</table>");q.push("</div>");return q.join("")},templateDatasetModal:function(){var q=[];q.push('<div id="dataset_info_modal">');q.push(' <table class="table table-striped table-condensed">');q.push(" <tr>");q.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');q.push(' <td><%= _.escape(item.get("name")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Data type</th>');q.push(' <td><%= _.escape(item.get("data_type")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Genome build</th>');q.push(' <td><%= _.escape(item.get("genome_build")) %></td>');q.push(" </tr>");q.push(' <th scope="row">Size</th>');q.push(" <td><%= _.escape(size) %></td>");q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Date uploaded</th>');q.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Uploaded by</th>');q.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');q.push(" </tr>");q.push(' <tr scope="row">');q.push(' <th scope="row">Data Lines</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');q.push(" </tr>");q.push(' <th scope="row">Comment Lines</th>');q.push(' <% if (item.get("metadata_comment_lines") === "") { %>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');q.push(" <% } else { %>");q.push(' <td scope="row">unknown</td>');q.push(" <% } %>");q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Number of Columns</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Column Types</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Miscellaneous information</th>');q.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');q.push(" </tr>");q.push(" </table>");q.push(' <pre class="peek">');q.push(" </pre>");q.push("</div>");return q.join("")},templateHistorySelectInModal:function(){var q=[];q.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');q.push("Select history: ");q.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');q.push(" <% _.each(histories, function(history) { %>");q.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');q.push(" <% }); %>");q.push("</select>");q.push("</span>");return q.join("")},templateBulkImportInModal:function(){var q=[];q.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');q.push("Select history: ");q.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');q.push(" <% _.each(histories, function(history) { %>");q.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');q.push(" <% }); %>");q.push("</select>");q.push("</span>");return q.join("")},size_to_string:function(q){var r="";if(q>=100000000000){q=q/100000000000;r="TB"}else{if(q>=100000000){q=q/100000000;r="GB"}else{if(q>=100000){q=q/100000;r="MB"}else{if(q>=100){q=q/100;r="KB"}else{q=q*10;r="b"}}}}return(Math.round(q)/10)+r},events:{"click #select-all-checkboxes":"selectAll","click .folder_row":"selectClickedRow","click #toolbtn_bulk_import":"modalBulkImport","click #toolbtn_dl":"bulkDownload","click .library-dataset":"showDatasetDetails","click #toolbtn_create_folder":"createFolderModal","click .btn_open_folder":"navigateToFolder"},render:function(q){$("#center").css("overflow","auto");view=this;var s=this;var r=new d({id:q.id});r.url=r.attributes.urlRoot+q.id+"/contents";r.fetch({success:function(t){for(var v=0;v<r.attributes.folder.models.length;v++){var u=r.attributes.folder.models[v];if(u.get("type")==="file"){u.set("readable_size",s.size_to_string(u.get("file_size")))}}var x=r.full_path;var y;if(x.length===1){y=0}else{y=x[x.length-2][0]}var w=_.template(s.templateFolder(),{path:r.full_path,items:r.attributes.folder.models,id:q.id,upper_folder_id:y});s.$el.html(w)}})},navigateToFolder:function(r){var q=$(r.target).attr("data-id");if(typeof q==="undefined"){return false}else{if(q==="0"){library_router.navigate("#",{trigger:true,replace:true})}else{library_router.navigate("folders/"+q,{trigger:true,replace:true})}}},showDatasetDetails:function(t){t.preventDefault();var u=$(t.target).parent().parent().attr("id");var s=new h();var r=new o();s.id=u;var q=this;s.fetch({success:function(v){r.fetch({success:function(w){q.renderModalAfterFetch(v,w)}})}})},renderModalAfterFetch:function(v,s){var t=this.size_to_string(v.get("file_size"));var u=_.template(this.templateDatasetModal(),{item:v,size:t});this.modal=null;var r=this;this.modal=new j.GalaxyModal({title:"Dataset Details",body:u,buttons:{Import:function(){r.importCurrentIntoHistory()},Download:function(){r.downloadCurrent()},Close:function(){r.modal.hide();$(".modal").remove();r.modal=null}}});this.modal.bindEvents(event);$(".peek").html(v.get("peek"));var q=_.template(this.templateHistorySelectInModal(),{histories:s.models});$(this.modal.elMain).find(".buttons").prepend(q);if(r.lastSelectedHistory.length>0){$(this.modal.elMain).find("#dataset_import_single").val(r.lastSelectedHistory)}this.modal.show()},downloadCurrent:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var q=[];q.push($("#id_row").attr("data-id"));var r="/api/libraries/datasets/download/uncompressed";var s={ldda_ids:q};folderContentView.processDownload(r,s);this.modal.enableButton("Import");this.modal.enableButton("Download")},importCurrentIntoHistory:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var s=$(this.modal.elMain).find("select[name=dataset_import_single] option:selected").val();this.lastSelectedHistory=s;var q=$("#id_row").attr("data-id");var t=new b();var r=this;t.url=t.urlRoot+s+"/contents";t.save({content:q,source:"library"},{success:function(){m.success("Dataset imported");r.modal.enableButton("Import");r.modal.enableButton("Download")},error:function(){m.error("An error occured! Dataset not imported. Please try again.");r.modal.enableButton("Import");r.modal.enableButton("Download")}})},selectAll:function(r){var q=r.target.checked;that=this;$(":checkbox").each(function(){this.checked=q;$row=$(this.parentElement.parentElement);(q)?that.makeDarkRow($row):that.makeWhiteRow($row)});this.checkTools()},selectClickedRow:function(r){var t="";var q;var s;if(r.target.localName==="input"){t=r.target;q=$(r.target.parentElement.parentElement);s="input"}else{if(r.target.localName==="td"){t=$("#"+r.target.parentElement.id).find(":checkbox")[0];q=$(r.target.parentElement);s="td"}}if(t===""){r.stopPropagation();return}if(t.checked){if(s==="td"){t.checked="";this.makeWhiteRow(q)}else{if(s==="input"){this.makeDarkRow(q)}}}else{if(s==="td"){t.checked="selected";this.makeDarkRow(q)}else{if(s==="input"){this.makeWhiteRow(q)}}}this.checkTools()},makeDarkRow:function(q){q.removeClass("light");q.find("a").removeClass("light");q.addClass("dark");q.find("a").addClass("dark")},makeWhiteRow:function(q){q.removeClass("dark");q.find("a").removeClass("dark");q.addClass("light");q.find("a").addClass("light")},checkTools:function(){var q=$("#folder_table").find(":checked");if(q.length>0){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}else{$("#toolbtn_bulk_import").hide();$("#toolbtn_dl").hide()}},modalBulkImport:function(){var r=this;var q=new o();q.fetch({success:function(s){var t=_.template(r.templateBulkImportInModal(),{histories:s.models});r.modal=new j.GalaxyModal({title:"Import into History",body:t,buttons:{Import:function(){r.importAllIntoHistory()},Close:function(){r.modal.hide();$(".modal").remove();r.modal=null}}});r.modal.show()}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var s=$("select[name=dataset_import_bulk] option:selected").val();var w=$("select[name=dataset_import_bulk] option:selected").text();var y=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){y.push(this.parentElement.parentElement.id)}});var x=_.template(this.templateProgressBar(),{history_name:w});$(this.modal.elMain).find(".modal-body").html(x);var t=100/y.length;this.initProgress(t);var q=[];for(var r=y.length-1;r>=0;r--){library_dataset_id=y[r];var u=new b();var v=this;u.url=u.urlRoot+s+"/contents";u.content=library_dataset_id;u.source="library";q.push(u)}this.chainCall(q)},chainCall:function(r){var q=this;var s=r.pop();if(typeof s==="undefined"){m.success("All datasets imported");this.modal.hide();q.modal.enableButton("Import");return}var t=$.when(s.save({content:s.content,source:s.source})).done(function(u){q.updateProgress();responses.push(u);q.chainCall(r)})},initProgress:function(q){this.progress=0;this.progressStep=q},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},templateProgressBar:function(){var q=[];q.push('<div class="import_text">');q.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");q.push("</div>");q.push('<div class="progress">');q.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');q.push(' <span class="completion_span">0% Complete</span>');q.push(" </div>");q.push("</div>");q.push("");return q.join("")},download:function(q,u){var s=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){s.push(this.parentElement.parentElement.id)}});var r="/api/libraries/datasets/download/"+u;var t={ldda_ids:s};this.processDownload(r,t,"get")},processDownload:function(r,s,t){if(r&&s){s=typeof s=="string"?s:$.param(s);var q="";$.each(s.split("&"),function(){var u=this.split("=");q+='<input type="hidden" name="'+u[0]+'" value="'+u[1]+'" />'});$('<form action="'+r+'" method="'+(t||"post")+'">'+q+"</form>").appendTo("body").submit().remove();m.info("Your download will begin soon")}},createFolderModal:function(){m.info("This will create folder...in the future")}});var a=Backbone.View.extend({el:"#center",events:{"click #create_new_library_btn":"show_library_modal"},initialize:function(){},template_library_list:function(){tmpl_array=[];tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');tmpl_array.push("");tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary file ">New Library</a>');tmpl_array.push('<table class="table table-condensed">');tmpl_array.push(" <thead>");tmpl_array.push(' <th class="button_heading"></th>');tmpl_array.push(" <th>name</th>");tmpl_array.push(" <th>description</th>");tmpl_array.push(" <th>synopsis</th> ");tmpl_array.push(" <th>model type</th> ");tmpl_array.push(" </thead>");tmpl_array.push(" <tbody>");tmpl_array.push(" <% _.each(libraries, function(library) { %>");tmpl_array.push(" <tr>");tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');tmpl_array.push(' <td><%- library.get("name") %></td>');tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');tmpl_array.push(" </tr>");tmpl_array.push(" <% }); %>");tmpl_array.push(" </tbody>");tmpl_array.push("</table>");tmpl_array.push("</div>");return tmpl_array.join("")},render:function(){$("#center").css("overflow","auto");var q=this;libraries=new n();libraries.fetch({success:function(r){var s=_.template(q.template_library_list(),{libraries:r.models});q.$el.html(s)},error:function(s,r){if(r.statusCode().status===403){m.error("Please log in first. Redirecting to login page in 3s.");setTimeout(q.redirectToLogin,3000)}else{m.error("An error occured. Please try again.")}}})},redirectToHome:function(){window.location="../"},redirectToLogin:function(){window.location="/user/login"},modal:null,show_library_modal:function(r){r.preventDefault();r.stopPropagation();var q=this;this.modal=new j.GalaxyModal({title:"Create New Library",body:this.template_new_library(),buttons:{Create:function(){q.create_new_library_event()},Close:function(){q.modal.hide()}}});this.modal.show()},create_new_library_event:function(){var s=this.serialize_new_library();if(this.validate_new_library(s)){var r=new e();var q=this;r.save(s,{success:function(t){q.modal.hide();q.clear_library_modal();q.render();m.success("Library created")},error:function(){m.error("An error occured :(")}})}else{m.error("Library's name is missing")}return false},clear_library_modal:function(){$("input[name='Name']").val("");$("input[name='Description']").val("");$("input[name='Synopsis']").val("")},serialize_new_library:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val(),synopsis:$("input[name='Synopsis']").val()}},validate_new_library:function(q){return q.name!==""},template_new_library:function(){tmpl_array=[];tmpl_array.push('<div id="new_library_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var f=Backbone.View.extend({folderContentView:null,galaxyLibraryview:null,initialize:function(){folderContentView=new l();galaxyLibraryview=new a();library_router=new p();library_router.on("route:libraries",function(){galaxyLibraryview.render()});library_router.on("route:folder_content",function(q){folderContentView.render({id:q})});library_router.on("route:download",function(q,r){if($("#center").find(":checked").length===0){library_router.navigate("folders/"+q,{trigger:true,replace:true})}else{folderContentView.download(q,r);library_router.navigate("folders/"+q,{trigger:false,replace:true})}});Backbone.history.start();return this}});return{GalaxyApp:f}});
\ No newline at end of file
+var view=null;var library_router=null;var responses=[];define(["galaxy.modal","galaxy.masthead","utils/galaxy.utils","libs/toastr"],function(j,k,g,m){var e=Backbone.Model.extend({urlRoot:"/api/libraries"});var n=Backbone.Collection.extend({url:"/api/libraries",model:e});var h=Backbone.Model.extend({urlRoot:"/api/libraries/datasets"});var c=Backbone.Collection.extend({model:h});var d=Backbone.Model.extend({defaults:{folder:new c(),full_path:"unknown",urlRoot:"/api/folders/",id:"unknown"},parse:function(q){this.full_path=q[0].full_path;this.get("folder").reset(q[1].folder_contents);return q}});var b=Backbone.Model.extend({urlRoot:"/api/histories/"});var i=Backbone.Model.extend({url:"/api/histories/"});var o=Backbone.Collection.extend({url:"/api/histories",model:i});var p=Backbone.Router.extend({routes:{"":"libraries","folders/:id":"folder_content","folders/:folder_id/download/:format":"download"}});var l=Backbone.View.extend({el:"#center",progress:0,progressStep:1,lastSelectedHistory:"",modal:null,folders:null,initialize:function(){this.folders=[];this.queue=jQuery.Deferred();this.queue.resolve()},templateFolder:function(){var q=[];q.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');q.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');q.push('<div id="library_folder_toolbar" >');q.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-plus"></span><span class="fa fa-folder-close"></span> folder</button>');q.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-external-link"></span> to history</button>');q.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');q.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');q.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');q.push(" </button>");q.push(' <ul class="dropdown-menu" role="menu">');q.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');q.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');q.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');q.push(" </ul>");q.push(" </div>");q.push("</div>");q.push('<div class="library_breadcrumb">');q.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');q.push("<% _.each(path, function(path_item) { %>");q.push("<% if (path_item[0] != id) { %>");q.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');q.push("<% } else { %>");q.push('<span title="You are in this folder"><%- path_item[1] %></span>');q.push("<% } %>");q.push("<% }); %>");q.push("</div>");q.push('<table id="folder_table" class="table table-condensed">');q.push(" <thead>");q.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');q.push(' <th class="button_heading">view</th>');q.push(" <th>name</th>");q.push(" <th>data type</th>");q.push(" <th>size</th>");q.push(" <th>date</th>");q.push(" </thead>");q.push(" <tbody>");q.push(" <td></td>");q.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');q.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');q.push(" <td></td>");q.push(" <td></td>");q.push(" <td></td>");q.push(" <td></td>");q.push(" </tr>");q.push(" <% _.each(items, function(content_item) { %>");q.push(' <tr class="folder_row light" id="<%- content_item.id %>">');q.push(' <% if (content_item.get("type") === "folder") { %>');q.push(" <td></td>");q.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');q.push(' <span class="fa fa-folder-open"></span> browse</td>');q.push(' <td><%- content_item.get("name") %>');q.push(' <% if (content_item.get("item_count") === 0) { %>');q.push(' <span class="muted">(empty folder)</span>');q.push(" <% } %>");q.push(" </td>");q.push(" <td>folder</td>");q.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>');q.push(" <% } else { %>");q.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');q.push(" <td>");q.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');q.push(' <span class="fa fa-eye"></span> details');q.push(" </button>");q.push(" </td>");q.push(' <td><%- content_item.get("name") %></td>');q.push(' <td><%= _.escape(content_item.get("data_type")) %></td>');q.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>');q.push(" <% } %> ");q.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>');q.push(" </tr>");q.push(" <% }); %>");q.push(" ");q.push(" </tbody>");q.push("</table>");q.push("</div>");return q.join("")},templateDatasetModal:function(){var q=[];q.push('<div id="dataset_info_modal">');q.push(' <table class="table table-striped table-condensed">');q.push(" <tr>");q.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');q.push(' <td><%= _.escape(item.get("name")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Data type</th>');q.push(' <td><%= _.escape(item.get("data_type")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Genome build</th>');q.push(' <td><%= _.escape(item.get("genome_build")) %></td>');q.push(" </tr>");q.push(' <th scope="row">Size</th>');q.push(" <td><%= _.escape(size) %></td>");q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Date uploaded</th>');q.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Uploaded by</th>');q.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');q.push(" </tr>");q.push(' <tr scope="row">');q.push(' <th scope="row">Data Lines</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');q.push(" </tr>");q.push(' <th scope="row">Comment Lines</th>');q.push(' <% if (item.get("metadata_comment_lines") === "") { %>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');q.push(" <% } else { %>");q.push(' <td scope="row">unknown</td>');q.push(" <% } %>");q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Number of Columns</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Column Types</th>');q.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');q.push(" </tr>");q.push(" <tr>");q.push(' <th scope="row">Miscellaneous information</th>');q.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');q.push(" </tr>");q.push(" </table>");q.push(' <pre class="peek">');q.push(" </pre>");q.push("</div>");return q.join("")},templateHistorySelectInModal:function(){var q=[];q.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');q.push("Select history: ");q.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');q.push(" <% _.each(histories, function(history) { %>");q.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');q.push(" <% }); %>");q.push("</select>");q.push("</span>");return q.join("")},templateBulkImportInModal:function(){var q=[];q.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');q.push("Select history: ");q.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');q.push(" <% _.each(histories, function(history) { %>");q.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');q.push(" <% }); %>");q.push("</select>");q.push("</span>");return q.join("")},size_to_string:function(q){var r="";if(q>=100000000000){q=q/100000000000;r="TB"}else{if(q>=100000000){q=q/100000000;r="GB"}else{if(q>=100000){q=q/100000;r="MB"}else{if(q>=100){q=q/100;r="KB"}else{q=q*10;r="b"}}}}return(Math.round(q)/10)+r},events:{"click #select-all-checkboxes":"selectAll","click .folder_row":"selectClickedRow","click #toolbtn_bulk_import":"modalBulkImport","click #toolbtn_dl":"bulkDownload","click .library-dataset":"showDatasetDetails","click #toolbtn_create_folder":"createFolderModal","click .btn_open_folder":"navigateToFolder"},render:function(q){$("#center").css("overflow","auto");view=this;var s=this;var r=new d({id:q.id});r.url=r.attributes.urlRoot+q.id+"/contents";r.fetch({success:function(t){for(var v=0;v<r.attributes.folder.models.length;v++){var u=r.attributes.folder.models[v];if(u.get("type")==="file"){u.set("readable_size",s.size_to_string(u.get("file_size")))}}var x=r.full_path;var y;if(x.length===1){y=0}else{y=x[x.length-2][0]}var w=_.template(s.templateFolder(),{path:r.full_path,items:r.attributes.folder.models,id:q.id,upper_folder_id:y});s.$el.html(w)}})},navigateToFolder:function(r){var q=$(r.target).attr("data-id");if(typeof q==="undefined"){return false}else{if(q==="0"){library_router.navigate("#",{trigger:true,replace:true})}else{library_router.navigate("folders/"+q,{trigger:true,replace:true})}}},showDatasetDetails:function(t){t.preventDefault();var u=$(t.target).parent().parent().attr("id");var s=new h();var r=new o();s.id=u;var q=this;s.fetch({success:function(v){r.fetch({success:function(w){q.renderModalAfterFetch(v,w)}})}})},renderModalAfterFetch:function(v,s){var t=this.size_to_string(v.get("file_size"));var u=_.template(this.templateDatasetModal(),{item:v,size:t});this.modal=null;var r=this;this.modal=new j.GalaxyModal({title:"Dataset Details",body:u,buttons:{Import:function(){r.importCurrentIntoHistory()},Download:function(){r.downloadCurrent()},Close:function(){r.modal.hide();$(".modal").remove();r.modal=null}}});this.modal.bindEvents(event);$(".peek").html(v.get("peek"));var q=_.template(this.templateHistorySelectInModal(),{histories:s.models});$(this.modal.elMain).find(".buttons").prepend(q);if(r.lastSelectedHistory.length>0){$(this.modal.elMain).find("#dataset_import_single").val(r.lastSelectedHistory)}this.modal.show()},downloadCurrent:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var q=[];q.push($("#id_row").attr("data-id"));var r="/api/libraries/datasets/download/uncompressed";var s={ldda_ids:q};folderContentView.processDownload(r,s);this.modal.enableButton("Import");this.modal.enableButton("Download")},importCurrentIntoHistory:function(){this.modal.disableButton("Import");this.modal.disableButton("Download");var s=$(this.modal.elMain).find("select[name=dataset_import_single] option:selected").val();this.lastSelectedHistory=s;var q=$("#id_row").attr("data-id");var t=new b();var r=this;t.url=t.urlRoot+s+"/contents";t.save({content:q,source:"library"},{success:function(){m.success("Dataset imported");r.modal.enableButton("Import");r.modal.enableButton("Download")},error:function(){m.error("An error occured! Dataset not imported. Please try again.");r.modal.enableButton("Import");r.modal.enableButton("Download")}})},selectAll:function(r){var q=r.target.checked;that=this;$(":checkbox").each(function(){this.checked=q;$row=$(this.parentElement.parentElement);(q)?that.makeDarkRow($row):that.makeWhiteRow($row)});this.checkTools()},selectClickedRow:function(r){var t="";var q;var s;if(r.target.localName==="input"){t=r.target;q=$(r.target.parentElement.parentElement);s="input"}else{if(r.target.localName==="td"){t=$("#"+r.target.parentElement.id).find(":checkbox")[0];q=$(r.target.parentElement);s="td"}}if(t===""){r.stopPropagation();return}if(t.checked){if(s==="td"){t.checked="";this.makeWhiteRow(q)}else{if(s==="input"){this.makeDarkRow(q)}}}else{if(s==="td"){t.checked="selected";this.makeDarkRow(q)}else{if(s==="input"){this.makeWhiteRow(q)}}}this.checkTools()},makeDarkRow:function(q){q.removeClass("light");q.find("a").removeClass("light");q.addClass("dark");q.find("a").addClass("dark")},makeWhiteRow:function(q){q.removeClass("dark");q.find("a").removeClass("dark");q.addClass("light");q.find("a").addClass("light")},checkTools:function(){var q=$("#folder_table").find(":checked");if(q.length>0){$("#toolbtn_bulk_import").show();$("#toolbtn_dl").show()}else{$("#toolbtn_bulk_import").hide();$("#toolbtn_dl").hide()}},modalBulkImport:function(){var r=this;var q=new o();q.fetch({success:function(s){var t=_.template(r.templateBulkImportInModal(),{histories:s.models});r.modal=new j.GalaxyModal({title:"Import into History",body:t,buttons:{Import:function(){r.importAllIntoHistory()},Close:function(){r.modal.hide();$(".modal").remove();r.modal=null}}});r.modal.bindEvents(event);r.modal.show()}})},importAllIntoHistory:function(){this.modal.disableButton("Import");var s=$("select[name=dataset_import_bulk] option:selected").val();var w=$("select[name=dataset_import_bulk] option:selected").text();var y=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){y.push(this.parentElement.parentElement.id)}});var x=_.template(this.templateProgressBar(),{history_name:w});$(this.modal.elMain).find(".modal-body").html(x);var t=100/y.length;this.initProgress(t);var q=[];for(var r=y.length-1;r>=0;r--){library_dataset_id=y[r];var u=new b();var v=this;u.url=u.urlRoot+s+"/contents";u.content=library_dataset_id;u.source="library";q.push(u)}this.chainCall(q)},chainCall:function(r){var q=this;var s=r.pop();if(typeof s==="undefined"){m.success("All datasets imported");this.modal.destroy();return}var t=$.when(s.save({content:s.content,source:s.source})).done(function(u){q.updateProgress();responses.push(u);q.chainCall(r)})},initProgress:function(q){this.progress=0;this.progressStep=q},updateProgress:function(){this.progress+=this.progressStep;$(".progress-bar-import").width(Math.round(this.progress)+"%");txt_representation=Math.round(this.progress)+"% Complete";$(".completion_span").text(txt_representation)},templateProgressBar:function(){var q=[];q.push('<div class="import_text">');q.push("Importing selected datasets to history <b><%= _.escape(history_name) %></b>");q.push("</div>");q.push('<div class="progress">');q.push(' <div class="progress-bar progress-bar-import" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');q.push(' <span class="completion_span">0% Complete</span>');q.push(" </div>");q.push("</div>");q.push("");return q.join("")},download:function(q,u){var s=[];$("#folder_table").find(":checked").each(function(){if(this.parentElement.parentElement.id!=""){s.push(this.parentElement.parentElement.id)}});var r="/api/libraries/datasets/download/"+u;var t={ldda_ids:s};this.processDownload(r,t,"get")},processDownload:function(r,s,t){if(r&&s){s=typeof s=="string"?s:$.param(s);var q="";$.each(s.split("&"),function(){var u=this.split("=");q+='<input type="hidden" name="'+u[0]+'" value="'+u[1]+'" />'});$('<form action="'+r+'" method="'+(t||"post")+'">'+q+"</form>").appendTo("body").submit().remove();m.info("Your download will begin soon")}},createFolderModal:function(){m.info("This will create folder...in the future")}});var a=Backbone.View.extend({el:"#center",events:{"click #create_new_library_btn":"show_library_modal"},initialize:function(){},template_library_list:function(){tmpl_array=[];tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');tmpl_array.push("");tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary file ">New Library</a>');tmpl_array.push('<table class="table table-condensed">');tmpl_array.push(" <thead>");tmpl_array.push(' <th class="button_heading"></th>');tmpl_array.push(" <th>name</th>");tmpl_array.push(" <th>description</th>");tmpl_array.push(" <th>synopsis</th> ");tmpl_array.push(" <th>model type</th> ");tmpl_array.push(" </thead>");tmpl_array.push(" <tbody>");tmpl_array.push(" <% _.each(libraries, function(library) { %>");tmpl_array.push(" <tr>");tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');tmpl_array.push(' <td><%- library.get("name") %></td>');tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');tmpl_array.push(" </tr>");tmpl_array.push(" <% }); %>");tmpl_array.push(" </tbody>");tmpl_array.push("</table>");tmpl_array.push("</div>");return tmpl_array.join("")},render:function(){$("#center").css("overflow","auto");var q=this;libraries=new n();libraries.fetch({success:function(r){var s=_.template(q.template_library_list(),{libraries:r.models});q.$el.html(s)},error:function(s,r){if(r.statusCode().status===403){m.error("Please log in first. Redirecting to login page in 3s.");setTimeout(q.redirectToLogin,3000)}else{m.error("An error occured. Please try again.")}}})},redirectToHome:function(){window.location="../"},redirectToLogin:function(){window.location="/user/login"},modal:null,show_library_modal:function(r){r.preventDefault();r.stopPropagation();var q=this;this.modal=new j.GalaxyModal({title:"Create New Library",body:this.template_new_library(),buttons:{Create:function(){q.create_new_library_event()},Close:function(){q.modal.hide()}}});this.modal.show()},create_new_library_event:function(){var s=this.serialize_new_library();if(this.validate_new_library(s)){var r=new e();var q=this;r.save(s,{success:function(t){q.modal.hide();q.clear_library_modal();q.render();m.success("Library created")},error:function(){m.error("An error occured :(")}})}else{m.error("Library's name is missing")}return false},clear_library_modal:function(){$("input[name='Name']").val("");$("input[name='Description']").val("");$("input[name='Synopsis']").val("")},serialize_new_library:function(){return{name:$("input[name='Name']").val(),description:$("input[name='Description']").val(),synopsis:$("input[name='Synopsis']").val()}},validate_new_library:function(q){return q.name!==""},template_new_library:function(){tmpl_array=[];tmpl_array.push('<div id="new_library_modal">');tmpl_array.push("<form>");tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');tmpl_array.push("</form>");tmpl_array.push("</div>");return tmpl_array.join("")}});var f=Backbone.View.extend({folderContentView:null,galaxyLibraryview:null,initialize:function(){folderContentView=new l();galaxyLibraryview=new a();library_router=new p();library_router.on("route:libraries",function(){galaxyLibraryview.render()});library_router.on("route:folder_content",function(q){folderContentView.render({id:q})});library_router.on("route:download",function(q,r){if($("#center").find(":checked").length===0){library_router.navigate("folders/"+q,{trigger:true,replace:true})}else{folderContentView.download(q,r);library_router.navigate("folders/"+q,{trigger:false,replace:true})}});Backbone.history.start();return this}});return{GalaxyApp:f}});
\ No newline at end of file
diff -r e92e13e9c103cc1f36dff65e1523479bf5cb17ed -r 10b7c04f919ff6d7fc1d03ba88ebb282645fcbba static/scripts/packed/galaxy.modal.js
--- a/static/scripts/packed/galaxy.modal.js
+++ b/static/scripts/packed/galaxy.modal.js
@@ -1,1 +1,1 @@
-define([],function(){var a=Backbone.View.extend({elMain:"#everything",optionsDefault:{title:"galaxy-modal",body:"",backdrop:true,height:null,width:null},options:{},initialize:function(b){self=this;if(b){this.create(b)}},bindEvents:function(c,b){$(document).on("keyup",function(d){if(d.keyCode==27){self.hide()}});$("html").on("click",function(d){self.hide()});$(".modal-content").on("click",function(d){d.stopPropagation()})},unbindEvents:function(b){$(document).off("keyup",function(c){if(c.keyCode==27){self.hide()}});$("html").off("click",function(c){self.hide()});$(".modal-content").off("click",function(c){c.stopPropagation()})},destroy:function(){this.hide();this.unbindEvents();$(".modal").remove()},show:function(b){this.initialize(b);if(this.options.height){this.$body.css("height",this.options.height);this.$body.css("overflow","hidden")}else{this.$body.css("max-height",$(window).height()/2)}if(this.options.width){this.$dialog.css("width",this.options.width)}if(this.visible){this.$el.show()}else{this.$el.fadeIn("fast")}this.visible=true},hide:function(){this.$el.fadeOut("fast");this.visible=false;this.unbindEvents()},create:function(c){this.options=_.defaults(c,this.optionsDefault);if(this.options.body=="progress"){this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')}if(this.$el){this.$el.remove()}this.setElement(this.template(this.options.title));this.$dialog=(this.$el).find(".modal-dialog");this.$body=(this.$el).find(".modal-body");this.$footer=(this.$el).find(".modal-footer");this.$buttons=(this.$el).find(".buttons");this.$backdrop=(this.$el).find(".modal-backdrop");this.$body.html(this.options.body);if(!this.options.backdrop){this.$backdrop.removeClass("in")}if(this.options.buttons){var b=this;$.each(this.options.buttons,function(d,e){b.$buttons.append($('<button id="'+String(d).toLowerCase()+'"></button>').text(d).click(e)).append(" ")})}else{this.$footer.hide()}$(this.elMain).append($(this.el))},enableButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).prop("disabled",false)},disableButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).prop("disabled",true)},hideButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).hide()},showButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).show()},scrollTop:function(){return this.$body.scrollTop()},template:function(b){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+b+'</h4></div><div class="modal-body"></div><div class="modal-footer"><div class="buttons" style="float: right;"></div></div></div</div></div>'}});return{GalaxyModal:a}});
\ No newline at end of file
+define([],function(){var a=Backbone.View.extend({elMain:"#everything",optionsDefault:{title:"galaxy-modal",body:"",backdrop:true,height:null,width:null},options:{},initialize:function(b){self=this;if(b){this.create(b)}},bindEvents:function(b){$(document).on("keyup",function(c){if(c.keyCode==27){self.hide();$(".modal").remove()}});$("html").on("click",function(c){self.hide();$(".modal").remove()});$(".modal-content").on("click",function(c){c.stopPropagation()})},unbindEvents:function(b){$(document).off("keyup",function(c){if(c.keyCode==27){self.hide();$(".modal").remove()}});$("html").off("click",function(c){self.hide();$(".modal").remove()});$(".modal-content").off("click",function(c){c.stopPropagation()})},destroy:function(){this.hide();this.unbindEvents();$(".modal").remove()},show:function(b){this.initialize(b);if(this.options.height){this.$body.css("height",this.options.height);this.$body.css("overflow","hidden")}else{this.$body.css("max-height",$(window).height()/2)}if(this.options.width){this.$dialog.css("width",this.options.width)}if(this.visible){this.$el.show()}else{this.$el.fadeIn("fast")}this.visible=true},hide:function(){this.$el.fadeOut("fast");this.visible=false;this.unbindEvents()},create:function(c){this.options=_.defaults(c,this.optionsDefault);if(this.options.body=="progress"){this.options.body=$('<div class="progress progress-striped active"><div class="progress-bar progress-bar-info" style="width:100%"></div></div>')}if(this.$el){this.$el.remove()}this.setElement(this.template(this.options.title));this.$dialog=(this.$el).find(".modal-dialog");this.$body=(this.$el).find(".modal-body");this.$footer=(this.$el).find(".modal-footer");this.$buttons=(this.$el).find(".buttons");this.$backdrop=(this.$el).find(".modal-backdrop");this.$body.html(this.options.body);if(!this.options.backdrop){this.$backdrop.removeClass("in")}if(this.options.buttons){var b=this;$.each(this.options.buttons,function(d,e){b.$buttons.append($('<button id="'+String(d).toLowerCase()+'"></button>').text(d).click(e)).append(" ")})}else{this.$footer.hide()}$(this.elMain).append($(this.el))},enableButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).prop("disabled",false)},disableButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).prop("disabled",true)},hideButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).hide()},showButton:function(b){this.$buttons.find("#"+String(b).toLowerCase()).show()},scrollTop:function(){return this.$body.scrollTop()},template:function(b){return'<div class="modal"><div class="modal-backdrop fade in" style="z-index: -1;"></div><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" style="display: none;">×</button><h4 class="title">'+b+'</h4></div><div class="modal-body"></div><div class="modal-footer"><div class="buttons" style="float: right;"></div></div></div</div></div>'}});return{GalaxyModal:a}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: natefoo: Allow changing the header for remote user.
by commits-noreply@bitbucket.org 03 Jan '14
by commits-noreply@bitbucket.org 03 Jan '14
03 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e92e13e9c103/
Changeset: e92e13e9c103
User: natefoo
Date: 2014-01-03 17:41:19
Summary: Allow changing the header for remote user.
Affected #: 6 files
diff -r 5f221e5774804c77987831bebc6941d5a91fa872 -r e92e13e9c103cc1f36dff65e1523479bf5cb17ed lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -123,6 +123,7 @@
self.retry_metadata_internally = string_as_bool( kwargs.get( "retry_metadata_internally", "True" ) )
self.use_remote_user = string_as_bool( kwargs.get( "use_remote_user", "False" ) )
self.remote_user_maildomain = kwargs.get( "remote_user_maildomain", None )
+ self.remote_user_header = kwargs.get( "remote_user_header", 'HTTP_REMOTE_USER' )
self.remote_user_logout_href = kwargs.get( "remote_user_logout_href", None )
self.require_login = string_as_bool( kwargs.get( "require_login", "False" ) )
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
diff -r 5f221e5774804c77987831bebc6941d5a91fa872 -r e92e13e9c103cc1f36dff65e1523479bf5cb17ed lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -586,9 +586,9 @@
# things now.
if self.app.config.use_remote_user:
#If this is an api request, and they've passed a key, we let this go.
- assert "HTTP_REMOTE_USER" in self.environ, \
- "use_remote_user is set but no HTTP_REMOTE_USER variable"
- remote_user_email = self.environ[ 'HTTP_REMOTE_USER' ]
+ assert self.app.config.remote_user_header in self.environ, \
+ "use_remote_user is set but %s header was not provided" % self.app.config.remote_user_header
+ remote_user_email = self.environ[ self.app.config.remote_user_header ]
if galaxy_session:
# An existing session, make sure correct association exists
if galaxy_session.user is None:
diff -r 5f221e5774804c77987831bebc6941d5a91fa872 -r e92e13e9c103cc1f36dff65e1523479bf5cb17ed lib/galaxy/web/framework/middleware/remoteuser.py
--- a/lib/galaxy/web/framework/middleware/remoteuser.py
+++ b/lib/galaxy/web/framework/middleware/remoteuser.py
@@ -36,11 +36,12 @@
"""
class RemoteUser( object ):
- def __init__( self, app, maildomain=None, display_servers=None, admin_users=None ):
+ def __init__( self, app, maildomain=None, display_servers=None, admin_users=None, remote_user_header=None ):
self.app = app
self.maildomain = maildomain
self.display_servers = display_servers or []
self.admin_users = admin_users or []
+ self.remote_user_header = remote_user_header or 'HTTP_REMOTE_USER'
def __call__( self, environ, start_response ):
# Allow display servers
if self.display_servers and environ.has_key( 'REMOTE_ADDR' ):
@@ -50,16 +51,16 @@
# in the event of a lookup failure, deny access
host = None
if host in self.display_servers:
- environ[ 'HTTP_REMOTE_USER' ] = 'remote_display_server@%s' % ( self.maildomain or 'example.org' )
+ environ[ self.remote_user_header ] = 'remote_display_server@%s' % ( self.maildomain or 'example.org' )
return self.app( environ, start_response )
# Apache sets REMOTE_USER to the string '(null)' when using the
# Rewrite* method for passing REMOTE_USER and a user is
# un-authenticated. Any other possible values need to go here as well.
path_info = environ.get('PATH_INFO', '')
- if environ.has_key( 'HTTP_REMOTE_USER' ) and environ[ 'HTTP_REMOTE_USER' ] != '(null)':
- if not environ[ 'HTTP_REMOTE_USER' ].count( '@' ):
+ if environ.has_key( self.remote_user_header ) and environ[ self.remote_user_header ] != '(null)':
+ if not environ[ self.remote_user_header ].count( '@' ):
if self.maildomain is not None:
- environ[ 'HTTP_REMOTE_USER' ] += '@' + self.maildomain
+ environ[ self.remote_user_header ] += '@' + self.maildomain
else:
title = "Access to Galaxy is denied"
message = """
@@ -73,7 +74,7 @@
before you may access Galaxy.
"""
return self.error( start_response, title, message )
- if path_info.startswith( '/user/create' ) and environ[ 'HTTP_REMOTE_USER' ] in self.admin_users:
+ if path_info.startswith( '/user/create' ) and environ[ self.remote_user_header ] in self.admin_users:
pass # admins can create users
elif path_info.startswith( '/user/api_keys' ):
pass # api keys can be managed when remote_user is in use
diff -r 5f221e5774804c77987831bebc6941d5a91fa872 -r e92e13e9c103cc1f36dff65e1523479bf5cb17ed lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -350,7 +350,8 @@
from galaxy.web.framework.middleware.remoteuser import RemoteUser
app = RemoteUser( app, maildomain = conf.get( 'remote_user_maildomain', None ),
display_servers = util.listify( conf.get( 'display_servers', '' ) ),
- admin_users = conf.get( 'admin_users', '' ).split( ',' ) )
+ admin_users = conf.get( 'admin_users', '' ).split( ',' ),
+ remote_user_header = conf.get( 'remote_user_header', 'HTTP_REMOTE_USER' ) )
log.debug( "Enabling 'remote user' middleware" )
# The recursive middleware allows for including requests in other
# requests or forwarding of requests, all on the server side.
diff -r 5f221e5774804c77987831bebc6941d5a91fa872 -r e92e13e9c103cc1f36dff65e1523479bf5cb17ed lib/galaxy/webapps/tool_shed/config.py
--- a/lib/galaxy/webapps/tool_shed/config.py
+++ b/lib/galaxy/webapps/tool_shed/config.py
@@ -72,6 +72,7 @@
self.blacklist_location = kwargs.get( 'blacklist_file', None )
self.blacklist_content = None
self.remote_user_maildomain = kwargs.get( "remote_user_maildomain", None )
+ self.remote_user_header = kwargs.get( "remote_user_header", 'HTTP_REMOTE_USER' )
self.remote_user_logout_href = kwargs.get( "remote_user_logout_href", None )
self.require_login = string_as_bool( kwargs.get( "require_login", "False" ) )
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
diff -r 5f221e5774804c77987831bebc6941d5a91fa872 -r e92e13e9c103cc1f36dff65e1523479bf5cb17ed universe_wsgi.ini.sample
--- a/universe_wsgi.ini.sample
+++ b/universe_wsgi.ini.sample
@@ -586,6 +586,13 @@
# to usernames, to become your Galaxy usernames (email addresses).
#remote_user_maildomain = None
+# If use_remote_user is enabled, the header that the upstream proxy provides
+# the remote username in defaults to HTTP_REMOTE_USER (the 'HTTP_' is prepended
+# by WSGI). This option allows you to change the header. Note, you still need
+# to prepend 'HTTP_' to the header in this option, but your proxy server should
+# *not* include 'HTTP_' at the beginning of the header name.
+#remote_user_header = 'HTTP_REMOTE_USER'
+
# If use_remote_user is enabled, you can set this to a URL that will log your
# users out.
#remote_user_logout_href = None
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: Order imports per database_contexts requirements to fix the tool shed's install and test framework.
by commits-noreply@bitbucket.org 03 Jan '14
by commits-noreply@bitbucket.org 03 Jan '14
03 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/5f221e577480/
Changeset: 5f221e577480
User: greg
Date: 2014-01-03 16:02:19
Summary: Order imports per database_contexts requirements to fix the tool shed's install and test framework.
Affected #: 2 files
diff -r f0f7c3cd2e8af64243f878c04f418c1ee054bc55 -r 5f221e5774804c77987831bebc6941d5a91fa872 scripts/functional_tests.py
--- a/scripts/functional_tests.py
+++ b/scripts/functional_tests.py
@@ -485,9 +485,14 @@
master_api_key=master_api_key,
user_api_key=os.environ.get( "GALAXY_TEST_USER_API_KEY", default_galaxy_user_key ),
)
- else: #when testing data managers, do not test toolbox
+ else:
+ # We must make sure that functional.test_toolbox is always imported after
+ # database_contexts.galaxy_content is set (which occurs in this method above).
+ # If functional.test_toolbox is imported before database_contexts.galaxy_content
+ # is set, sa_session will be None in all methods that use it.
import functional.test_toolbox
functional.test_toolbox.toolbox = app.toolbox
+ # When testing data managers, do not test toolbox.
functional.test_toolbox.build_tests(
testing_shed_tools=testing_shed_tools,
master_api_key=master_api_key,
diff -r f0f7c3cd2e8af64243f878c04f418c1ee054bc55 -r 5f221e5774804c77987831bebc6941d5a91fa872 test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -52,7 +52,6 @@
from paste import httpserver
from functional import database_contexts
-import functional.test_toolbox as imported_test_toolbox
log = logging.getLogger( 'install_and_test_repositories_with_tools' )
@@ -136,6 +135,10 @@
return tool_id, tool_version
def install_and_test_repositories( app, galaxy_shed_tools_dict, galaxy_shed_tool_conf_file ):
+ # We must make sure that functional.test_toolbox is always imported after database_contexts.galaxy_content
+ # is set (which occurs in the main method before this method is called). If functional.test_toolbox is
+ # imported before database_contexts.galaxy_content is set, sa_session will be None in all methods that use it.
+ import functional.test_toolbox as imported_test_toolbox
global test_toolbox
test_toolbox = imported_test_toolbox
# Initialize a dictionary for the summary that will be printed to stdout.
@@ -442,7 +445,6 @@
app = UniverseApplication( **kwargs )
database_contexts.galaxy_context = app.model.context
database_contexts.install_context = app.install_model.context
-
log.debug( "Embedded Galaxy application started..." )
# ---- Run galaxy webserver ------------------------------------------------------
server = None
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
11 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/3f8f90da2773/
Changeset: 3f8f90da2773
User: jmchilton
Date: 2014-01-03 00:50:10
Summary: Fix LWR exit code handling.
Affected #: 1 file
diff -r 5378f0517318861997ae7be112bbbf4098bdf7ce -r 3f8f90da27736f7ae1e7dfee4bd2e43bc1cc4bfd lib/galaxy/jobs/runners/lwr.py
--- a/lib/galaxy/jobs/runners/lwr.py
+++ b/lib/galaxy/jobs/runners/lwr.py
@@ -173,6 +173,7 @@
run_results = client.raw_check_complete()
stdout = run_results.get('stdout', '')
stderr = run_results.get('stderr', '')
+ exit_code = run_results.get('returncode', None)
working_directory_contents = run_results.get('working_directory_contents', [])
# Use LWR client code to transfer/copy files back
# and cleanup job if needed.
@@ -207,7 +208,7 @@
self._handle_metadata_externally( job_wrapper, resolve_requirements=True )
# Finish the job
try:
- job_wrapper.finish( stdout, stderr )
+ job_wrapper.finish( stdout, stderr, exit_code )
except Exception:
log.exception("Job wrapper finish method failed")
job_wrapper.fail("Unable to finish job", exception=True)
https://bitbucket.org/galaxy/galaxy-central/commits/18b2967240e5/
Changeset: 18b2967240e5
User: jmchilton
Date: 2014-01-03 00:50:10
Summary: Refactor JobRunner's in_directory out into galaxy.util for reuse.
Affected #: 2 files
diff -r 3f8f90da27736f7ae1e7dfee4bd2e43bc1cc4bfd -r 18b2967240e53d77ed87c3ef5aeaec532597bd58 lib/galaxy/jobs/runners/__init__.py
--- a/lib/galaxy/jobs/runners/__init__.py
+++ b/lib/galaxy/jobs/runners/__init__.py
@@ -15,6 +15,7 @@
from galaxy.jobs.command_factory import build_command
from galaxy import model
from galaxy.util import DATABASE_MAX_STRING_SIZE, shrink_stream_by_size
+from galaxy.util import in_directory
from galaxy.jobs.runners.util.job_script import job_script
log = logging.getLogger( __name__ )
@@ -154,18 +155,6 @@
if not job_working_directory:
job_working_directory = os.path.abspath( job_wrapper.working_directory )
- def in_directory( file, directory ):
- """
- Return true, if the common prefix of both is equal to directory
- e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b
- """
-
- # Make both absolute.
- directory = os.path.abspath( directory )
- file = os.path.abspath( file )
-
- return os.path.commonprefix( [ file, directory ] ) == directory
-
# Set up dict of dataset id --> output path; output path can be real or
# false depending on outputs_to_working_directory
output_paths = {}
diff -r 3f8f90da27736f7ae1e7dfee4bd2e43bc1cc4bfd -r 18b2967240e53d77ed87c3ef5aeaec532597bd58 lib/galaxy/util/__init__.py
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -374,6 +374,20 @@
return default
return out
+
+def in_directory( file, directory ):
+ """
+ Return true, if the common prefix of both is equal to directory
+ e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b
+ """
+
+ # Make both absolute.
+ directory = os.path.abspath( directory )
+ file = os.path.abspath( file )
+
+ return os.path.commonprefix( [ file, directory ] ) == directory
+
+
class Params( object ):
"""
Stores and 'sanitizes' parameters. Alphanumeric characters and the
https://bitbucket.org/galaxy/galaxy-central/commits/9920d006a966/
Changeset: 9920d006a966
User: jmchilton
Date: 2014-01-03 00:50:11
Summary: Update LWR client through LWR changeset 8ef5299.
This encompasses numerous individual LWR changesets. The largest of these changes include extensions to:
- Enable LWR client to work in a fashion where remote paths are precalculated and used during job input evaluation instead of having these inputs rewritten after the fact. (Galaxy + LWR runner require significant modifications to enable this - found in subsequent changesets). https://bitbucket.org/jmchilton/lwr/commits/a06587ff0544f3a09cf221057be1b91….
- Allow staging or arbitrary paths (not restricted to job files such as inputs, working directory files, configs, etc...). https://bitbucket.org/jmchilton/lwr/commits/63981e79696337399edb42be5614bc7….
- Vast improvements to extra_files_path handling - before it only worked for one level of input extra_files_path - now works with arbitrary nesting of inputs and outputs. https://bitbucket.org/jmchilton/lwr/commits/b5e8c4dffc3a04639550e01f3f44ac3…, https://bitbucket.org/jmchilton/lwr/commits/ab5bc61f6e0c9e11748436bd61aa1f6….
- Handle tools with version_command defined (LWR servers running on *nix systems only). https://bitbucket.org/jmchilton/lwr/commits/a3b43baa1aa6a8167a4e8cbb195d2a9….
- Allow restircting path mapping to specific input types (input, tool, config, workdir, output, output_workdir, *default* (all previous), unstructured (new for arbitrary files), and *any* (*default* + unstructured). https://bitbucket.org/jmchilton/lwr/commits/27e678d47846c2fdf5792d0c64167d1…
As well as several major refactorings to break improve the LWR client code structure
- Break up stager.py into smaller modules - https://bitbucket.org/jmchilton/lwr/commits/d0efda40b2c92100161ca7f825cb754…
- Refactor complex method __download_results into class with smaller helper methods - https://bitbucket.org/jmchilton/lwr/commits/45fd16e52579c273eea5f52948ca57a…
- Introduce higher level abstrction for staging actions - https://bitbucket.org/jmchilton/lwr/commits/35ea7e2fa88714aff5a38f0e0a55e3f….
Affected #: 10 files
diff -r 18b2967240e53d77ed87c3ef5aeaec532597bd58 -r 9920d006a96618d7729ef09e0fb861b22ff530db lib/galaxy/jobs/runners/lwr.py
--- a/lib/galaxy/jobs/runners/lwr.py
+++ b/lib/galaxy/jobs/runners/lwr.py
@@ -15,6 +15,8 @@
from .lwr_client import finish_job as lwr_finish_job
from .lwr_client import submit_job as lwr_submit_job
from .lwr_client import ClientJobDescription
+from .lwr_client import LwrOutputs
+from .lwr_client import GalaxyOutputs
log = logging.getLogger( __name__ )
@@ -79,6 +81,7 @@
tool=job_wrapper.tool,
config_files=job_wrapper.extra_filenames,
requirements=requirements,
+ version_file=job_wrapper.get_version_string_path(),
)
job_id = lwr_submit_job(client, client_job_description, remote_job_config)
log.info("lwr job submitted with job_id %s" % job_id)
@@ -174,7 +177,7 @@
stdout = run_results.get('stdout', '')
stderr = run_results.get('stderr', '')
exit_code = run_results.get('returncode', None)
- working_directory_contents = run_results.get('working_directory_contents', [])
+ lwr_outputs = LwrOutputs(run_results)
# Use LWR client code to transfer/copy files back
# and cleanup job if needed.
completed_normally = \
@@ -188,13 +191,17 @@
# no need to handle them differently here.
work_dir_outputs = []
output_files = self.get_output_files( job_wrapper )
+ galaxy_outputs = GalaxyOutputs(
+ working_directory=job_wrapper.working_directory,
+ work_dir_outputs=work_dir_outputs,
+ output_files=output_files,
+ version_file=job_wrapper.get_version_string_path(),
+ )
finish_args = dict( client=client,
- working_directory=job_wrapper.working_directory,
job_completed_normally=completed_normally,
cleanup_job=cleanup_job,
- work_dir_outputs=work_dir_outputs,
- output_files=output_files,
- working_directory_contents=working_directory_contents )
+ galaxy_outputs=galaxy_outputs,
+ lwr_outputs=lwr_outputs )
failed = lwr_finish_job( **finish_args )
if failed:
diff -r 18b2967240e53d77ed87c3ef5aeaec532597bd58 -r 9920d006a96618d7729ef09e0fb861b22ff530db lib/galaxy/jobs/runners/lwr_client/__init__.py
--- a/lib/galaxy/jobs/runners/lwr_client/__init__.py
+++ b/lib/galaxy/jobs/runners/lwr_client/__init__.py
@@ -6,9 +6,24 @@
"""
-from .stager import submit_job, finish_job, ClientJobDescription
+from .staging.down import finish_job
+from .staging.up import submit_job
+from .staging import ClientJobDescription
+from .staging import LwrOutputs
+from .staging import GalaxyOutputs
from .client import OutputNotFoundException
from .manager import ClientManager
from .destination import url_to_destination_params
+from .path_mapper import PathMapper
-__all__ = [ClientManager, OutputNotFoundException, url_to_destination_params, finish_job, submit_job, ClientJobDescription]
+__all__ = [
+ ClientManager,
+ OutputNotFoundException,
+ url_to_destination_params,
+ finish_job,
+ submit_job,
+ ClientJobDescription,
+ LwrOutputs,
+ GalaxyOutputs,
+ PathMapper,
+]
diff -r 18b2967240e53d77ed87c3ef5aeaec532597bd58 -r 9920d006a96618d7729ef09e0fb861b22ff530db lib/galaxy/jobs/runners/lwr_client/action_mapper.py
--- a/lib/galaxy/jobs/runners/lwr_client/action_mapper.py
+++ b/lib/galaxy/jobs/runners/lwr_client/action_mapper.py
@@ -1,12 +1,49 @@
from simplejson import load
from os.path import abspath
-from fnmatch import fnmatch
+from os.path import dirname
+from os.path import join
+from os.path import basename
+from os.path import sep
+import fnmatch
from re import compile
-
+from re import escape
+from galaxy.util.bunch import Bunch
+from .util import directory_files
+from .util import unique_path_prefix
DEFAULT_MAPPED_ACTION = 'transfer' # Not really clear to me what this should be, exception?
DEFAULT_PATH_MAPPER_TYPE = 'prefix'
+# Poor man's enum.
+path_type = Bunch(
+ # Galaxy input datasets and extra files.
+ INPUT="input",
+ # Galaxy config and param files.
+ CONFIG="config",
+ # Files from tool's tool_dir (for now just wrapper if available).
+ TOOL="tool",
+ # Input work dir files - e.g. metadata files, task-split input files, etc..
+ WORKDIR="workdir",
+ # Galaxy output datasets in their final home.
+ OUTPUT="output",
+ # Galaxy from_work_dir output paths and other files (e.g. galaxy.json)
+ OUTPUT_WORKDIR="output_workdir",
+ # Other fixed tool parameter paths (likely coming from tool data, but not
+ # nessecarily). Not sure this is the best name...
+ UNSTRUCTURED="unstructured",
+)
+
+
+ACTION_DEFAULT_PATH_TYPES = [
+ path_type.INPUT,
+ path_type.CONFIG,
+ path_type.TOOL,
+ path_type.WORKDIR,
+ path_type.OUTPUT,
+ path_type.OUTPUT_WORKDIR,
+]
+ALL_PATH_TYPES = ACTION_DEFAULT_PATH_TYPES + [path_type.UNSTRUCTURED]
+
class FileActionMapper(object):
"""
@@ -15,37 +52,61 @@
>>> json_string = r'''{"paths": [ \
{"path": "/opt/galaxy", "action": "none"}, \
{"path": "/galaxy/data", "action": "transfer"}, \
- {"path": "/cool/bamfiles/**/*.bam", "action": "copy", "type": "glob"}, \
- {"path": ".*/dataset_\\\\d+.dat", "action": "copy", "type": "regex"} \
+ {"path": "/cool/bamfiles/**/*.bam", "action": "copy", "match_type": "glob"}, \
+ {"path": ".*/dataset_\\\\d+.dat", "action": "copy", "match_type": "regex"} \
]}'''
>>> from tempfile import NamedTemporaryFile
>>> from os import unlink
- >>> f = NamedTemporaryFile(delete=False)
- >>> write_result = f.write(json_string.encode('UTF-8'))
- >>> f.close()
- >>> class MockClient():
- ... default_file_action = 'none'
- ... action_config_path = f.name
- ...
- >>> mapper = FileActionMapper(MockClient())
- >>> unlink(f.name)
+ >>> def mapper_for(default_action, config_contents):
+ ... f = NamedTemporaryFile(delete=False)
+ ... f.write(config_contents.encode('UTF-8'))
+ ... f.close()
+ ... mock_client = Bunch(default_file_action=default_action, action_config_path=f.name)
+ ... mapper = FileActionMapper(mock_client)
+ ... unlink(f.name)
+ ... return mapper
+ >>> mapper = mapper_for(default_action='none', config_contents=json_string)
>>> # Test first config line above, implicit path prefix mapper
- >>> mapper.action('/opt/galaxy/tools/filters/catWrapper.py', 'input')[0] == u'none'
+ >>> action = mapper.action('/opt/galaxy/tools/filters/catWrapper.py', 'input')
+ >>> action.action_type == u'none'
True
+ >>> action.staging_needed
+ False
>>> # Test another (2nd) mapper, this one with a different action
- >>> mapper.action('/galaxy/data/files/000/dataset_1.dat', 'input')[0] == u'transfer'
+ >>> action = mapper.action('/galaxy/data/files/000/dataset_1.dat', 'input')
+ >>> action.action_type == u'transfer'
+ True
+ >>> action.staging_needed
True
>>> # Always at least copy work_dir outputs.
- >>> mapper.action('/opt/galaxy/database/working_directory/45.sh', 'work_dir')[0] == u'copy'
+ >>> action = mapper.action('/opt/galaxy/database/working_directory/45.sh', 'workdir')
+ >>> action.action_type == u'copy'
+ True
+ >>> action.staging_needed
True
>>> # Test glob mapper (matching test)
- >>> mapper.action('/cool/bamfiles/projectABC/study1/patient3.bam', 'input')[0] == u'copy'
+ >>> mapper.action('/cool/bamfiles/projectABC/study1/patient3.bam', 'input').action_type == u'copy'
True
>>> # Test glob mapper (non-matching test)
- >>> mapper.action('/cool/bamfiles/projectABC/study1/patient3.bam.bai', 'input')[0] == u'none'
+ >>> mapper.action('/cool/bamfiles/projectABC/study1/patient3.bam.bai', 'input').action_type == u'none'
True
>>> # Regex mapper test.
- >>> mapper.action('/old/galaxy/data/dataset_10245.dat', 'input')[0] == u'copy'
+ >>> mapper.action('/old/galaxy/data/dataset_10245.dat', 'input').action_type == u'copy'
+ True
+ >>> # Doesn't map unstructured paths by default
+ >>> mapper.action('/old/galaxy/data/dataset_10245.dat', 'unstructured').action_type == u'none'
+ True
+ >>> input_only_mapper = mapper_for(default_action="none", config_contents=r'''{"paths": [ \
+ {"path": "/", "action": "transfer", "path_types": "input"} \
+ ] }''')
+ >>> input_only_mapper.action('/dataset_1.dat', 'input').action_type == u'transfer'
+ True
+ >>> input_only_mapper.action('/dataset_1.dat', 'output').action_type == u'none'
+ True
+ >>> unstructured_mapper = mapper_for(default_action="none", config_contents=r'''{"paths": [ \
+ {"path": "/", "action": "transfer", "path_types": "*any*"} \
+ ] }''')
+ >>> unstructured_mapper.action('/old/galaxy/data/dataset_10245.dat', 'unstructured').action_type == u'transfer'
True
"""
@@ -59,27 +120,92 @@
def __load_action_config(self, path):
config = load(open(path, 'rb'))
for path_config in config.get('paths', []):
- map_type = path_config.get('type', DEFAULT_PATH_MAPPER_TYPE)
+ map_type = path_config.get('match_type', DEFAULT_PATH_MAPPER_TYPE)
self.mappers.append(mappers[map_type](path_config))
- def action(self, path, type):
- action = self.default_action
+ def action(self, path, type, mapper=None):
+ action_type = self.default_action if type in ACTION_DEFAULT_PATH_TYPES else "none"
+ file_lister = DEFAULT_FILE_LISTER
normalized_path = abspath(path)
- for mapper in self.mappers:
- if mapper.matches(normalized_path):
- action = mapper.action
- break
- if type in ["work_dir", "output_task"] and action == "none":
+ if not mapper:
+ for query_mapper in self.mappers:
+ if query_mapper.matches(normalized_path, type):
+ mapper = query_mapper
+ break
+ if mapper:
+ action_type = mapper.action_type
+ file_lister = mapper.file_lister
+ if type in ["workdir", "output_workdir"] and action_type == "none":
## We are changing the working_directory relative to what
## Galaxy would use, these need to be copied over.
- action = "copy"
- return (action,)
+ action_type = "copy"
+ action_class = actions.get(action_type, None)
+ if action_class is None:
+ message_template = "Unknown action_type encountered %s while trying to map path %s"
+ message_args = (action_type, path)
+ raise Exception(message_template % message_args)
+ return action_class(path, file_lister=file_lister)
+
+ def unstructured_mappers(self):
+ """ Return mappers that will map 'unstructured' files (i.e. go beyond
+ mapping inputs, outputs, and config files).
+ """
+ return filter(lambda m: path_type.UNSTRUCTURED in m.path_types, self.mappers)
+
+
+class BaseAction(object):
+
+ def __init__(self, path, file_lister=None):
+ self.path = path
+ self.file_lister = file_lister or DEFAULT_FILE_LISTER
+
+ def unstructured_map(self):
+ unstructured_map = self.file_lister.unstructured_map(self.path)
+ # To ensure uniqueness, prepend unique prefix to each name
+ prefix = unique_path_prefix(self.path)
+ for path, name in unstructured_map.iteritems():
+ unstructured_map[path] = join(prefix, name)
+ return unstructured_map
+
+
+class NoneAction(BaseAction):
+ """ This action indicates the corresponding path does not require any
+ additional action. This should indicate paths that are available both on
+ the LWR client (i.e. Galaxy server) and remote LWR server with the same
+ paths. """
+ action_type = "none"
+ staging_needed = False
+
+
+class TransferAction(BaseAction):
+ """ This actions indicates that the LWR client should initiate an HTTP
+ transfer of the corresponding path to the remote LWR server before
+ launching the job. """
+ action_type = "transfer"
+ staging_needed = True
+
+
+class CopyAction(BaseAction):
+ """ This action indicates that the LWR client should execute a file system
+ copy of the corresponding path to the LWR staging directory prior to
+ launching the corresponding job. """
+ action_type = "copy"
+ staging_needed = True
class BasePathMapper(object):
def __init__(self, config):
- self.action = config.get('action', DEFAULT_MAPPED_ACTION)
+ self.action_type = config.get('action', DEFAULT_MAPPED_ACTION)
+ path_types_str = config.get('path_types', "*defaults*")
+ path_types_str = path_types_str.replace("*defaults*", ",".join(ACTION_DEFAULT_PATH_TYPES))
+ path_types_str = path_types_str.replace("*any*", ",".join(ALL_PATH_TYPES))
+ self.path_types = path_types_str.split(",")
+ self.file_lister = FileLister(config)
+
+ def matches(self, path, path_type):
+ path_type_matches = path_type in self.path_types
+ return path_type_matches and self._path_matches(path)
class PrefixPathMapper(BasePathMapper):
@@ -88,9 +214,13 @@
super(PrefixPathMapper, self).__init__(config)
self.prefix_path = abspath(config['path'])
- def matches(self, path):
+ def _path_matches(self, path):
return path.startswith(self.prefix_path)
+ def to_pattern(self):
+ pattern_str = "(%s%s[^\s,\"\']+)" % (escape(self.prefix_path), escape(sep))
+ return compile(pattern_str)
+
class GlobPathMapper(BasePathMapper):
@@ -98,8 +228,11 @@
super(GlobPathMapper, self).__init__(config)
self.glob_path = config['path']
- def matches(self, path):
- return fnmatch(path, self.glob_path)
+ def _path_matches(self, path):
+ return fnmatch.fnmatch(path, self.glob_path)
+
+ def to_pattern(self):
+ return compile(fnmatch.translate(self.glob_path))
class RegexPathMapper(BasePathMapper):
@@ -108,9 +241,32 @@
super(RegexPathMapper, self).__init__(config)
self.pattern = compile(config['path'])
- def matches(self, path):
+ def _path_matches(self, path):
return self.pattern.match(path) is not None
+ def to_pattern(self):
+ return self.pattern
+
+
+class FileLister(object):
+
+ def __init__(self, config):
+ self.depth = int(config.get("depth", "0"))
+
+ def unstructured_map(self, path):
+ depth = self.depth
+ if self.depth == 0:
+ return {path: basename(path)}
+ else:
+ while depth > 0:
+ path = dirname(path)
+ depth -= 1
+ return dict([(join(path, f), f) for f in directory_files(path)])
+
+DEFAULT_FILE_LISTER = FileLister(dict(depth=0))
+
+ACTION_CLASSES = [NoneAction, TransferAction, CopyAction]
+actions = dict([(clazz.action_type, clazz) for clazz in ACTION_CLASSES])
mappers = {
'prefix': PrefixPathMapper,
@@ -119,4 +275,4 @@
}
-__all__ = [FileActionMapper]
+__all__ = [FileActionMapper, path_type]
diff -r 18b2967240e53d77ed87c3ef5aeaec532597bd58 -r 9920d006a96618d7729ef09e0fb861b22ff530db lib/galaxy/jobs/runners/lwr_client/client.py
--- a/lib/galaxy/jobs/runners/lwr_client/client.py
+++ b/lib/galaxy/jobs/runners/lwr_client/client.py
@@ -82,16 +82,16 @@
args = {"job_id": self.job_id, "name": name, "input_type": input_type}
return self._raw_execute('input_path', args)
- def put_file(self, path, input_type, name=None, contents=None, action='transfer'):
+ def put_file(self, path, input_type, name=None, contents=None, action_type='transfer'):
if not name:
name = os.path.basename(path)
args = {"job_id": self.job_id, "name": name, "input_type": input_type}
input_path = path
if contents:
input_path = None
- if action == 'transfer':
+ if action_type == 'transfer':
return self._upload_file(args, contents, input_path)
- elif action == 'copy':
+ elif action_type == 'copy':
lwr_path = self._raw_execute('input_path', args)
self._copy(path, lwr_path)
return {'path': lwr_path}
@@ -105,11 +105,14 @@
## path. Use old paths.
input_type = args['input_type']
action = {
- 'input': 'upload_input',
- 'input_extra': 'upload_extra_input',
+ # For backward compatibility just target upload_input_extra for all
+ # inputs, it allows nested inputs. Want to do away with distinction
+ # inputs and extra inputs.
+ 'input': 'upload_extra_input',
'config': 'upload_config_file',
- 'work_dir': 'upload_working_directory_file',
- 'tool': 'upload_tool_file'
+ 'workdir': 'upload_working_directory_file',
+ 'tool': 'upload_tool_file',
+ 'unstructured': 'upload_unstructured_file',
}[input_type]
del args['input_type']
return action
@@ -119,7 +122,23 @@
return self._raw_execute("get_output_type", {"name": name,
"job_id": self.job_id})
- def fetch_output(self, path, working_directory, action='transfer'):
+ # Deprecated
+ def fetch_output_legacy(self, path, working_directory, action_type='transfer'):
+ # Needs to determine if output is task/working directory or standard.
+ name = os.path.basename(path)
+
+ output_type = self._get_output_type(name)
+ if output_type == "none":
+ # Just make sure the file was created.
+ if not os.path.exists(path):
+ raise OutputNotFoundException(path)
+ return
+ elif output_type in ["task"]:
+ path = os.path.join(working_directory, name)
+
+ self.__populate_output_path(name, path, output_type, action_type)
+
+ def fetch_output(self, path, name=None, check_exists_remotely=False, action_type='transfer'):
"""
Download an output dataset from the remote server.
@@ -130,38 +149,23 @@
working_directory : str
Local working_directory for the job.
"""
- name = os.path.basename(path)
- output_type = self._get_output_type(name)
- if output_type == "none":
- # Just make sure the file was created.
- if not os.path.exists(path):
- raise OutputNotFoundException(path)
- return
+ if not name:
+ # Extra files will send in the path.
+ name = os.path.basename(path)
- output_path = self.__output_path(path, name, working_directory, output_type)
- self.__populate_output_path(name, output_path, output_type, action)
+ output_type = "direct" # Task/from_work_dir outputs now handled with fetch_work_dir_output
+ self.__populate_output_path(name, path, output_type, action_type)
- def __populate_output_path(self, name, output_path, output_type, action):
- if action == 'transfer':
+ def __populate_output_path(self, name, output_path, output_type, action_type):
+ self.__ensure_directory(output_path)
+ if action_type == 'transfer':
self.__raw_download_output(name, self.job_id, output_type, output_path)
- elif action == 'copy':
+ elif action_type == 'copy':
lwr_path = self._output_path(name, self.job_id, output_type)['path']
self._copy(lwr_path, output_path)
- def __output_path(self, path, name, working_directory, output_type):
- """
- Preconditions: output_type is not 'none'.
- """
- if output_type == "direct":
- output_path = path
- elif output_type == "task":
- output_path = os.path.join(working_directory, name)
- else:
- raise Exception("Unknown output_type returned from LWR server %s" % output_type)
- return output_path
-
- def fetch_work_dir_output(self, name, working_directory, output_path, action='transfer'):
+ def fetch_work_dir_output(self, name, working_directory, output_path, action_type='transfer'):
"""
Download an output dataset specified with from_work_dir from the
remote server.
@@ -175,12 +179,18 @@
output_path : str
Full path to output dataset.
"""
- if action == 'transfer':
+ self.__ensure_directory(output_path)
+ if action_type == 'transfer':
self.__raw_download_output(name, self.job_id, "work_dir", output_path)
else: # Even if action is none - LWR has a different work_dir so this needs to be copied.
lwr_path = self._output_path(name, self.job_id, 'work_dir')['path']
self._copy(lwr_path, output_path)
+ def __ensure_directory(self, output_path):
+ output_path_directory = os.path.dirname(output_path)
+ if not os.path.exists(output_path_directory):
+ os.makedirs(output_path_directory)
+
@parseJson()
def _output_path(self, name, job_id, output_type):
return self._raw_execute("output_path",
diff -r 18b2967240e53d77ed87c3ef5aeaec532597bd58 -r 9920d006a96618d7729ef09e0fb861b22ff530db lib/galaxy/jobs/runners/lwr_client/path_mapper.py
--- /dev/null
+++ b/lib/galaxy/jobs/runners/lwr_client/path_mapper.py
@@ -0,0 +1,97 @@
+import os.path
+from .action_mapper import FileActionMapper
+from .action_mapper import path_type
+from .util import PathHelper
+
+from galaxy.util import in_directory
+
+
+class PathMapper(object):
+ """ Ties together a FileActionMapper and remote job configuration returned
+ by the LWR setup method to pre-determine the location of files for staging
+ on the remote LWR server.
+
+ This is not useful when rewrite_paths (as has traditionally been done with
+ the LWR) because when doing that the LWR determines the paths as files are
+ uploaded. When rewrite_paths is disabled however, the destination of files
+ needs to be determined prior to transfer so an object of this class can be
+ used.
+ """
+
+ def __init__(
+ self,
+ client,
+ remote_job_config,
+ local_working_directory,
+ action_mapper=None,
+ ):
+ self.local_working_directory = local_working_directory
+ if not action_mapper:
+ action_mapper = FileActionMapper(client)
+ self.action_mapper = action_mapper
+ self.input_directory = remote_job_config["inputs_directory"]
+ self.output_directory = remote_job_config["outputs_directory"]
+ self.working_directory = remote_job_config["working_directory"]
+ self.unstructured_files_directory = remote_job_config["unstructured_files_directory"]
+ self.config_directory = remote_job_config["configs_directory"]
+ separator = remote_job_config["system_properties"]["separator"]
+ self.path_helper = PathHelper(separator)
+
+ def remote_output_path_rewrite(self, local_path):
+ output_type = path_type.OUTPUT
+ if in_directory(local_path, self.local_working_directory):
+ output_type = path_type.OUTPUT_WORKDIR
+ remote_path = self.__remote_path_rewrite(local_path, output_type)
+ return remote_path
+
+ def remote_input_path_rewrite(self, local_path):
+ remote_path = self.__remote_path_rewrite(local_path, path_type.INPUT)
+ return remote_path
+
+ def remote_version_path_rewrite(self, local_path):
+ remote_path = self.__remote_path_rewrite(local_path, path_type.OUTPUT, name="COMMAND_VERSION")
+ return remote_path
+
+ def check_for_arbitrary_rewrite(self, local_path):
+ if not os.path.exists(local_path):
+ return None, []
+
+ path = str(local_path) # Use false_path if needed.
+ action = self.action_mapper.action(path, path_type.UNSTRUCTURED)
+ if not action.staging_needed:
+ return None, []
+ unique_names = action.unstructured_map()
+ name = unique_names[path]
+ remote_path = self.path_helper.remote_join(self.unstructured_files_directory, name)
+ return remote_path, unique_names
+
+ def __remote_path_rewrite(self, dataset_path, dataset_path_type, name=None):
+ """ Return remote path of this file (if staging is required) else None.
+ """
+ path = str(dataset_path) # Use false_path if needed.
+ action = self.action_mapper.action(path, dataset_path_type)
+ remote_path_rewrite = None
+ if action.staging_needed:
+ if name is None:
+ name = os.path.basename(path)
+ remote_directory = self.__remote_directory(dataset_path_type)
+ remote_path_rewrite = self.path_helper.remote_join(remote_directory, name)
+ return remote_path_rewrite
+
+ def __action(self, dataset_path, dataset_path_type):
+ path = str(dataset_path) # Use false_path if needed.
+ action = self.action_mapper.action(path, dataset_path_type)
+ return action
+
+ def __remote_directory(self, dataset_path_type):
+ if dataset_path_type in [path_type.OUTPUT]:
+ return self.output_directory
+ elif dataset_path_type in [path_type.WORKDIR, path_type.OUTPUT_WORKDIR]:
+ return self.working_directory
+ elif dataset_path_type in [path_type.INPUT]:
+ return self.input_directory
+ else:
+ message = "PathMapper cannot handle path type %s" % dataset_path_type
+ raise Exception(message)
+
+__all__ = [PathMapper]
diff -r 18b2967240e53d77ed87c3ef5aeaec532597bd58 -r 9920d006a96618d7729ef09e0fb861b22ff530db lib/galaxy/jobs/runners/lwr_client/stager.py
--- a/lib/galaxy/jobs/runners/lwr_client/stager.py
+++ /dev/null
@@ -1,417 +0,0 @@
-from os.path import abspath, basename, join, exists
-from os import listdir, sep
-from re import findall
-from re import compile
-from io import open
-from contextlib import contextmanager
-
-from .action_mapper import FileActionMapper
-
-from logging import getLogger
-log = getLogger(__name__)
-
-# All output files marked with from_work_dir attributes will copied or downloaded
-# this pattern picks up attiditional files to copy back - such as those
-# associated with multiple outputs and metadata configuration. Set to .* to just
-# copy everything
-COPY_FROM_WORKING_DIRECTORY_PATTERN = compile(r"primary_.*|galaxy.json|metadata_.*")
-
-
-class JobInputs(object):
- """
- Abstractions over dynamic inputs created for a given job (namely the command to
- execute and created configfiles).
-
- **Parameters**
-
- command_line : str
- Local command to execute for this job. (To be rewritten.)
- config_files : str
- Config files created for this job. (To be rewritten.)
-
-
- >>> import tempfile
- >>> tf = tempfile.NamedTemporaryFile()
- >>> def setup_inputs(tf):
- ... open(tf.name, "w").write(u"world /path/to/input the rest")
- ... inputs = JobInputs(u"hello /path/to/input", [tf.name])
- ... return inputs
- >>> inputs = setup_inputs(tf)
- >>> inputs.rewrite_paths(u"/path/to/input", u'C:\\input')
- >>> inputs.rewritten_command_line == u'hello C:\\\\input'
- True
- >>> inputs.rewritten_config_files[tf.name] == u'world C:\\\\input the rest'
- True
- >>> tf.close()
- >>> tf = tempfile.NamedTemporaryFile()
- >>> inputs = setup_inputs(tf)
- >>> inputs.find_referenced_subfiles('/path/to') == [u'/path/to/input']
- True
- >>> inputs.path_referenced('/path/to')
- True
- >>> inputs.path_referenced(u'/path/to')
- True
- >>> inputs.path_referenced('/path/to/input')
- True
- >>> inputs.path_referenced('/path/to/notinput')
- False
- >>> tf.close()
- """
-
- def __init__(self, command_line, config_files):
- self.rewritten_command_line = command_line
- self.rewritten_config_files = {}
- for config_file in config_files or []:
- config_contents = _read(config_file)
- self.rewritten_config_files[config_file] = config_contents
-
- def find_referenced_subfiles(self, directory):
- """
- Return list of files below specified `directory` in job inputs. Could
- use more sophisticated logic (match quotes to handle spaces, handle
- subdirectories, etc...).
-
- **Parameters**
-
- directory : str
- Full path to directory to search.
-
- """
- pattern = r"(%s%s\S+)" % (directory, sep)
- referenced_files = set()
- for input_contents in self.__items():
- referenced_files.update(findall(pattern, input_contents))
- return list(referenced_files)
-
- def path_referenced(self, path):
- pattern = r"%s" % path
- found = False
- for input_contents in self.__items():
- if findall(pattern, input_contents):
- found = True
- break
- return found
-
- def rewrite_paths(self, local_path, remote_path):
- """
- Rewrite references to `local_path` with `remote_path` in job inputs.
- """
- self.__rewrite_command_line(local_path, remote_path)
- self.__rewrite_config_files(local_path, remote_path)
-
- def __rewrite_command_line(self, local_path, remote_path):
- self.rewritten_command_line = self.rewritten_command_line.replace(local_path, remote_path)
-
- def __rewrite_config_files(self, local_path, remote_path):
- for config_file, rewritten_contents in self.rewritten_config_files.items():
- self.rewritten_config_files[config_file] = rewritten_contents.replace(local_path, remote_path)
-
- def __items(self):
- items = [self.rewritten_command_line]
- items.extend(self.rewritten_config_files.values())
- return items
-
-
-class TransferTracker(object):
-
- def __init__(self, client, job_inputs):
- self.client = client
- self.action_mapper = FileActionMapper(client)
- self.job_inputs = job_inputs
- self.file_renames = {}
-
- def handle_transfer(self, path, type, name=None, contents=None):
- if contents:
- # If contents loaded in memory, no need to write out file and copy,
- # just transfer.
- action = ('transfer', )
- else:
- if not exists(path):
- message = "handle_tranfer called on non-existent file - [%s]" % path
- log.warn(message)
- raise Exception(message)
- action = self.__action(path, type)
-
- if action[0] in ['transfer', 'copy']:
- response = self.client.put_file(path, type, name=name, contents=contents)
- self.register_rewrite(path, response['path'], type, force=True)
- elif action[0] == 'none':
- # No action for this file.
- pass
- else:
- raise Exception("Unknown action type (%s) encountered for path (%s)" % (action[0], path))
-
- def register_rewrite(self, local_path, remote_path, type, force=False):
- action = self.__action(local_path, type)
- if action[0] in ['transfer', 'copy'] or force:
- self.file_renames[local_path] = remote_path
-
- def rewrite_input_paths(self):
- """
- For each file that has been transferred and renamed, updated
- command_line and configfiles to reflect that rewrite.
- """
- for local_path, remote_path in self.file_renames.items():
- self.job_inputs.rewrite_paths(local_path, remote_path)
-
- def __action(self, path, type):
- return self.action_mapper.action(path, type)
-
-
-class FileStager(object):
- """
- Objects of the FileStager class interact with an LWR client object to
- stage the files required to run jobs on a remote LWR server.
-
- **Parameters**
-
- client : JobClient
- LWR client object.
- client_job_description : client_job_description
- Description of client view of job to stage and execute remotely.
- """
-
- def __init__(self, client, client_job_description, job_config):
- """
- """
- self.client = client
- self.command_line = client_job_description.command_line
- self.config_files = client_job_description.config_files
- self.input_files = client_job_description.input_files
- self.output_files = client_job_description.output_files
- self.tool_id = client_job_description.tool.id
- self.tool_version = client_job_description.tool.version
- self.tool_dir = abspath(client_job_description.tool.tool_dir)
- self.working_directory = client_job_description.working_directory
-
- # Setup job inputs, these will need to be rewritten before
- # shipping off to remote LWR server.
- self.job_inputs = JobInputs(self.command_line, self.config_files)
-
- self.transfer_tracker = TransferTracker(client, self.job_inputs)
-
- self.__handle_setup(job_config)
- self.__initialize_referenced_tool_files()
- self.__upload_tool_files()
- self.__upload_input_files()
- self.__upload_working_directory_files()
- self.__initialize_output_file_renames()
- self.__initialize_task_output_file_renames()
- self.__initialize_config_file_renames()
- self.__handle_rewrites()
- self.__upload_rewritten_config_files()
-
- def __handle_setup(self, job_config):
- if not job_config:
- job_config = self.client.setup(self.tool_id, self.tool_version)
-
- self.new_working_directory = job_config['working_directory']
- self.new_outputs_directory = job_config['outputs_directory']
- # Default configs_directory to match remote working_directory to mimic
- # behavior of older LWR servers.
- self.new_configs_drectory = job_config.get('configs_directory', self.new_working_directory)
- self.remote_path_separator = job_config['path_separator']
- # If remote LWR server assigned job id, use that otherwise
- # just use local job_id assigned.
- galaxy_job_id = self.client.job_id
- self.job_id = job_config.get('job_id', galaxy_job_id)
- if self.job_id != galaxy_job_id:
- # Remote LWR server assigned an id different than the
- # Galaxy job id, update client to reflect this.
- self.client.job_id = self.job_id
-
- def __initialize_referenced_tool_files(self):
- self.referenced_tool_files = self.job_inputs.find_referenced_subfiles(self.tool_dir)
-
- def __upload_tool_files(self):
- for referenced_tool_file in self.referenced_tool_files:
- self.transfer_tracker.handle_transfer(referenced_tool_file, 'tool')
-
- def __upload_input_files(self):
- for input_file in self.input_files:
- self.__upload_input_file(input_file)
- self.__upload_input_extra_files(input_file)
-
- def __upload_input_file(self, input_file):
- if self.job_inputs.path_referenced(input_file):
- if exists(input_file):
- self.transfer_tracker.handle_transfer(input_file, 'input')
- else:
- message = "LWR: __upload_input_file called on empty or missing dataset." + \
- " So such file: [%s]" % input_file
- log.debug(message)
-
- def __upload_input_extra_files(self, input_file):
- # TODO: Determine if this is object store safe and what needs to be
- # done if it is not.
- files_path = "%s_files" % input_file[0:-len(".dat")]
- if exists(files_path) and self.job_inputs.path_referenced(files_path):
- for extra_file in listdir(files_path):
- extra_file_path = join(files_path, extra_file)
- relative_path = basename(files_path)
- extra_file_relative_path = join(relative_path, extra_file)
- self.transfer_tracker.handle_transfer(extra_file_path, 'input_extra', name=extra_file_relative_path)
-
- def __upload_working_directory_files(self):
- # Task manager stages files into working directory, these need to be
- # uploaded if present.
- for working_directory_file in listdir(self.working_directory):
- path = join(self.working_directory, working_directory_file)
- self.transfer_tracker.handle_transfer(path, 'work_dir')
-
- def __initialize_output_file_renames(self):
- for output_file in self.output_files:
- remote_path = r'%s%s%s' % (self.new_outputs_directory, self.remote_path_separator, basename(output_file))
- self.transfer_tracker.register_rewrite(output_file, remote_path, 'output')
-
- def __initialize_task_output_file_renames(self):
- for output_file in self.output_files:
- name = basename(output_file)
- task_file = join(self.working_directory, name)
- remote_path = r'%s%s%s' % (self.new_working_directory, self.remote_path_separator, name)
- self.transfer_tracker.register_rewrite(task_file, remote_path, 'output_task')
-
- def __initialize_config_file_renames(self):
- for config_file in self.config_files:
- remote_path = r'%s%s%s' % (self.new_configs_drectory, self.remote_path_separator, basename(config_file))
- self.transfer_tracker.register_rewrite(config_file, remote_path, 'config')
-
- def __handle_rewrites(self):
- """
- For each file that has been transferred and renamed, updated
- command_line and configfiles to reflect that rewrite.
- """
- self.transfer_tracker.rewrite_input_paths()
-
- def __upload_rewritten_config_files(self):
- for config_file, new_config_contents in self.job_inputs.rewritten_config_files.items():
- self.client.put_file(config_file, input_type='config', contents=new_config_contents)
-
- def get_rewritten_command_line(self):
- """
- Returns the rewritten version of the command line to execute suitable
- for remote host.
- """
- return self.job_inputs.rewritten_command_line
-
-
-def finish_job(client, cleanup_job, job_completed_normally, working_directory, work_dir_outputs, output_files, working_directory_contents=[]):
- """
- """
- download_failure_exceptions = []
- if job_completed_normally:
- download_failure_exceptions = __download_results(client, working_directory, work_dir_outputs, output_files, working_directory_contents)
- return __clean(download_failure_exceptions, cleanup_job, client)
-
-
-def __download_results(client, working_directory, work_dir_outputs, output_files, working_directory_contents):
- action_mapper = FileActionMapper(client)
- downloaded_working_directory_files = []
- exception_tracker = DownloadExceptionTracker()
-
- # Fetch explicit working directory outputs.
- for source_file, output_file in work_dir_outputs:
- name = basename(source_file)
- with exception_tracker():
- action = action_mapper.action(output_file, 'output')
- client.fetch_work_dir_output(name, working_directory, output_file, action[0])
- downloaded_working_directory_files.append(name)
- # Remove from full output_files list so don't try to download directly.
- output_files.remove(output_file)
-
- # Fetch output files.
- for output_file in output_files:
- with exception_tracker():
- action = action_mapper.action(output_file, 'output')
- client.fetch_output(output_file, working_directory=working_directory, action=action[0])
-
- # Fetch remaining working directory outputs of interest.
- for name in working_directory_contents:
- if name in downloaded_working_directory_files:
- continue
- if COPY_FROM_WORKING_DIRECTORY_PATTERN.match(name):
- with exception_tracker():
- output_file = join(working_directory, name)
- action = action_mapper.action(output_file, 'output')
- client.fetch_work_dir_output(name, working_directory, output_file, action=action[0])
- downloaded_working_directory_files.append(name)
-
- return exception_tracker.download_failure_exceptions
-
-
-class DownloadExceptionTracker(object):
-
- def __init__(self):
- self.download_failure_exceptions = []
-
- @contextmanager
- def __call__(self):
- try:
- yield
- except Exception as e:
- self.download_failure_exceptions.append(e)
-
-
-def __clean(download_failure_exceptions, cleanup_job, client):
- failed = (len(download_failure_exceptions) > 0)
- if (not failed and cleanup_job != "never") or cleanup_job == "always":
- try:
- client.clean()
- except:
- log.warn("Failed to cleanup remote LWR job")
- return failed
-
-
-def submit_job(client, client_job_description, job_config=None):
- """
- """
- file_stager = FileStager(client, client_job_description, job_config)
- rebuilt_command_line = file_stager.get_rewritten_command_line()
- job_id = file_stager.job_id
- client.launch(rebuilt_command_line, requirements=client_job_description.requirements)
- return job_id
-
-
-def _read(path):
- """
- Utility method to quickly read small files (config files and tool
- wrappers) into memory as bytes.
- """
- input = open(path, "r", encoding="utf-8")
- try:
- return input.read()
- finally:
- input.close()
-
-
-class ClientJobDescription(object):
- """ A description of how client views job - command_line, inputs, etc..
-
- **Parameters**
-
- command_line : str
- The local command line to execute, this will be rewritten for the remote server.
- config_files : list
- List of Galaxy 'configfile's produced for this job. These will be rewritten and sent to remote server.
- input_files : list
- List of input files used by job. These will be transferred and references rewritten.
- output_files : list
- List of output_files produced by job.
- tool_dir : str
- Directory containing tool to execute (if a wrapper is used, it will be transferred to remote server).
- working_directory : str
- Local path created by Galaxy for running this job.
- requirements : list
- List of requirements for tool execution.
- """
-
- def __init__(self, tool, command_line, config_files, input_files, output_files, working_directory, requirements):
- self.tool = tool
- self.command_line = command_line
- self.config_files = config_files
- self.input_files = input_files
- self.output_files = output_files
- self.working_directory = working_directory
- self.requirements = requirements
-
-__all__ = [submit_job, ClientJobDescription, finish_job]
diff -r 18b2967240e53d77ed87c3ef5aeaec532597bd58 -r 9920d006a96618d7729ef09e0fb861b22ff530db lib/galaxy/jobs/runners/lwr_client/staging/__init__.py
--- /dev/null
+++ b/lib/galaxy/jobs/runners/lwr_client/staging/__init__.py
@@ -0,0 +1,120 @@
+from os.path import basename
+from os.path import join
+from os.path import dirname
+from os import sep
+
+from ..util import PathHelper
+
+COMMAND_VERSION_FILENAME = "COMMAND_VERSION"
+
+
+class ClientJobDescription(object):
+ """ A description of how client views job - command_line, inputs, etc..
+
+ **Parameters**
+
+ command_line : str
+ The local command line to execute, this will be rewritten for the remote server.
+ config_files : list
+ List of Galaxy 'configfile's produced for this job. These will be rewritten and sent to remote server.
+ input_files : list
+ List of input files used by job. These will be transferred and references rewritten.
+ output_files : list
+ List of output_files produced by job.
+ tool_dir : str
+ Directory containing tool to execute (if a wrapper is used, it will be transferred to remote server).
+ working_directory : str
+ Local path created by Galaxy for running this job.
+ requirements : list
+ List of requirements for tool execution.
+ version_file : str
+ Path to version file expected on the client server
+ arbitrary_files : dict()
+ Additional non-input, non-tool, non-config, non-working directory files
+ to transfer before staging job. This is most likely data indices but
+ can be anything. For now these are copied into staging working
+ directory but this will be reworked to find a better, more robust
+ location.
+ rewrite_paths : boolean
+ Indicates whether paths should be rewritten in job inputs (command_line
+ and config files) while staging files).
+ """
+
+ def __init__(
+ self,
+ tool,
+ command_line,
+ config_files,
+ input_files,
+ output_files,
+ working_directory,
+ requirements,
+ version_file=None,
+ arbitrary_files=None,
+ rewrite_paths=True,
+ ):
+ self.tool = tool
+ self.command_line = command_line
+ self.config_files = config_files
+ self.input_files = input_files
+ self.output_files = output_files
+ self.working_directory = working_directory
+ self.requirements = requirements
+ self.version_file = version_file
+ self.rewrite_paths = rewrite_paths
+ self.arbitrary_files = arbitrary_files or {}
+
+
+class GalaxyOutputs(object):
+ """ Abstraction describing the output datasets EXPECTED by the Galaxy job
+ runner client. """
+
+ def __init__(self, working_directory, work_dir_outputs, output_files, version_file):
+ self.working_directory = working_directory
+ self.work_dir_outputs = work_dir_outputs
+ self.output_files = output_files
+ self.version_file = version_file
+
+
+class LwrOutputs(object):
+ """ Abstraction describing the output files PRODUCED by the remote LWR
+ server. """
+
+ def __init__(self, complete_response):
+ # Default to None instead of [] to distinguish between empty contents and it not set
+ # by the LWR - older LWR instances will not set these in complete response.
+ self.working_directory_contents = complete_response.get("working_directory_contents", None)
+ self.output_directory_contents = complete_response.get("outputs_directory_contents", None)
+ # Older (pre-2014) LWR servers will not include separator in response,
+ #so this should only be used when reasoning about outputs in
+ # subdirectories which was not previously supported.
+ self.path_helper = PathHelper(complete_response.get("system_properties", {}).get("separator", sep))
+
+ def has_output_file(self, output_file):
+ if self.output_directory_contents is None:
+ # Legacy LWR doesn't report this, return None indicating unsure if
+ # output was generated.
+ return None
+ else:
+ return basename(output_file) in self.output_directory_contents
+
+ def has_output_directory_listing(self):
+ return self.output_directory_contents is not None
+
+ def output_extras(self, output_file):
+ """
+ Returns dict mapping local path to remote name.
+ """
+ if not self.has_output_directory_listing():
+ # Fetching $output.extra_files_path is not supported with legacy
+ # LWR (pre-2014) severs.
+ return {}
+
+ output_directory = dirname(output_file)
+
+ def local_path(name):
+ return join(output_directory, self.path_helper.local_name(name))
+
+ files_directory = "%s_files%s" % (basename(output_file)[0:-len(".dat")], self.path_helper.separator)
+ names = filter(lambda o: o.startswith(files_directory), self.output_directory_contents)
+ return dict(map(lambda name: (local_path(name), name), names))
diff -r 18b2967240e53d77ed87c3ef5aeaec532597bd58 -r 9920d006a96618d7729ef09e0fb861b22ff530db lib/galaxy/jobs/runners/lwr_client/staging/down.py
--- /dev/null
+++ b/lib/galaxy/jobs/runners/lwr_client/staging/down.py
@@ -0,0 +1,127 @@
+from os.path import join
+from os.path import relpath
+from re import compile
+from contextlib import contextmanager
+
+from ..staging import COMMAND_VERSION_FILENAME
+from ..action_mapper import FileActionMapper
+
+
+from logging import getLogger
+log = getLogger(__name__)
+
+# All output files marked with from_work_dir attributes will copied or downloaded
+# this pattern picks up attiditional files to copy back - such as those
+# associated with multiple outputs and metadata configuration. Set to .* to just
+# copy everything
+COPY_FROM_WORKING_DIRECTORY_PATTERN = compile(r"primary_.*|galaxy.json|metadata_.*|dataset_\d+\.dat|dataset_\d+_files.+")
+
+
+def finish_job(client, cleanup_job, job_completed_normally, galaxy_outputs, lwr_outputs):
+ """ Responsible for downloading results from remote server and cleaning up
+ LWR staging directory (if needed.)
+ """
+ download_failure_exceptions = []
+ if job_completed_normally:
+ downloader = ResultsDownloader(client, galaxy_outputs, lwr_outputs)
+ download_failure_exceptions = downloader.download()
+ return __clean(download_failure_exceptions, cleanup_job, client)
+
+
+class ResultsDownloader(object):
+
+ def __init__(self, client, galaxy_outputs, lwr_outputs):
+ self.client = client
+ self.galaxy_outputs = galaxy_outputs
+ self.lwr_outputs = lwr_outputs
+ self.action_mapper = FileActionMapper(client)
+ self.downloaded_working_directory_files = []
+ self.exception_tracker = DownloadExceptionTracker()
+ self.output_files = galaxy_outputs.output_files
+ self.working_directory_contents = lwr_outputs.working_directory_contents or []
+
+ def download(self):
+ self.__download_working_directory_outputs()
+ self.__download_outputs()
+ self.__download_version_file()
+ self.__download_other_working_directory_files()
+ return self.exception_tracker.download_failure_exceptions
+
+ def __download_working_directory_outputs(self):
+ working_directory = self.galaxy_outputs.working_directory
+ # Fetch explicit working directory outputs.
+ for source_file, output_file in self.galaxy_outputs.work_dir_outputs:
+ name = relpath(source_file, working_directory)
+ remote_name = self.lwr_outputs.path_helper.remote_name(name)
+ with self.exception_tracker():
+ action = self.action_mapper.action(output_file, 'output_workdir')
+ self.client.fetch_work_dir_output(remote_name, working_directory, output_file, action_type=action.action_type)
+ self.downloaded_working_directory_files.append(remote_name)
+ # Remove from full output_files list so don't try to download directly.
+ self.output_files.remove(output_file)
+
+ def __download_outputs(self):
+ # Legacy LWR not returning list of files, iterate over the list of
+ # expected outputs for tool.
+ for output_file in self.output_files:
+ # Fetch ouptut directly...
+ with self.exception_tracker():
+ action = self.action_mapper.action(output_file, 'output')
+ output_generated = self.lwr_outputs.has_output_file(output_file)
+ working_directory = self.galaxy_outputs.working_directory
+ if output_generated is None:
+ self.client.fetch_output_legacy(output_file, working_directory, action_type=action.action_type)
+ elif output_generated:
+ self.client.fetch_output(output_file, action_type=action.action_type)
+
+ for local_path, remote_name in self.lwr_outputs.output_extras(output_file).iteritems():
+ with self.exception_tracker():
+ action = self.action_mapper.action(local_path, 'output')
+ self.client.fetch_output(path=local_path, name=remote_name, action_type=action.action_type)
+ # else not output generated, do not attempt download.
+
+ def __download_version_file(self):
+ version_file = self.galaxy_outputs.version_file
+ # output_directory_contents may be none for legacy LWR servers.
+ lwr_output_directory_contents = (self.lwr_outputs.output_directory_contents or [])
+ if version_file and COMMAND_VERSION_FILENAME in lwr_output_directory_contents:
+ action = self.action_mapper.action(version_file, 'output')
+ self.client.fetch_output(path=version_file, name=COMMAND_VERSION_FILENAME, action_type=action.action_type)
+
+ def __download_other_working_directory_files(self):
+ working_directory = self.galaxy_outputs.working_directory
+ # Fetch remaining working directory outputs of interest.
+ for name in self.working_directory_contents:
+ if name in self.downloaded_working_directory_files:
+ continue
+ if COPY_FROM_WORKING_DIRECTORY_PATTERN.match(name):
+ with self.exception_tracker():
+ output_file = join(working_directory, self.lwr_outputs.path_helper.local_name(name))
+ action = self.action_mapper.action(output_file, 'output_workdir')
+ self.client.fetch_work_dir_output(name, working_directory, output_file, action_type=action.action_type)
+ self.downloaded_working_directory_files.append(name)
+
+
+class DownloadExceptionTracker(object):
+
+ def __init__(self):
+ self.download_failure_exceptions = []
+
+ @contextmanager
+ def __call__(self):
+ try:
+ yield
+ except Exception as e:
+ self.download_failure_exceptions.append(e)
+
+
+def __clean(download_failure_exceptions, cleanup_job, client):
+ failed = (len(download_failure_exceptions) > 0)
+ if (not failed and cleanup_job != "never") or cleanup_job == "always":
+ try:
+ client.clean()
+ except:
+ log.warn("Failed to cleanup remote LWR job")
+ return failed
+
+__all__ = [finish_job]
diff -r 18b2967240e53d77ed87c3ef5aeaec532597bd58 -r 9920d006a96618d7729ef09e0fb861b22ff530db lib/galaxy/jobs/runners/lwr_client/staging/up.py
--- /dev/null
+++ b/lib/galaxy/jobs/runners/lwr_client/staging/up.py
@@ -0,0 +1,373 @@
+from os.path import abspath, basename, join, exists
+from os.path import dirname
+from os.path import relpath
+from os import listdir, sep
+from re import findall
+from io import open
+
+from ..staging import COMMAND_VERSION_FILENAME
+from ..action_mapper import FileActionMapper
+from ..action_mapper import path_type
+from ..util import PathHelper
+from ..util import directory_files
+
+from logging import getLogger
+log = getLogger(__name__)
+
+
+def submit_job(client, client_job_description, job_config=None):
+ """
+ """
+ file_stager = FileStager(client, client_job_description, job_config)
+ rebuilt_command_line = file_stager.get_command_line()
+ job_id = file_stager.job_id
+ client.launch(rebuilt_command_line, requirements=client_job_description.requirements)
+ return job_id
+
+
+class FileStager(object):
+ """
+ Objects of the FileStager class interact with an LWR client object to
+ stage the files required to run jobs on a remote LWR server.
+
+ **Parameters**
+
+ client : JobClient
+ LWR client object.
+ client_job_description : client_job_description
+ Description of client view of job to stage and execute remotely.
+ """
+
+ def __init__(self, client, client_job_description, job_config):
+ """
+ """
+ self.client = client
+ self.command_line = client_job_description.command_line
+ self.config_files = client_job_description.config_files
+ self.input_files = client_job_description.input_files
+ self.output_files = client_job_description.output_files
+ self.tool_id = client_job_description.tool.id
+ self.tool_version = client_job_description.tool.version
+ self.tool_dir = abspath(client_job_description.tool.tool_dir)
+ self.working_directory = client_job_description.working_directory
+ self.version_file = client_job_description.version_file
+ self.arbitrary_files = client_job_description.arbitrary_files
+ self.rewrite_paths = client_job_description.rewrite_paths
+
+ # Setup job inputs, these will need to be rewritten before
+ # shipping off to remote LWR server.
+ self.job_inputs = JobInputs(self.command_line, self.config_files)
+
+ self.action_mapper = FileActionMapper(client)
+ self.transfer_tracker = TransferTracker(client, self.action_mapper, self.job_inputs, rewrite_paths=self.rewrite_paths)
+
+ self.__handle_setup(job_config)
+ self.__initialize_referenced_tool_files()
+ if self.rewrite_paths:
+ self.__initialize_referenced_arbitrary_files()
+
+ self.__upload_tool_files()
+ self.__upload_input_files()
+ self.__upload_working_directory_files()
+ self.__upload_arbitrary_files()
+
+ if self.rewrite_paths:
+ self.__initialize_output_file_renames()
+ self.__initialize_task_output_file_renames()
+ self.__initialize_config_file_renames()
+ self.__initialize_version_file_rename()
+
+ self.__handle_rewrites()
+
+ self.__upload_rewritten_config_files()
+
+ def __handle_setup(self, job_config):
+ if not job_config:
+ job_config = self.client.setup(self.tool_id, self.tool_version)
+
+ self.new_working_directory = job_config['working_directory']
+ self.new_outputs_directory = job_config['outputs_directory']
+ # Default configs_directory to match remote working_directory to mimic
+ # behavior of older LWR servers.
+ self.new_configs_directory = job_config.get('configs_directory', self.new_working_directory)
+ self.remote_separator = self.__parse_remote_separator(job_config)
+ self.path_helper = PathHelper(self.remote_separator)
+ # If remote LWR server assigned job id, use that otherwise
+ # just use local job_id assigned.
+ galaxy_job_id = self.client.job_id
+ self.job_id = job_config.get('job_id', galaxy_job_id)
+ if self.job_id != galaxy_job_id:
+ # Remote LWR server assigned an id different than the
+ # Galaxy job id, update client to reflect this.
+ self.client.job_id = self.job_id
+
+ def __parse_remote_separator(self, job_config):
+ separator = job_config.get("system_properties", {}).get("separator", None)
+ if not separator: # Legacy LWR
+ separator = job_config["path_separator"] # Poorly named
+ return separator
+
+ def __initialize_referenced_tool_files(self):
+ self.referenced_tool_files = self.job_inputs.find_referenced_subfiles(self.tool_dir)
+
+ def __initialize_referenced_arbitrary_files(self):
+ referenced_arbitrary_path_mappers = dict()
+ for mapper in self.action_mapper.unstructured_mappers():
+ mapper_pattern = mapper.to_pattern()
+ # TODO: Make more sophisticated, allow parent directories,
+ # grabbing sibbling files based on patterns, etc...
+ paths = self.job_inputs.find_pattern_references(mapper_pattern)
+ for path in paths:
+ if path not in referenced_arbitrary_path_mappers:
+ referenced_arbitrary_path_mappers[path] = mapper
+ for path, mapper in referenced_arbitrary_path_mappers.iteritems():
+ action = self.action_mapper.action(path, path_type.UNSTRUCTURED, mapper)
+ unstructured_map = action.unstructured_map()
+ self.arbitrary_files.update(unstructured_map)
+
+ def __upload_tool_files(self):
+ for referenced_tool_file in self.referenced_tool_files:
+ self.transfer_tracker.handle_transfer(referenced_tool_file, path_type.TOOL)
+
+ def __upload_arbitrary_files(self):
+ for path, name in self.arbitrary_files.iteritems():
+ self.transfer_tracker.handle_transfer(path, path_type.UNSTRUCTURED, name=name)
+
+ def __upload_input_files(self):
+ for input_file in self.input_files:
+ self.__upload_input_file(input_file)
+ self.__upload_input_extra_files(input_file)
+
+ def __upload_input_file(self, input_file):
+ if self.__stage_input(input_file):
+ if exists(input_file):
+ self.transfer_tracker.handle_transfer(input_file, path_type.INPUT)
+ else:
+ message = "LWR: __upload_input_file called on empty or missing dataset." + \
+ " So such file: [%s]" % input_file
+ log.debug(message)
+
+ def __upload_input_extra_files(self, input_file):
+ files_path = "%s_files" % input_file[0:-len(".dat")]
+ if exists(files_path) and self.__stage_input(files_path):
+ for extra_file_name in directory_files(files_path):
+ extra_file_path = join(files_path, extra_file_name)
+ remote_name = self.path_helper.remote_name(relpath(extra_file_path, dirname(files_path)))
+ self.transfer_tracker.handle_transfer(extra_file_path, path_type.INPUT, name=remote_name)
+
+ def __upload_working_directory_files(self):
+ # Task manager stages files into working directory, these need to be
+ # uploaded if present.
+ working_directory_files = listdir(self.working_directory) if exists(self.working_directory) else []
+ for working_directory_file in working_directory_files:
+ path = join(self.working_directory, working_directory_file)
+ self.transfer_tracker.handle_transfer(path, 'workdir')
+
+ def __initialize_version_file_rename(self):
+ version_file = self.version_file
+ if version_file:
+ remote_path = self.path_helper.remote_join(self.new_outputs_directory, COMMAND_VERSION_FILENAME)
+ self.transfer_tracker.register_rewrite(version_file, remote_path, path_type.OUTPUT)
+
+ def __initialize_output_file_renames(self):
+ for output_file in self.output_files:
+ remote_path = self.path_helper.remote_join(self.new_outputs_directory, basename(output_file))
+ self.transfer_tracker.register_rewrite(output_file, remote_path, path_type.OUTPUT)
+
+ def __initialize_task_output_file_renames(self):
+ for output_file in self.output_files:
+ name = basename(output_file)
+ task_file = join(self.working_directory, name)
+ remote_path = self.path_helper.remote_join(self.new_working_directory, name)
+ self.transfer_tracker.register_rewrite(task_file, remote_path, path_type.OUTPUT_WORKDIR)
+
+ def __initialize_config_file_renames(self):
+ for config_file in self.config_files:
+ remote_path = self.path_helper.remote_join(self.new_configs_directory, basename(config_file))
+ self.transfer_tracker.register_rewrite(config_file, remote_path, path_type.CONFIG)
+
+ def __handle_rewrites(self):
+ """
+ For each file that has been transferred and renamed, updated
+ command_line and configfiles to reflect that rewrite.
+ """
+ self.transfer_tracker.rewrite_input_paths()
+
+ def __upload_rewritten_config_files(self):
+ for config_file, new_config_contents in self.job_inputs.config_files.items():
+ self.client.put_file(config_file, input_type='config', contents=new_config_contents)
+
+ def get_command_line(self):
+ """
+ Returns the rewritten version of the command line to execute suitable
+ for remote host.
+ """
+ return self.job_inputs.command_line
+
+ def __stage_input(self, file_path):
+ # If we have disabled path rewriting, just assume everything needs to be transferred,
+ # else check to ensure the file is referenced before transferring it.
+ return (not self.rewrite_paths) or self.job_inputs.path_referenced(file_path)
+
+
+class JobInputs(object):
+ """
+ Abstractions over dynamic inputs created for a given job (namely the command to
+ execute and created configfiles).
+
+ **Parameters**
+
+ command_line : str
+ Local command to execute for this job. (To be rewritten.)
+ config_files : str
+ Config files created for this job. (To be rewritten.)
+
+
+ >>> import tempfile
+ >>> tf = tempfile.NamedTemporaryFile()
+ >>> def setup_inputs(tf):
+ ... open(tf.name, "w").write(u"world /path/to/input the rest")
+ ... inputs = JobInputs(u"hello /path/to/input", [tf.name])
+ ... return inputs
+ >>> inputs = setup_inputs(tf)
+ >>> inputs.rewrite_paths(u"/path/to/input", u'C:\\input')
+ >>> inputs.command_line == u'hello C:\\\\input'
+ True
+ >>> inputs.config_files[tf.name] == u'world C:\\\\input the rest'
+ True
+ >>> tf.close()
+ >>> tf = tempfile.NamedTemporaryFile()
+ >>> inputs = setup_inputs(tf)
+ >>> inputs.find_referenced_subfiles('/path/to') == [u'/path/to/input']
+ True
+ >>> inputs.path_referenced('/path/to')
+ True
+ >>> inputs.path_referenced(u'/path/to')
+ True
+ >>> inputs.path_referenced('/path/to/input')
+ True
+ >>> inputs.path_referenced('/path/to/notinput')
+ False
+ >>> tf.close()
+ """
+
+ def __init__(self, command_line, config_files):
+ self.command_line = command_line
+ self.config_files = {}
+ for config_file in config_files or []:
+ config_contents = _read(config_file)
+ self.config_files[config_file] = config_contents
+
+ def find_pattern_references(self, pattern):
+ referenced_files = set()
+ for input_contents in self.__items():
+ referenced_files.update(findall(pattern, input_contents))
+ return list(referenced_files)
+
+ def find_referenced_subfiles(self, directory):
+ """
+ Return list of files below specified `directory` in job inputs. Could
+ use more sophisticated logic (match quotes to handle spaces, handle
+ subdirectories, etc...).
+
+ **Parameters**
+
+ directory : str
+ Full path to directory to search.
+
+ """
+ pattern = r"(%s%s\S+)" % (directory, sep)
+ return self.find_pattern_references(pattern)
+
+ def path_referenced(self, path):
+ pattern = r"%s" % path
+ found = False
+ for input_contents in self.__items():
+ if findall(pattern, input_contents):
+ found = True
+ break
+ return found
+
+ def rewrite_paths(self, local_path, remote_path):
+ """
+ Rewrite references to `local_path` with `remote_path` in job inputs.
+ """
+ self.__rewrite_command_line(local_path, remote_path)
+ self.__rewrite_config_files(local_path, remote_path)
+
+ def __rewrite_command_line(self, local_path, remote_path):
+ self.command_line = self.command_line.replace(local_path, remote_path)
+
+ def __rewrite_config_files(self, local_path, remote_path):
+ for config_file, contents in self.config_files.items():
+ self.config_files[config_file] = contents.replace(local_path, remote_path)
+
+ def __items(self):
+ items = [self.command_line]
+ items.extend(self.config_files.values())
+ return items
+
+
+class TransferTracker(object):
+
+ def __init__(self, client, action_mapper, job_inputs, rewrite_paths):
+ self.client = client
+ self.action_mapper = action_mapper
+
+ self.job_inputs = job_inputs
+ self.rewrite_paths = rewrite_paths
+ self.file_renames = {}
+
+ def handle_transfer(self, path, type, name=None, contents=None):
+ if contents:
+ # If contents loaded in memory, no need to write out file and copy,
+ # just transfer.
+ action_type = 'transfer'
+ else:
+ if not exists(path):
+ message = "handle_tranfer called on non-existent file - [%s]" % path
+ log.warn(message)
+ raise Exception(message)
+ action_type = self.__action(path, type).action_type
+
+ if action_type in ['transfer', 'copy']:
+ response = self.client.put_file(path, type, name=name, contents=contents)
+ register = self.rewrite_paths or type == 'tool' # Even if inputs not rewritten, tool must be.
+ if register:
+ self.register_rewrite(path, response['path'], type, force=True)
+ elif action_type == 'none':
+ # No action for this file.
+ pass
+ else:
+ raise Exception("Unknown action type (%s) encountered for path (%s)" % (action_type, path))
+
+ def register_rewrite(self, local_path, remote_path, type, force=False):
+ action = self.__action(local_path, type)
+ if action.action_type in ['transfer', 'copy'] or force:
+ self.file_renames[local_path] = remote_path
+
+ def rewrite_input_paths(self):
+ """
+ For each file that has been transferred and renamed, updated
+ command_line and configfiles to reflect that rewrite.
+ """
+ for local_path, remote_path in self.file_renames.items():
+ self.job_inputs.rewrite_paths(local_path, remote_path)
+
+ def __action(self, path, type):
+ return self.action_mapper.action(path, type)
+
+
+def _read(path):
+ """
+ Utility method to quickly read small files (config files and tool
+ wrappers) into memory as bytes.
+ """
+ input = open(path, "r", encoding="utf-8")
+ try:
+ return input.read()
+ finally:
+ input.close()
+
+
+__all__ = [submit_job]
diff -r 18b2967240e53d77ed87c3ef5aeaec532597bd58 -r 9920d006a96618d7729ef09e0fb861b22ff530db lib/galaxy/jobs/runners/lwr_client/util.py
--- a/lib/galaxy/jobs/runners/lwr_client/util.py
+++ b/lib/galaxy/jobs/runners/lwr_client/util.py
@@ -1,5 +1,85 @@
from threading import Lock, Event
from weakref import WeakValueDictionary
+from os import walk
+from os import curdir
+from os.path import relpath
+from os.path import join
+import os.path
+import hashlib
+
+
+def unique_path_prefix(path):
+ m = hashlib.md5()
+ m.update(path)
+ return m.hexdigest()
+
+
+def directory_files(directory):
+ """
+
+ >>> from tempfile import mkdtemp
+ >>> from shutil import rmtree
+ >>> from os.path import join
+ >>> from os import makedirs
+ >>> tempdir = mkdtemp()
+ >>> with open(join(tempdir, "moo"), "w") as f: pass
+ >>> directory_files(tempdir)
+ ['moo']
+ >>> subdir = join(tempdir, "cow", "sub1")
+ >>> makedirs(subdir)
+ >>> with open(join(subdir, "subfile1"), "w") as f: pass
+ >>> with open(join(subdir, "subfile2"), "w") as f: pass
+ >>> sorted(directory_files(tempdir))
+ ['cow/sub1/subfile1', 'cow/sub1/subfile2', 'moo']
+ >>> rmtree(tempdir)
+ """
+ contents = []
+ for path, _, files in walk(directory):
+ relative_path = relpath(path, directory)
+ for name in files:
+ # Return file1.txt, dataset_1_files/image.png, etc... don't
+ # include . in path.
+ if relative_path != curdir:
+ contents.append(join(relative_path, name))
+ else:
+ contents.append(name)
+ return contents
+
+
+class PathHelper(object):
+ '''
+
+ >>> import posixpath
+ >>> # Forcing local path to posixpath because LWR designed to be used with
+ >>> # posix client.
+ >>> posix_path_helper = PathHelper("/", local_path_module=posixpath)
+ >>> windows_slash = "\\\\"
+ >>> len(windows_slash)
+ 1
+ >>> nt_path_helper = PathHelper(windows_slash, local_path_module=posixpath)
+ >>> posix_path_helper.remote_name("moo/cow")
+ 'moo/cow'
+ >>> nt_path_helper.remote_name("moo/cow")
+ 'moo\\\\cow'
+ >>> posix_path_helper.local_name("moo/cow")
+ 'moo/cow'
+ >>> nt_path_helper.local_name("moo\\\\cow")
+ 'moo/cow'
+ '''
+
+ def __init__(self, separator, local_path_module=os.path):
+ self.separator = separator
+ self.local_join = local_path_module.join
+ self.local_sep = local_path_module.sep
+
+ def remote_name(self, local_name):
+ return self.remote_join(*local_name.split(self.local_sep))
+
+ def local_name(self, remote_name):
+ return self.local_join(*remote_name.split(self.separator))
+
+ def remote_join(self, *args):
+ return self.separator.join(args)
class TransferEventManager(object):
https://bitbucket.org/galaxy/galaxy-central/commits/6da672723d5c/
Changeset: 6da672723d5c
User: jmchilton
Date: 2014-01-03 00:50:11
Summary: Fix bug in from_work_dir location checking...
... would only be exhibited if using LWR + 'remote_metadata'.
Affected #: 1 file
diff -r 9920d006a96618d7729ef09e0fb861b22ff530db -r 6da672723d5c6e55347abb57de399460c39b5d38 lib/galaxy/jobs/runners/__init__.py
--- a/lib/galaxy/jobs/runners/__init__.py
+++ b/lib/galaxy/jobs/runners/__init__.py
@@ -179,7 +179,7 @@
# TODO: move instead of copy to save time?
source_file = os.path.join( job_working_directory, hda_tool_output.from_work_dir )
destination = job_wrapper.get_output_destination( output_paths[ dataset.dataset_id ] )
- if in_directory( source_file, job_wrapper.working_directory ):
+ if in_directory( source_file, job_working_directory ):
output_pairs.append( ( source_file, destination ) )
log.debug( "Copying %s to %s as directed by from_work_dir" % ( source_file, destination ) )
else:
https://bitbucket.org/galaxy/galaxy-central/commits/4529b941cf27/
Changeset: 4529b941cf27
User: jmchilton
Date: 2014-01-03 00:50:12
Summary: PEP-8 fixes for local job runner.
Affected #: 1 file
diff -r 6da672723d5c6e55347abb57de399460c39b5d38 -r 4529b941cf2751323d2a5cb8875dfe8bf3ccb02c lib/galaxy/jobs/runners/local.py
--- a/lib/galaxy/jobs/runners/local.py
+++ b/lib/galaxy/jobs/runners/local.py
@@ -18,11 +18,13 @@
__all__ = [ 'LocalJobRunner' ]
+
class LocalJobRunner( BaseJobRunner ):
"""
Job runner backed by a finite pool of worker threads. FIFO scheduling
"""
runner_name = "LocalRunner"
+
def __init__( self, app, nworkers ):
"""Start the job runner """
@@ -74,13 +76,13 @@
log.debug( '(%s) executing: %s' % ( job_id, command_line ) )
stdout_file = tempfile.NamedTemporaryFile( suffix='_stdout', dir=job_wrapper.working_directory )
stderr_file = tempfile.NamedTemporaryFile( suffix='_stderr', dir=job_wrapper.working_directory )
- proc = subprocess.Popen( args = command_line,
- shell = True,
- cwd = job_wrapper.working_directory,
- stdout = stdout_file,
- stderr = stderr_file,
- env = self._environ,
- preexec_fn = os.setpgrp )
+ proc = subprocess.Popen( args=command_line,
+ shell=True,
+ cwd=job_wrapper.working_directory,
+ stdout=stdout_file,
+ stderr=stderr_file,
+ env=self._environ,
+ preexec_fn=os.setpgrp )
job_wrapper.set_job_destination(job_wrapper.job_destination, proc.pid)
job_wrapper.change_state( model.Job.states.RUNNING )
job_start = datetime.datetime.now()
@@ -89,7 +91,7 @@
while proc.poll() is None:
i += 1
if (i % 20) == 0:
- msg = job_wrapper.check_limits(runtime = datetime.datetime.now() - job_start)
+ msg = job_wrapper.check_limits(runtime=datetime.datetime.now() - job_start)
if msg is not None:
job_wrapper.fail(msg)
log.debug('(%s) Terminating process group' % job_id)
@@ -122,7 +124,7 @@
#if our local job has JobExternalOutputMetadata associated, then our primary job has to have already finished
job_ext_output_metadata = job.get_external_output_metadata()
if job_ext_output_metadata:
- pid = job_ext_output_metadata[0].job_runner_external_pid #every JobExternalOutputMetadata has a pid set, we just need to take from one of them
+ pid = job_ext_output_metadata[0].job_runner_external_pid # every JobExternalOutputMetadata has a pid set, we just need to take from one of them
else:
pid = job.get_job_runner_external_id()
if pid in [ None, '' ]:
@@ -140,14 +142,14 @@
return # give up
sleep( 2 )
if not self._check_pid( pid ):
- log.debug( "stop_job(): %s: PID %d successfully killed with signal %d" %( job.get_id(), pid, sig ) )
+ log.debug( "stop_job(): %s: PID %d successfully killed with signal %d" % ( job.get_id(), pid, sig ) )
return
else:
- log.warning( "stop_job(): %s: PID %d refuses to die after signaling TERM/KILL" %( job.get_id(), pid ) )
+ log.warning( "stop_job(): %s: PID %d refuses to die after signaling TERM/KILL" % ( job.get_id(), pid ) )
def recover( self, job, job_wrapper ):
# local jobs can't be recovered
- job_wrapper.change_state( model.Job.states.ERROR, info = "This job was killed when Galaxy was restarted. Please retry the job." )
+ job_wrapper.change_state( model.Job.states.ERROR, info="This job was killed when Galaxy was restarted. Please retry the job." )
def _check_pid( self, pid ):
try:
@@ -157,7 +159,7 @@
if e.errno == errno.ESRCH:
log.debug( "_check_pid(): PID %d is dead" % pid )
else:
- log.warning( "_check_pid(): Got errno %s when attempting to check PID %d: %s" %( errno.errorcode[e.errno], pid, e.strerror ) )
+ log.warning( "_check_pid(): Got errno %s when attempting to check PID %d: %s" % ( errno.errorcode[e.errno], pid, e.strerror ) )
return False
def _terminate( self, proc ):
@@ -165,4 +167,4 @@
sleep( 1 )
if proc.poll() is None:
os.killpg( proc.pid, 9 )
- return proc.wait() # reap
+ return proc.wait() # reap
https://bitbucket.org/galaxy/galaxy-central/commits/18e6f346d6b1/
Changeset: 18e6f346d6b1
User: jmchilton
Date: 2014-01-03 00:50:12
Summary: PEP-8 fixes for jobs/__init__.py
Affected #: 1 file
diff -r 4529b941cf2751323d2a5cb8875dfe8bf3ccb02c -r 18e6f346d6b13521c9757989bfb7fc06791e561b lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -58,6 +58,7 @@
if self.tags is not None:
self['tags'] = [ x.strip() for x in self.tags.split(',') ]
+
class JobToolConfiguration( Bunch ):
"""
Provides details on what handler and destination a tool should use
@@ -71,12 +72,14 @@
self['params'] = dict()
super(JobToolConfiguration, self).__init__(**kwds)
+
class JobConfiguration( object ):
"""A parser and interface to advanced job management features.
These features are configured in the job configuration, by default, ``job_conf.xml``
"""
DEFAULT_NWORKERS = 4
+
def __init__(self, app):
"""Parse the job configuration XML.
"""
@@ -172,17 +175,17 @@
self.tools[id].append(JobToolConfiguration(**dict(tool.items())))
self.tools[id][-1]['params'] = self.__get_params(tool)
- types = dict(registered_user_concurrent_jobs = int,
- anonymous_user_concurrent_jobs = int,
- walltime = str,
- output_size = int)
+ types = dict(registered_user_concurrent_jobs=int,
+ anonymous_user_concurrent_jobs=int,
+ walltime=str,
+ output_size=int)
- self.limits = Bunch(registered_user_concurrent_jobs = None,
- anonymous_user_concurrent_jobs = None,
- walltime = None,
- walltime_delta = None,
- output_size = None,
- concurrent_jobs = {})
+ self.limits = Bunch(registered_user_concurrent_jobs=None,
+ anonymous_user_concurrent_jobs=None,
+ walltime=None,
+ walltime_delta=None,
+ output_size=None,
+ concurrent_jobs={})
# Parse job limits
limits = root.find('limits')
@@ -253,12 +256,12 @@
self.default_destination_id = self.app.config.default_cluster_job_runner
# Set the job limits
- self.limits = Bunch(registered_user_concurrent_jobs = self.app.config.registered_user_job_limit,
- anonymous_user_concurrent_jobs = self.app.config.anonymous_user_job_limit,
- walltime = self.app.config.job_walltime,
- walltime_delta = self.app.config.job_walltime_delta,
- output_size = self.app.config.output_size_limit,
- concurrent_jobs = {})
+ self.limits = Bunch(registered_user_concurrent_jobs=self.app.config.registered_user_job_limit,
+ anonymous_user_concurrent_jobs=self.app.config.anonymous_user_job_limit,
+ walltime=self.app.config.job_walltime,
+ walltime_delta=self.app.config.job_walltime_delta,
+ output_size=self.app.config.output_size_limit,
+ concurrent_jobs={})
log.debug('Done loading job configuration')
@@ -533,6 +536,7 @@
else:
log.warning("Legacy destination with id '%s' could not be converted: Unknown runner plugin: %s" % (id, destination.runner))
+
class JobWrapper( object ):
"""
Wraps a 'model.Job' with convenience methods for running processes and
@@ -627,7 +631,7 @@
Prepare the job to run by creating the working directory and the
config files.
"""
- self.sa_session.expunge_all() #this prevents the metadata reverting that has been seen in conjunction with the PBS job runner
+ self.sa_session.expunge_all() # this prevents the metadata reverting that has been seen in conjunction with the PBS job runner
if not os.path.exists( self.working_directory ):
os.mkdir( self.working_directory )
@@ -686,7 +690,7 @@
self.command_line = self.tool.build_command_line( param_dict )
# FIXME: for now, tools get Galaxy's lib dir in their path
if self.command_line and self.command_line.startswith( 'python' ):
- self.galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root
+ self.galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root
# Shell fragment to inject dependencies
self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
# We need command_line persisted to the db in order for Galaxy to re-queue the job
@@ -719,7 +723,7 @@
job.traceback = traceback.format_exc()
# Get the exception and let the tool attempt to generate
# a better message
- etype, evalue, tb = sys.exc_info()
+ etype, evalue, tb = sys.exc_info()
m = self.tool.handle_job_failure_exception( evalue )
if m:
message = m
@@ -790,7 +794,7 @@
job.state = job.states.PAUSED
self.sa_session.add( job )
- def change_state( self, state, info = False ):
+ def change_state( self, state, info=False ):
job = self.get_job()
self.sa_session.refresh( job )
for dataset_assoc in job.output_datasets + job.output_library_datasets:
@@ -816,7 +820,7 @@
log.warning('set_runner() is deprecated, use set_job_destination()')
self.set_job_destination(self.job_destination, external_id)
- def set_job_destination(self, job_destination, external_id=None ):
+ def set_job_destination( self, job_destination, external_id=None ):
"""
Persist job destination params in the database for recovery.
@@ -896,11 +900,11 @@
job.state = final_job_state
return self.fail( "Job %s's output dataset(s) could not be read" % job.id )
- job_context = ExpressionContext( dict( stdout = job.stdout, stderr = job.stderr ) )
+ job_context = ExpressionContext( dict( stdout=job.stdout, stderr=job.stderr ) )
for dataset_assoc in job.output_datasets + job.output_library_datasets:
context = self.get_dataset_finish_context( job_context, dataset_assoc.dataset.dataset )
#should this also be checking library associations? - can a library item be added from a history before the job has ended? - lets not allow this to occur
- for dataset in dataset_assoc.dataset.dataset.history_associations + dataset_assoc.dataset.dataset.library_associations: #need to update all associated output hdas, i.e. history was shared with job running
+ for dataset in dataset_assoc.dataset.dataset.history_associations + dataset_assoc.dataset.dataset.library_associations: # need to update all associated output hdas, i.e. history was shared with job running
trynum = 0
while trynum < self.app.config.retry_job_output_collection:
try:
@@ -911,9 +915,9 @@
except ( OSError, ObjectNotFound ), e:
trynum += 1
log.warning( 'Error accessing %s, will retry: %s', dataset.dataset.file_name, e )
- time.sleep( 2 )
+ time.sleep( 2 )
dataset.blurb = 'done'
- dataset.peek = 'no peek'
+ dataset.peek = 'no peek'
dataset.info = (dataset.info or '')
if context['stdout'].strip():
#Ensure white space between entries
@@ -941,7 +945,7 @@
#it would be quicker to just copy the metadata from the originating output dataset,
#but somewhat trickier (need to recurse up the copied_from tree), for now we'll call set_meta()
if ( not self.external_output_metadata.external_metadata_set_successfully( dataset, self.sa_session ) and self.app.config.retry_metadata_internally ):
- dataset.datatype.set_meta( dataset, overwrite = False ) #call datatype.set_meta directly for the initial set_meta call during dataset creation
+ dataset.datatype.set_meta( dataset, overwrite=False ) # call datatype.set_meta directly for the initial set_meta call during dataset creation
elif not self.external_output_metadata.external_metadata_set_successfully( dataset, self.sa_session ) and job.states.ERROR != final_job_state:
dataset._state = model.Dataset.states.FAILED_METADATA
else:
@@ -1011,19 +1015,22 @@
out_data = dict( [ ( da.name, da.dataset ) for da in job.output_datasets ] )
inp_data.update( [ ( da.name, da.dataset ) for da in job.input_library_datasets ] )
out_data.update( [ ( da.name, da.dataset ) for da in job.output_library_datasets ] )
- param_dict = dict( [ ( p.name, p.value ) for p in job.parameters ] ) # why not re-use self.param_dict here? ##dunno...probably should, this causes tools.parameters.basic.UnvalidatedValue to be used in following methods instead of validated and transformed values during i.e. running workflows
+ param_dict = dict( [ ( p.name, p.value ) for p in job.parameters ] ) # why not re-use self.param_dict here? ##dunno...probably should, this causes tools.parameters.basic.UnvalidatedValue to be used in following methods instead of validated and transformed values during i.e. running workflows
param_dict = self.tool.params_from_strings( param_dict, self.app )
# Check for and move associated_files
self.tool.collect_associated_files(out_data, self.working_directory)
gitd = self.sa_session.query( model.GenomeIndexToolData ).filter_by( job=job ).first()
if gitd:
- self.tool.collect_associated_files({'' : gitd}, self.working_directory)
+ self.tool.collect_associated_files({'': gitd}, self.working_directory)
# Create generated output children and primary datasets and add to param_dict
- collected_datasets = {'children':self.tool.collect_child_datasets(out_data, self.working_directory),'primary':self.tool.collect_primary_datasets(out_data, self.working_directory)}
- param_dict.update({'__collected_datasets__':collected_datasets})
+ collected_datasets = {
+ 'children': self.tool.collect_child_datasets(out_data, self.working_directory),
+ 'primary': self.tool.collect_primary_datasets(out_data, self.working_directory)
+ }
+ param_dict.update({'__collected_datasets__': collected_datasets})
# Certain tools require tasks to be completed after job execution
# ( this used to be performed in the "exec_after_process" hook, but hooks are deprecated ).
- self.tool.exec_after_process( self.queue.app, inp_data, out_data, param_dict, job = job )
+ self.tool.exec_after_process( self.queue.app, inp_data, out_data, param_dict, job=job )
# Call 'exec_after_process' hook
self.tool.call_hook( 'exec_after_process', self.queue.app, inp_data=inp_data,
out_data=out_data, param_dict=param_dict,
@@ -1114,7 +1121,7 @@
def get_input_fnames( self ):
job = self.get_job()
filenames = []
- for da in job.input_datasets + job.input_library_datasets: #da is JobToInputDatasetAssociation object
+ for da in job.input_datasets + job.input_library_datasets: # da is JobToInputDatasetAssociation object
if da.dataset:
filenames.extend(self.get_input_dataset_fnames(da.dataset))
return filenames
@@ -1136,11 +1143,12 @@
def compute_outputs( self ) :
class DatasetPath( object ):
- def __init__( self, dataset_id, real_path, false_path = None, mutable = True ):
+ def __init__( self, dataset_id, real_path, false_path=None, mutable=True ):
self.dataset_id = dataset_id
self.real_path = real_path
self.false_path = false_path
self.mutable = mutable
+
def __str__( self ):
if self.false_path is None:
return self.real_path
@@ -1157,13 +1165,13 @@
self.output_hdas_and_paths = {}
for name, hda in [ ( da.name, da.dataset ) for da in job.output_datasets + job.output_library_datasets ]:
false_path = os.path.abspath( os.path.join( self.working_directory, "galaxy_dataset_%d.dat" % hda.dataset.id ) )
- dsp = DatasetPath( hda.dataset.id, hda.dataset.file_name, false_path, mutable = hda.dataset.external_filename is None )
+ dsp = DatasetPath( hda.dataset.id, hda.dataset.file_name, false_path, mutable=hda.dataset.external_filename is None )
self.output_paths.append( dsp )
self.output_hdas_and_paths[name] = hda, dsp
if special:
false_path = os.path.abspath( os.path.join( self.working_directory, "galaxy_dataset_%d.dat" % special.dataset.id ) )
else:
- results = [ ( da.name, da.dataset, DatasetPath( da.dataset.dataset.id, da.dataset.file_name, mutable = da.dataset.dataset.external_filename is None ) ) for da in job.output_datasets + job.output_library_datasets ]
+ results = [ ( da.name, da.dataset, DatasetPath( da.dataset.dataset.id, da.dataset.file_name, mutable=da.dataset.dataset.external_filename is None ) ) for da in job.output_datasets + job.output_library_datasets ]
self.output_paths = [t[2] for t in results]
self.output_hdas_and_paths = dict([(t[0], t[1:]) for t in results])
if special:
@@ -1238,13 +1246,13 @@
datatypes_config = self.app.datatypes_registry.integrated_datatypes_configs
return self.external_output_metadata.setup_external_metadata( [ output_dataset_assoc.dataset for output_dataset_assoc in job.output_datasets ],
self.sa_session,
- exec_dir = exec_dir,
- tmp_dir = tmp_dir,
- dataset_files_path = dataset_files_path,
- config_root = config_root,
- config_file = config_file,
- datatypes_config = datatypes_config,
- job_metadata = os.path.join( self.working_directory, TOOL_PROVIDED_JOB_METADATA_FILE ),
+ exec_dir=exec_dir,
+ tmp_dir=tmp_dir,
+ dataset_files_path=dataset_files_path,
+ config_root=config_root,
+ config_file=config_file,
+ datatypes_config=datatypes_config,
+ job_metadata=os.path.join( self.working_directory, TOOL_PROVIDED_JOB_METADATA_FILE ),
**kwds )
@property
@@ -1318,7 +1326,7 @@
just copy these files directly to the ulimate destination.
"""
return output_path
-
+
@property
def requires_setting_metadata( self ):
if self.tool:
@@ -1391,7 +1399,7 @@
out_data.update( [ ( da.name, da.dataset ) for da in job.output_library_datasets ] )
# DBTODO New method for generating command line for a task?
# These can be passed on the command line if wanted as $userId $userEmail
- if job.history and job.history.user: # check for anonymous user!
+ if job.history and job.history.user: # check for anonymous user!
userId = '%d' % job.history.user.id
userEmail = str(job.history.user.email)
else:
@@ -1430,7 +1438,7 @@
self.command_line = self.command_line.replace(k, v)
# FIXME: for now, tools get Galaxy's lib dir in their path
if self.command_line and self.command_line.startswith( 'python' ):
- self.galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root
+ self.galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root
# Shell fragment to inject dependencies
self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
# We need command_line persisted to the db in order for Galaxy to re-queue the job
@@ -1452,7 +1460,7 @@
self.status = 'error'
# How do we want to handle task failure? Fail the job and let it clean up?
- def change_state( self, state, info = False ):
+ def change_state( self, state, info=False ):
task = self.get_task()
self.sa_session.refresh( task )
if info:
@@ -1568,11 +1576,14 @@
"""
def put( self, *args, **kwargs ):
return
+
def put_stop( self, *args ):
return
+
def shutdown( self ):
return
+
class ParallelismInfo(object):
"""
Stores the information (if any) for running multiple instances of the tool in parallel
@@ -1584,7 +1595,7 @@
items = tag.iteritems()
else:
items = tag.attrib.items()
- self.attributes = dict([item for item in items if item[0] != 'method' ])
+ self.attributes = dict( [ item for item in items if item[ 0 ] != 'method' ])
if len(self.attributes) == 0:
# legacy basic mode - provide compatible defaults
self.attributes['split_size'] = 20
https://bitbucket.org/galaxy/galaxy-central/commits/ebbd49858ce9/
Changeset: ebbd49858ce9
User: jmchilton
Date: 2014-01-03 00:50:13
Summary: PEP-8 fixes task runner.
Affected #: 1 file
diff -r 18e6f346d6b13521c9757989bfb7fc06791e561b -r ebbd49858ce94bd99805622456268e84bcbc5d7f lib/galaxy/jobs/runners/tasks.py
--- a/lib/galaxy/jobs/runners/tasks.py
+++ b/lib/galaxy/jobs/runners/tasks.py
@@ -1,11 +1,10 @@
import logging
import subprocess
-from Queue import Queue
-import threading
from galaxy import model
-import os, errno
+import os
+import errno
from time import sleep
from galaxy.jobs import TaskWrapper
@@ -15,11 +14,13 @@
__all__ = [ 'TaskedJobRunner' ]
+
class TaskedJobRunner( BaseJobRunner ):
"""
Job runner backed by a finite pool of worker threads. FIFO scheduling
"""
runner_name = "TaskRunner"
+
def __init__( self, app, nworkers ):
"""Start the job runner with 'nworkers' worker threads"""
super( TaskedJobRunner, self ).__init__( app, nworkers )
@@ -124,14 +125,14 @@
#this is terminate-able when output dataset/job is deleted
#so that long running set_meta()s can be canceled without having to reboot the server
if job_wrapper.get_state() not in [ model.Job.states.ERROR, model.Job.states.DELETED ] and job_wrapper.output_paths:
- external_metadata_script = job_wrapper.setup_external_metadata( output_fnames = job_wrapper.get_output_fnames(),
- set_extension = True,
- kwds = { 'overwrite' : False } ) #we don't want to overwrite metadata that was copied over in init_meta(), as per established behavior
+ external_metadata_script = job_wrapper.setup_external_metadata( output_fnames=job_wrapper.get_output_fnames(),
+ set_extension=True,
+ kwds={ 'overwrite' : False } ) # we don't want to overwrite metadata that was copied over in init_meta(), as per established behavior
log.debug( 'executing external set_meta script for job %d: %s' % ( job_wrapper.job_id, external_metadata_script ) )
- external_metadata_proc = subprocess.Popen( args = external_metadata_script,
- shell = True,
- env = os.environ,
- preexec_fn = os.setpgrp )
+ external_metadata_proc = subprocess.Popen( args=external_metadata_script,
+ shell=True,
+ env=os.environ,
+ preexec_fn=os.setpgrp )
job_wrapper.external_output_metadata.set_job_runner_external_pid( external_metadata_proc.pid, self.sa_session )
external_metadata_proc.wait()
log.debug( 'execution of external set_meta finished for job %d' % job_wrapper.job_id )
@@ -160,7 +161,7 @@
else:
#if our local job has JobExternalOutputMetadata associated, then our primary job has to have already finished
if job.external_output_metadata:
- pid = job.external_output_metadata[0].job_runner_external_pid #every JobExternalOutputMetadata has a pid set, we just need to take from one of them
+ pid = job.external_output_metadata[0].job_runner_external_pid # every JobExternalOutputMetadata has a pid set, we just need to take from one of them
else:
pid = job.job_runner_external_id
if pid in [ None, '' ]:
@@ -170,7 +171,7 @@
def recover( self, job, job_wrapper ):
# DBTODO Task Recovery, this should be possible.
- job_wrapper.change_state( model.Job.states.ERROR, info = "This job was killed when Galaxy was restarted. Please retry the job." )
+ job_wrapper.change_state( model.Job.states.ERROR, info="This job was killed when Galaxy was restarted. Please retry the job." )
def _cancel_job( self, job_wrapper, task_wrappers ):
"""
@@ -227,7 +228,7 @@
if e.errno == errno.ESRCH:
log.debug( "_check_pid(): PID %d is dead" % pid )
else:
- log.warning( "_check_pid(): Got errno %s when attempting to check PID %d: %s" %( errno.errorcode[e.errno], pid, e.strerror ) )
+ log.warning( "_check_pid(): Got errno %s when attempting to check PID %d: %s" % ( errno.errorcode[e.errno], pid, e.strerror ) )
return False
def _stop_pid( self, pid, job_id ):
@@ -254,7 +255,7 @@
# avoid a two-second overhead using some other asynchronous method.
sleep( 2 )
if not self._check_pid( pid ):
- log.debug( "_stop_pid(): %s: PID %d successfully killed with signal %d" %( job_id, pid, sig ) )
+ log.debug( "_stop_pid(): %s: PID %d successfully killed with signal %d" % ( job_id, pid, sig ) )
return
else:
- log.warning( "_stop_pid(): %s: PID %d refuses to die after signaling TERM/KILL" %( job_id, pid ) )
+ log.warning( "_stop_pid(): %s: PID %d refuses to die after signaling TERM/KILL" % ( job_id, pid ) )
https://bitbucket.org/galaxy/galaxy-central/commits/6facd407e247/
Changeset: 6facd407e247
User: jmchilton
Date: 2014-01-03 00:50:14
Summary: PEP-8 fixes for set_metadata.py.
Affected #: 1 file
diff -r ebbd49858ce94bd99805622456268e84bcbc5d7f -r 6facd407e2479f56fac8530a2f413226d497dfb2 scripts/set_metadata.py
--- a/scripts/set_metadata.py
+++ b/scripts/set_metadata.py
@@ -10,7 +10,9 @@
logging.basicConfig()
log = logging.getLogger( __name__ )
-import os, sys, cPickle
+import os
+import sys
+import cPickle
# ensure supported version
from check_python import check_python
try:
@@ -19,15 +21,15 @@
sys.exit(1)
new_path = [ os.path.join( os.getcwd(), "lib" ) ]
-new_path.extend( sys.path[1:] ) # remove scripts/ from the path
+new_path.extend( sys.path[ 1: ] ) # remove scripts/ from the path
sys.path = new_path
from galaxy import eggs
import pkg_resources
pkg_resources.require("simplejson")
import simplejson
-import galaxy.model.mapping #need to load this before we unpickle, in order to setup properties assigned by the mappers
-galaxy.model.Job() #this looks REAL stupid, but it is REQUIRED in order for SA to insert parameters into the classes defined by the mappers --> it appears that instantiating ANY mapper'ed class would suffice here
+import galaxy.model.mapping # need to load this before we unpickle, in order to setup properties assigned by the mappers
+galaxy.model.Job() # this looks REAL stupid, but it is REQUIRED in order for SA to insert parameters into the classes defined by the mappers --> it appears that instantiating ANY mapper'ed class would suffice here
from galaxy.util import stringify_dictionary_keys
from galaxy.util.json import from_json_string
from sqlalchemy.orm import clear_mappers
@@ -35,6 +37,7 @@
from galaxy import config
import ConfigParser
+
def __main__():
file_path = sys.argv.pop( 1 )
tmp_dir = sys.argv.pop( 1 )
@@ -45,7 +48,7 @@
config_file_name = sys.argv.pop( 1 )
if not os.path.isabs( config_file_name ):
config_file_name = os.path.join( config_root, config_file_name )
-
+
# Set up reference to object store
# First, read in the main config file for Galaxy; this is required because
# the object store configuration is stored there
@@ -65,7 +68,7 @@
universe_config = config.Configuration(**conf_dict)
object_store = build_object_store_from_config(universe_config)
galaxy.model.Dataset.object_store = object_store
-
+
# Set up datatypes registry
datatypes_config = sys.argv.pop( 1 )
datatypes_registry = galaxy.datatypes.registry.Registry()
@@ -89,32 +92,32 @@
filename_out = fields.pop( 0 )
filename_results_code = fields.pop( 0 )
dataset_filename_override = fields.pop( 0 )
- #Need to be careful with the way that these parameters are populated from the filename splitting,
- #because if a job is running when the server is updated, any existing external metadata command-lines
+ # Need to be careful with the way that these parameters are populated from the filename splitting,
+ # because if a job is running when the server is updated, any existing external metadata command-lines
#will not have info about the newly added override_metadata file
if fields:
override_metadata = fields.pop( 0 )
else:
override_metadata = None
try:
- dataset = cPickle.load( open( filename_in ) ) #load DatasetInstance
+ dataset = cPickle.load( open( filename_in ) ) # load DatasetInstance
if dataset_filename_override:
dataset.dataset.external_filename = dataset_filename_override
if ext_override.get( dataset.dataset.id, None ):
dataset.extension = ext_override[ dataset.dataset.id ]
- #Metadata FileParameter types may not be writable on a cluster node, and are therefore temporarily substituted with MetadataTempFiles
+ # Metadata FileParameter types may not be writable on a cluster node, and are therefore temporarily substituted with MetadataTempFiles
if override_metadata:
override_metadata = simplejson.load( open( override_metadata ) )
for metadata_name, metadata_file_override in override_metadata:
if galaxy.datatypes.metadata.MetadataTempFile.is_JSONified_value( metadata_file_override ):
metadata_file_override = galaxy.datatypes.metadata.MetadataTempFile.from_JSON( metadata_file_override )
setattr( dataset.metadata, metadata_name, metadata_file_override )
- kwds = stringify_dictionary_keys( simplejson.load( open( filename_kwds ) ) )#load kwds; need to ensure our keywords are not unicode
+ kwds = stringify_dictionary_keys( simplejson.load( open( filename_kwds ) ) ) # load kwds; need to ensure our keywords are not unicode
dataset.datatype.set_meta( dataset, **kwds )
- dataset.metadata.to_JSON_dict( filename_out ) # write out results of set_meta
- simplejson.dump( ( True, 'Metadata has been set successfully' ), open( filename_results_code, 'wb+' ) ) #setting metadata has succeeded
+ dataset.metadata.to_JSON_dict( filename_out ) # write out results of set_meta
+ simplejson.dump( ( True, 'Metadata has been set successfully' ), open( filename_results_code, 'wb+' ) ) # setting metadata has succeeded
except Exception, e:
- simplejson.dump( ( False, str( e ) ), open( filename_results_code, 'wb+' ) ) #setting metadata has failed somehow
+ simplejson.dump( ( False, str( e ) ), open( filename_results_code, 'wb+' ) ) # setting metadata has failed somehow
clear_mappers()
# Shut down any additional threads that might have been created via the ObjectStore
object_store.shutdown()
https://bitbucket.org/galaxy/galaxy-central/commits/de7ed9e75dda/
Changeset: de7ed9e75dda
User: jmchilton
Date: 2014-01-03 00:50:14
Summary: Work on unit test path problems.
Move test/unit/tool_shed to test/unit/tool_shed_unit_tests.
Affected #: 5 files
diff -r 6facd407e2479f56fac8530a2f413226d497dfb2 -r de7ed9e75dda6b8d9a364a56f9ac29a085a9e3cc test/unit/tool_shed/test_fabric_util.py
--- a/test/unit/tool_shed/test_fabric_util.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from contextlib import contextmanager
-from tool_shed.galaxy_install.tool_dependencies import fabric_util
-
-
-def test_env_file_builder():
- install_dir = "/opt/galaxy/dependencies/foo/"
- env_file_builder = fabric_util.EnvFileBuilder( install_dir )
- added_lines = []
- mock_return = dict(value=0)
-
- def mock_file_append( text, file_path, **kwds ):
- added_lines.append(text)
- return mock_return["value"]
-
- with __mock_fabric_util_method("file_append", mock_file_append):
- env_file_builder.append_line( name="PATH", action="prepend_to", value="/usr/bin/local/R" )
- assert added_lines == [ "PATH=/usr/bin/local/R:$PATH; export PATH" ]
- assert env_file_builder.return_code == 0
-
- # Reset mock lines
- del added_lines[:]
- # Next time file_append will fail
- mock_return["value"] = 1
-
- env_file_builder.append_line( action="source", value="/usr/bin/local/R/env.sh" )
- assert added_lines == [ "if [ -f /usr/bin/local/R/env.sh ] ; then . /usr/bin/local/R/env.sh ; fi" ]
- # Check failure
- assert env_file_builder.return_code == 1
-
- mock_return["value"] = 0
- env_file_builder.append_line( name="LD_LIBRARY_PATH", action="append_to", value="/usr/bin/local/R/lib" )
- # Verify even though last append succeeded, previous failure still recorded.
- assert env_file_builder.return_code == 1
-
-
-## Poor man's mocking. Need to get a real mocking library as real Galaxy development
-## dependnecy.
-@contextmanager
-def __mock_fabric_util_method(name, mock_method):
- real_method = getattr(fabric_util, name)
- try:
- setattr(fabric_util, name, mock_method)
- yield
- finally:
- setattr(fabric_util, name, real_method)
diff -r 6facd407e2479f56fac8530a2f413226d497dfb2 -r de7ed9e75dda6b8d9a364a56f9ac29a085a9e3cc test/unit/tool_shed/test_td_common_util.py
--- a/test/unit/tool_shed/test_td_common_util.py
+++ /dev/null
@@ -1,80 +0,0 @@
-from os.path import join
-from contextlib import contextmanager
-from galaxy.util import parse_xml_string
-
-from tool_shed.galaxy_install.tool_dependencies import fabric_util
-from tool_shed.galaxy_install.tool_dependencies import td_common_util
-
-
-TEST_DEPENDENCIES_DIR = "/opt/galaxy/dependencies"
-TEST_INSTALL_DIR = "%s/test_install_dir" % TEST_DEPENDENCIES_DIR
-
-
-class MockApp( object ):
-
- def __init__( self ):
- pass
-
-def test_create_or_update_env_shell_file():
- test_path = "/usr/share/R/libs"
- env_file_builder = fabric_util.EnvFileBuilder( test_path )
- line, path = env_file_builder.create_or_update_env_shell_file( TEST_INSTALL_DIR, dict( action="append_to", name="R_LIBS", value=test_path ) )
- assert path == join( TEST_INSTALL_DIR, "env.sh" )
- assert line == "R_LIBS=$R_LIBS:/usr/share/R/libs; export R_LIBS"
-
- line, path = env_file_builder.create_or_update_env_shell_file( TEST_INSTALL_DIR, dict( action="prepend_to", name="R_LIBS", value=test_path ) )
- assert path == join( TEST_INSTALL_DIR, "env.sh" )
- assert line == "R_LIBS=/usr/share/R/libs:$R_LIBS; export R_LIBS"
-
- line, path = env_file_builder.create_or_update_env_shell_file( TEST_INSTALL_DIR, dict( action="set_to", name="R_LIBS", value=test_path ) )
- assert path == join( TEST_INSTALL_DIR, "env.sh" )
- assert line == "R_LIBS=/usr/share/R/libs; export R_LIBS"
-
- line, path = env_file_builder.create_or_update_env_shell_file( TEST_INSTALL_DIR, dict( action="source", value=test_path ) )
- assert path == join( TEST_INSTALL_DIR, "env.sh" )
- assert line == "if [ -f /usr/share/R/libs ] ; then . /usr/share/R/libs ; fi"
-
-def test_get_env_shell_file_paths_from_setup_environment_elem():
- xml = """<action name="setup_r_environment">
- <repository name="package_r_3_0_1" owner="bgruening" toolshed="toolshed.g2.bx.psu.edu" changeset_revision="1234567">
- <package name="R" version="3.0.1" />
- </repository>
- </action>
- """
- mock_app = MockApp()
- action_elem = parse_xml_string( xml )
- required_for_install_env_sh = '/path/to/existing.sh'
- all_env_paths = [ required_for_install_env_sh ]
- action_dict = {}
-
- r_env_sh = '/path/to/go/env.sh'
-
- def mock_get_env_shell_file_paths( app, elem ):
- assert app == mock_app
- assert elem.get( 'name' ) == "package_r_3_0_1"
- return [ r_env_sh ]
-
- with __mock_common_util_method( "get_env_shell_file_paths", mock_get_env_shell_file_paths ):
- td_common_util.get_env_shell_file_paths_from_setup_environment_elem( mock_app, all_env_paths, action_elem, action_dict )
- ## Verify old env files weren't deleted.
- assert required_for_install_env_sh in all_env_paths
- ## Verify new ones added.
- assert r_env_sh in all_env_paths
- ## env_shell_file_paths includes everything
- assert all( [ env in action_dict[ 'env_shell_file_paths' ] for env in all_env_paths ] )
-
- ## action_shell_file_paths includes only env files defined in
- ## inside the setup_ action element.
- assert required_for_install_env_sh not in action_dict[ 'action_shell_file_paths' ]
- assert r_env_sh in action_dict[ 'action_shell_file_paths' ]
-
-## Poor man's mocking. Need to get a real mocking library as real Galaxy development
-## dependnecy.
-@contextmanager
-def __mock_common_util_method( name, mock_method ):
- real_method = getattr( td_common_util, name )
- try:
- setattr( td_common_util, name, mock_method )
- yield
- finally:
- setattr( td_common_util, name, real_method )
diff -r 6facd407e2479f56fac8530a2f413226d497dfb2 -r de7ed9e75dda6b8d9a364a56f9ac29a085a9e3cc test/unit/tool_shed_unit_tests/__init__.py
--- /dev/null
+++ b/test/unit/tool_shed_unit_tests/__init__.py
@@ -0,0 +1,4 @@
+"""
+Module cannot be called tool_shed, because this conflicts with lib/tool_shed
+also at top level of path.
+"""
diff -r 6facd407e2479f56fac8530a2f413226d497dfb2 -r de7ed9e75dda6b8d9a364a56f9ac29a085a9e3cc test/unit/tool_shed_unit_tests/test_fabric_util.py
--- /dev/null
+++ b/test/unit/tool_shed_unit_tests/test_fabric_util.py
@@ -0,0 +1,45 @@
+from contextlib import contextmanager
+from tool_shed.galaxy_install.tool_dependencies import fabric_util
+
+
+def test_env_file_builder():
+ install_dir = "/opt/galaxy/dependencies/foo/"
+ env_file_builder = fabric_util.EnvFileBuilder( install_dir )
+ added_lines = []
+ mock_return = dict(value=0)
+
+ def mock_file_append( text, file_path, **kwds ):
+ added_lines.append(text)
+ return mock_return["value"]
+
+ with __mock_fabric_util_method("file_append", mock_file_append):
+ env_file_builder.append_line( name="PATH", action="prepend_to", value="/usr/bin/local/R" )
+ assert added_lines == [ "PATH=/usr/bin/local/R:$PATH; export PATH" ]
+ assert env_file_builder.return_code == 0
+
+ # Reset mock lines
+ del added_lines[:]
+ # Next time file_append will fail
+ mock_return["value"] = 1
+
+ env_file_builder.append_line( action="source", value="/usr/bin/local/R/env.sh" )
+ assert added_lines == [ "if [ -f /usr/bin/local/R/env.sh ] ; then . /usr/bin/local/R/env.sh ; fi" ]
+ # Check failure
+ assert env_file_builder.return_code == 1
+
+ mock_return["value"] = 0
+ env_file_builder.append_line( name="LD_LIBRARY_PATH", action="append_to", value="/usr/bin/local/R/lib" )
+ # Verify even though last append succeeded, previous failure still recorded.
+ assert env_file_builder.return_code == 1
+
+
+## Poor man's mocking. Need to get a real mocking library as real Galaxy development
+## dependnecy.
+@contextmanager
+def __mock_fabric_util_method(name, mock_method):
+ real_method = getattr(fabric_util, name)
+ try:
+ setattr(fabric_util, name, mock_method)
+ yield
+ finally:
+ setattr(fabric_util, name, real_method)
diff -r 6facd407e2479f56fac8530a2f413226d497dfb2 -r de7ed9e75dda6b8d9a364a56f9ac29a085a9e3cc test/unit/tool_shed_unit_tests/test_td_common_util.py
--- /dev/null
+++ b/test/unit/tool_shed_unit_tests/test_td_common_util.py
@@ -0,0 +1,80 @@
+from os.path import join
+from contextlib import contextmanager
+from galaxy.util import parse_xml_string
+
+from tool_shed.galaxy_install.tool_dependencies import fabric_util
+from tool_shed.galaxy_install.tool_dependencies import td_common_util
+
+
+TEST_DEPENDENCIES_DIR = "/opt/galaxy/dependencies"
+TEST_INSTALL_DIR = "%s/test_install_dir" % TEST_DEPENDENCIES_DIR
+
+
+class MockApp( object ):
+
+ def __init__( self ):
+ pass
+
+def test_create_or_update_env_shell_file():
+ test_path = "/usr/share/R/libs"
+ env_file_builder = fabric_util.EnvFileBuilder( test_path )
+ line, path = env_file_builder.create_or_update_env_shell_file( TEST_INSTALL_DIR, dict( action="append_to", name="R_LIBS", value=test_path ) )
+ assert path == join( TEST_INSTALL_DIR, "env.sh" )
+ assert line == "R_LIBS=$R_LIBS:/usr/share/R/libs; export R_LIBS"
+
+ line, path = env_file_builder.create_or_update_env_shell_file( TEST_INSTALL_DIR, dict( action="prepend_to", name="R_LIBS", value=test_path ) )
+ assert path == join( TEST_INSTALL_DIR, "env.sh" )
+ assert line == "R_LIBS=/usr/share/R/libs:$R_LIBS; export R_LIBS"
+
+ line, path = env_file_builder.create_or_update_env_shell_file( TEST_INSTALL_DIR, dict( action="set_to", name="R_LIBS", value=test_path ) )
+ assert path == join( TEST_INSTALL_DIR, "env.sh" )
+ assert line == "R_LIBS=/usr/share/R/libs; export R_LIBS"
+
+ line, path = env_file_builder.create_or_update_env_shell_file( TEST_INSTALL_DIR, dict( action="source", value=test_path ) )
+ assert path == join( TEST_INSTALL_DIR, "env.sh" )
+ assert line == "if [ -f /usr/share/R/libs ] ; then . /usr/share/R/libs ; fi"
+
+def test_get_env_shell_file_paths_from_setup_environment_elem():
+ xml = """<action name="setup_r_environment">
+ <repository name="package_r_3_0_1" owner="bgruening" toolshed="toolshed.g2.bx.psu.edu" changeset_revision="1234567">
+ <package name="R" version="3.0.1" />
+ </repository>
+ </action>
+ """
+ mock_app = MockApp()
+ action_elem = parse_xml_string( xml )
+ required_for_install_env_sh = '/path/to/existing.sh'
+ all_env_paths = [ required_for_install_env_sh ]
+ action_dict = {}
+
+ r_env_sh = '/path/to/go/env.sh'
+
+ def mock_get_env_shell_file_paths( app, elem ):
+ assert app == mock_app
+ assert elem.get( 'name' ) == "package_r_3_0_1"
+ return [ r_env_sh ]
+
+ with __mock_common_util_method( "get_env_shell_file_paths", mock_get_env_shell_file_paths ):
+ td_common_util.get_env_shell_file_paths_from_setup_environment_elem( mock_app, all_env_paths, action_elem, action_dict )
+ ## Verify old env files weren't deleted.
+ assert required_for_install_env_sh in all_env_paths
+ ## Verify new ones added.
+ assert r_env_sh in all_env_paths
+ ## env_shell_file_paths includes everything
+ assert all( [ env in action_dict[ 'env_shell_file_paths' ] for env in all_env_paths ] )
+
+ ## action_shell_file_paths includes only env files defined in
+ ## inside the setup_ action element.
+ assert required_for_install_env_sh not in action_dict[ 'action_shell_file_paths' ]
+ assert r_env_sh in action_dict[ 'action_shell_file_paths' ]
+
+## Poor man's mocking. Need to get a real mocking library as real Galaxy development
+## dependnecy.
+@contextmanager
+def __mock_common_util_method( name, mock_method ):
+ real_method = getattr( td_common_util, name )
+ try:
+ setattr( td_common_util, name, mock_method )
+ yield
+ finally:
+ setattr( td_common_util, name, real_method )
https://bitbucket.org/galaxy/galaxy-central/commits/586dd135b4af/
Changeset: 586dd135b4af
User: jmchilton
Date: 2014-01-03 00:50:15
Summary: Fix version output redirection for pre-bash shells.
Affected #: 1 file
diff -r de7ed9e75dda6b8d9a364a56f9ac29a085a9e3cc -r 586dd135b4afc9d947c15aedc45c5104cc9ad94c lib/galaxy/jobs/command_factory.py
--- a/lib/galaxy/jobs/command_factory.py
+++ b/lib/galaxy/jobs/command_factory.py
@@ -38,7 +38,7 @@
def __handle_version_command(commands_builder, job_wrapper):
# Prepend version string
if job_wrapper.version_string_cmd:
- version_command = "%s &> %s" % ( job_wrapper.version_string_cmd, job_wrapper.get_version_string_path() )
+ version_command = "%s > %s 2>&1" % ( job_wrapper.version_string_cmd, job_wrapper.get_version_string_path() )
commands_builder.prepend_command(version_command)
https://bitbucket.org/galaxy/galaxy-central/commits/f0f7c3cd2e8a/
Changeset: f0f7c3cd2e8a
User: jmchilton
Date: 2014-01-03 00:50:15
Summary: Adjust GALAXY_SLOTS in local.py for pre-bash shells.
Affected #: 1 file
diff -r 586dd135b4afc9d947c15aedc45c5104cc9ad94c -r f0f7c3cd2e8af64243f878c04f418c1ee054bc55 lib/galaxy/jobs/runners/local.py
--- a/lib/galaxy/jobs/runners/local.py
+++ b/lib/galaxy/jobs/runners/local.py
@@ -54,9 +54,9 @@
slots = job_wrapper.job_destination.params.get( "local_slots", None )
command_line = command_line.lstrip( " ;" )
if slots:
- command_line = 'export GALAXY_SLOTS="%d"; export GALAXY_SLOTS_CONFIGURED="1"; %s' % ( int( slots ), command_line )
+ command_line = 'GALAXY_SLOTS="%d"; export GALAXY_SLOTS; GALAXY_SLOTS_CONFIGURED="1"; export GALAXY_SLOTS_CONFIGURED; %s' % ( int( slots ), command_line )
else:
- command_line = 'export GALAXY_SLOTS="1"; %s' % command_line
+ command_line = 'GALAXY_SLOTS="1"; export GALAXY_SLOTS; %s' % command_line
return command_line
def queue_job( self, job_wrapper ):
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: guerler: Fix XSS issue in former grids code
by commits-noreply@bitbucket.org 03 Jan '14
by commits-noreply@bitbucket.org 03 Jan '14
03 Jan '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/27350e3fc09b/
Changeset: 27350e3fc09b
Branch: stable
User: guerler
Date: 2014-01-03 05:25:22
Summary: Fix XSS issue in former grids code
Affected #: 1 file
diff -r 8411a9f30feb3a6fd15cecc09809afe2e24b5e79 -r 27350e3fc09bd36fea964c703b5ea787e2215de4 templates/grid_base.mako
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -56,6 +56,14 @@
${h.js("libs/jquery/jquery.autocomplete", "galaxy.autocom_tagging", "libs/jquery/jquery.rating", "galaxy.grids" )}
${handle_refresh_frames()}
+ <%
+ self.grid_options = {
+ 'sort_key' : sort_key,
+ 'use_async' : grid.use_async,
+ 'cur_page_num' : cur_page_num,
+ 'num_pages' : num_pages
+ }
+ %><script type="text/javascript">
// Needed URLs for grid history searching.
@@ -65,6 +73,7 @@
//
// Create grid object.
//
+ var grid_options = ${ h.to_json_string(self.grid_options) };
// Operations that are async (AJAX) compatible.
var async_ops = [];
@@ -83,20 +92,20 @@
/** Returns true if string denotes true. */
var is_true = function(s) { return _.indexOf(['True', 'true', 't'], s) !== -1; };
-
+
// Create grid.
var grid = new Grid({
url_base: '${trans.request.path_url}',
- async: is_true('${grid.use_async}'),
+ async: is_true(grid_options.use_async),
async_ops: async_ops,
categorical_filters: categorical_filters,
filters: ${h.to_json_string( cur_filter_dict )},
- sort_key: '${sort_key}',
+ sort_key: grid_options.sort_key,
show_item_checkboxes: is_true('${context.get('show_item_checkboxes', False)}'),
- cur_page: ${cur_page_num},
+ cur_page: grid_options.cur_page_num,
// persistent page="all"
//cur_page: ('${cur_page_num}' === 'all')?('all'):(Number('${cur_page_num}')),
- num_pages: ${num_pages}
+ num_pages: grid_options.num_pages
});
// Initialize grid objects on load.
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