galaxy-commits
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
September 2012
- 1 participants
- 161 discussions
24 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/327632b4f999/
changeset: 327632b4f999
user: carlfeberhard
date: 2012-09-24 19:47:23
summary: scatterplot: add require.js
affected #: 3 files
diff -r 8b7dfceabe3ea6cc15001f05a623157e95f1bcb7 -r 327632b4f9992b2c3f4b9c6f705bed1f4e05914d static/scripts/viz/scatterplot.js
--- a/static/scripts/viz/scatterplot.js
+++ b/static/scripts/viz/scatterplot.js
@@ -1,3 +1,9 @@
+define([
+ "../libs/underscore",
+ "../libs/d3",
+ "../mvc/base-mvc"
+
+], function(){
/* =============================================================================
todo:
outside this:
@@ -355,11 +361,11 @@
};
}
-//// ugh...this seems like the wrong way to use models
-//var ScatterplotModel = BaseModel.extend( LoggableMixin ).extend({
-// logger : console
-//});
-
+//==============================================================================
+/**
+ * Scatterplot control UI as a backbone view
+ *
+ */
var ScatterplotView = BaseView.extend( LoggableMixin ).extend({
//logger : console,
tagName : 'form',
@@ -370,15 +376,19 @@
},
initialize : function( attributes ){
- if( !attributes.dataset ){
+ if( !attributes || !attributes.dataset ){
throw( "ScatterplotView requires a dataset" );
} else {
this.dataset = attributes.dataset;
}
+
+ // passed from mako helper
+ //TODO: integrate to galaxyPaths
this.apiDatasetsURL = attributes.apiDatasetsURL;
+
+ // set up the basic chart infrastructure with config (if any)
this.chartConfig = attributes.chartConfig || {};
this.log( 'this.chartConfig:', this.chartConfig );
-
this.plot = new TwoVarScatterplot( this.chartConfig );
},
@@ -422,18 +432,23 @@
},
renderScatterplot : function(){
+ // parse the column values for both
+ // indeces (for the data fetch) and names (for the graph)
var view = this,
- url = this.apiDatasetsURL + '/' + this.dataset.id + '?data_type=raw_data&';
+ url = this.apiDatasetsURL + '/' + this.dataset.id + '?data_type=raw_data&',
+
xSelector = this.$el.find( '[name="x-column"]' ),
xVal = xSelector.val(),
xName = xSelector.children( '[value="' + xVal + '"]' ).text(),
+
ySelector = this.$el.find( '[name="y-column"]' ),
yVal = ySelector.val(),
yName = ySelector.children( '[value="' + yVal + '"]' ).text();
- //TODO
this.log( xName, yName );
+
this.chartConfig.xLabel = xName;
this.chartConfig.yLabel = yName;
+
//TODO: alter directly
view.plot.updateConfig( this.chartConfig );
@@ -441,6 +456,7 @@
//TODO: other vals: max, start, page
//TODO: chart config
+ // fetch the data, sending chosen columns to the server
url += jQuery.param({
columns : '[' + [ xVal, yVal ] + ']'
});
@@ -450,6 +466,7 @@
url : url,
dataType : 'json',
success : function( response ){
+ //TODO: server sends back an endpoint, cache for next pagination request
view.endpoint = response.endpoint;
view.plot.render(
// pull apart first two regardless of number of columns
@@ -464,3 +481,8 @@
}
});
+//==============================================================================
+return {
+ //TwoVarScatterplot : TwoVarScatterplot,
+ ScatterplotView : ScatterplotView
+};});
\ No newline at end of file
diff -r 8b7dfceabe3ea6cc15001f05a623157e95f1bcb7 -r 327632b4f9992b2c3f4b9c6f705bed1f4e05914d templates/base_panels.mako
--- a/templates/base_panels.mako
+++ b/templates/base_panels.mako
@@ -48,7 +48,17 @@
<!--[if lt IE 7]>
${h.js( 'libs/IE/IE7', 'libs/IE/ie7-recalc' )}
<![endif]-->
- ${h.js( 'libs/jquery/jquery', 'libs/json2', 'libs/bootstrap', 'libs/underscore', 'libs/backbone/backbone', 'libs/backbone/backbone-relational', 'libs/handlebars.runtime', 'mvc/ui', 'galaxy.base' )}
+ ${h.js(
+ 'libs/jquery/jquery',
+ 'libs/json2',
+ 'libs/bootstrap',
+ 'libs/underscore',
+ 'libs/backbone/backbone',
+ 'libs/backbone/backbone-relational',
+ 'libs/handlebars.runtime',
+ 'mvc/ui',
+ 'galaxy.base'
+ )}
<script type="text/javascript">
// Set up needed paths.
var galaxy_paths = new GalaxyPaths({
diff -r 8b7dfceabe3ea6cc15001f05a623157e95f1bcb7 -r 327632b4f9992b2c3f4b9c6f705bed1f4e05914d templates/visualization/scatterplot.mako
--- a/templates/visualization/scatterplot.mako
+++ b/templates/visualization/scatterplot.mako
@@ -6,25 +6,25 @@
<style type="text/css">
.title {
- margin: 0px;
+ margin: 0px;
padding: 8px;
background-color: #ebd9b2;
border: 2px solid #ebd9b2;
}
.subtitle {
- margin: 0px;
+ margin: 0px;
padding: 0px 8px 8px 16px;
background-color: #ebd9b2;
- color: white;
- font-size: small;
+ color: white;
+ font-size: small;
}
#chart-settings-form {
/*from width + margin of chart?*/
- float: right;
+ float: right;
width: 100%;
- margin: 0px;
+ margin: 0px;
padding-top: 1em;
}
@@ -45,80 +45,67 @@
overflow: auto;
}
-.clear {
- clear: both;
- margin: 0px;
-}
-
-
-svg .chart {
- /*shape-rendering: crispEdges;*/
-}
-
svg .grid-line {
- fill: none;
- stroke: lightgrey;
- stroke-opacity: 0.5;
- shape-rendering: crispEdges;
- stroke-dasharray: 3, 3;
+ fill: none;
+ stroke: lightgrey;
+ stroke-opacity: 0.5;
+ shape-rendering: crispEdges;
+ stroke-dasharray: 3, 3;
}
svg .axis path, svg .axis line {
- fill: none;
- stroke: black;
- shape-rendering: crispEdges;
+ fill: none;
+ stroke: black;
+ shape-rendering: crispEdges;
}
svg .axis text {
- font-family: sans-serif;
- font-size: 12px;
+ font-family: sans-serif;
+ font-size: 12px;
}
-
svg .glyph {
- stroke: none;
- fill: black;
- fill-opacity: 0.2;
+ stroke: none;
+ fill: black;
+ fill-opacity: 0.2;
}
-
+
</style>
-
+
</%def><%def name="javascripts()">
${parent.javascripts()}
-${h.js(
- "libs/underscore",
- "libs/backbone/backbone",
- "libs/backbone/backbone-relational",
- "libs/d3",
- "mvc/base-mvc",
- "viz/scatterplot"
-)}
+${h.js( "libs/require" )}
<script type="text/javascript">
-$(function() {
- var hda = ${h.to_json_string( hda )},
- historyID = '${historyID}'
- apiDatasetsURL = "${h.url_for( controller='/api/datasets' )}";
- //?? hmmmm
- //kwargs = ${h.to_json_string( kwargs )};
-
- var settingsForm = new ScatterplotView({
- dataset : hda,
- el : $( '#chart-settings-form' ),
+require.config({ baseUrl : "${h.url_for( '/static/scripts' )}", });
+
+require([ "viz/scatterplot" ], function( scatterplot ){
+
+ var hda = ${h.to_json_string( hda )},
+ historyID = '${historyID}'
+ apiDatasetsURL = "${h.url_for( controller='/api/datasets' )}";
+ //?? hmmmm
+ //kwargs = ${h.to_json_string( kwargs )};
+
+ var settingsForm = new scatterplot.ScatterplotView({
+ dataset : hda,
+ el : $( '#chart-settings-form' ),
apiDatasetsURL : apiDatasetsURL,
+
chartConfig : {
containerSelector : '#chart-holder',
marginTop : 20,
}
- }).render();
+ }).render();
});
+
</script></%def><%def name="body()"><h2 class="title">Scatterplot of '${hda['name']}'</h2><p class="subtitle">${hda['misc_info']}</p>
- <div id="chart-holder"></div>
- <div id="chart-settings-form"></div>
+ <div id="chart-holder"></div>
+ <div id="chart-settings-form"></div></%def>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
23 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/8b7dfceabe3e/
changeset: 8b7dfceabe3e
user: jgoecks
date: 2012-09-24 01:34:11
summary: Fix Cuffdiff parameter group naming.
affected #: 1 file
diff -r 3574c1fa4cb3494f82fd1915c63da6154b230756 -r 8b7dfceabe3ea6cc15001f05a623157e95f1bcb7 tools/ngs_rna/cuffdiff_wrapper.xml
--- a/tools/ngs_rna/cuffdiff_wrapper.xml
+++ b/tools/ngs_rna/cuffdiff_wrapper.xml
@@ -22,11 +22,11 @@
--cds_diff_output=$cds_diff
--promoters_diff_output=$promoters_diff
- ## Set paired-end data parameters?
- #if $singlePaired.sPaired == "Yes":
- -m $singlePaired.frag_mean_len
- -s $singlePaired.frag_len_std_dev
- #end if
+ ## Set advanced data parameters?
+ #if $additional.sAdditional == "Yes":
+ -m $additional.frag_mean_len
+ -s $additional.frag_len_std_dev
+ #end if
## Normalization?
#if str($do_normalization) == "Yes":
@@ -113,15 +113,15 @@
</when><when value="No"></when></conditional>
- <conditional name="singlePaired">
- <param name="sPaired" type="select" label="Set Parameters for Paired-end Reads? (not recommended)">
+ <conditional name="additional">
+ <param name="sAdditional" type="select" label="Set Additional Parameters? (not recommended)"><option value="No">No</option><option value="Yes">Yes</option></param><when value="No"></when><when value="Yes">
- <param name="frag_mean_len" type="integer" value="20" label="Average Fragment Length"/>
- <param name="frag_len_std_dev" type="integer" value="20" label="Fragment Length Standard Deviation"/>
+ <param name="frag_mean_len" type="integer" value="200" label="Average Fragment Length"/>
+ <param name="frag_len_std_dev" type="integer" value="80" label="Fragment Length Standard Deviation"/></when></conditional></inputs>
@@ -154,7 +154,7 @@
<param name="min_alignment_count" value="0" /><param name="do_bias_correction" value="No" /><param name="do_normalization" value="No" />
- <param name="sPaired" value="single"/>
+ <param name="sAdditional" value="No"/><!--
Line diffs are needed because cuffdiff does not produce deterministic output.
TODO: can we find datasets that lead to deterministic behavior?
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/3574c1fa4cb3/
changeset: 3574c1fa4cb3
user: carlfeberhard
date: 2012-09-22 00:39:04
summary: fixes to ae57c837ab03
affected #: 3 files
diff -r 45f11c9b4affd9006f1f7159368567cd412d20b8 -r 3574c1fa4cb3494f82fd1915c63da6154b230756 lib/galaxy/visualization/data_providers/basic.py
--- a/lib/galaxy/visualization/data_providers/basic.py
+++ b/lib/galaxy/visualization/data_providers/basic.py
@@ -90,7 +90,7 @@
if( skip_comments
and start_val == 0
and self.original_dataset.metadata.comment_lines ):
- start_val = self.original_dataset.metadata.comment_lines
+ start_val = int( self.original_dataset.metadata.comment_lines ) + 1
response = {}
response[ 'data' ] = data = []
@@ -102,6 +102,8 @@
"column index (%d) must be less" % ( column )
+ " than the number of columns: %d" % ( self.original_dataset.metadata.columns ) )
+ #print columns, start_val, max_vals, skip_comments, kwargs
+
# alter meta by column_selectors (if any)
def cast_val( val, type ):
""" Cast value based on type. """
@@ -125,8 +127,9 @@
fields = line.split()
fields_len = len( fields )
#TODO: this will return the wrong number of columns for abberrant lines
- data.append([ cast_val( fields[c], self.original_dataset.metadata.column_types[c] )
- for c in columns if ( c < fields_len ) ])
+ line_data = [ cast_val( fields[c], self.original_dataset.metadata.column_types[c] )
+ for c in columns if ( c < fields_len ) ]
+ data.append( line_data )
response[ 'endpoint' ] = dict( last_line=( count - 1 ), file_ptr=f.tell() )
f.close()
diff -r 45f11c9b4affd9006f1f7159368567cd412d20b8 -r 3574c1fa4cb3494f82fd1915c63da6154b230756 lib/galaxy/web/api/datasets.py
--- a/lib/galaxy/web/api/datasets.py
+++ b/lib/galaxy/web/api/datasets.py
@@ -189,13 +189,9 @@
# Return data.
data = None
data_provider = trans.app.data_provider_registry.get_data_provider( trans, raw=True, original_dataset=dataset )
+ print 'data_provider:', data_provider
- if data_provider == ColumnDataProvider:
- #pre: should have column kwargs
- #print 'kwargs:', kwargs
- #TODO??: could default to first two here
- assert 'cols' in kwargs, (
- "ColumnDataProvider needs a 'cols' parameter in the query string" )
+ if isinstance( data_provider, ColumnDataProvider ):
data = data_provider.get_data( **kwargs )
else:
diff -r 45f11c9b4affd9006f1f7159368567cd412d20b8 -r 3574c1fa4cb3494f82fd1915c63da6154b230756 templates/visualization/scatterplot.mako
--- a/templates/visualization/scatterplot.mako
+++ b/templates/visualization/scatterplot.mako
@@ -2,22 +2,21 @@
<%def name="stylesheets()">
${parent.stylesheets()}
-##${h.css( "history", "autocomplete_tagging", "trackster", "overcast/jquery-ui-1.8.5.custom", "library" )}
<style type="text/css">
-* { margin: 0px, padding: 0px; }
.title {
- margin: 0px;
+ margin: 0px;
padding: 8px;
background-color: #ebd9b2;
+ border: 2px solid #ebd9b2;
}
.subtitle {
- margin: 0px;
- padding: 0px, 8px, 8px, 16px;
+ margin: 0px;
+ padding: 0px 8px 8px 16px;
background-color: #ebd9b2;
- color: grey;
+ color: white;
font-size: small;
}
@@ -25,7 +24,7 @@
/*from width + margin of chart?*/
float: right;
width: 100%;
- background-color: #ebd9b2;
+ margin: 0px;
padding-top: 1em;
}
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jgoecks: Circster: add support for drawing a generic quantitative track and draw summary tree data using a single path.
by Bitbucket 21 Sep '12
by Bitbucket 21 Sep '12
21 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/45f11c9b4aff/
changeset: 45f11c9b4aff
user: jgoecks
date: 2012-09-21 23:44:41
summary: Circster: add support for drawing a generic quantitative track and draw summary tree data using a single path.
affected #: 1 file
diff -r ae57c837ab039ded7f5056252c2e630988080faf -r 45f11c9b4affd9006f1f7159368567cd412d20b8 static/scripts/viz/circster.js
--- a/static/scripts/viz/circster.js
+++ b/static/scripts/viz/circster.js
@@ -46,7 +46,7 @@
width = self.$el.width(),
height = self.$el.height(),
// Compute radius start based on model, will be centered
- // and fit entirely inside element by default
+ // and fit entirely inside element by default.
init_radius_start = ( Math.min(width, height)/2 -
this.model.get('tracks').length * (this.dataset_arc_height + this.track_gap) );
@@ -67,7 +67,7 @@
return utils.is_visible(this, svg);
});
visible_elts.each(function(d, i) {
- console.log(this.attr('title'));
+ // TODO: redraw visible elements.
});
}))
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
@@ -78,7 +78,9 @@
this.model.get('tracks').each(function(track, index) {
var dataset = track.get('genome_wide_data'),
radius_start = init_radius_start + index * (dataset_arc_height + self.track_gap),
- track_renderer_class = (dataset instanceof visualization.GenomeWideBigWigData ? CircsterBigWigTrackRenderer : CircsterSummaryTreeTrackRenderer );
+ track_renderer_class = (dataset instanceof visualization.GenomeWideBigWigData ?
+ CircsterBigWigTrackRenderer :
+ CircsterSummaryTreeTrackRenderer );
var track_renderer = new track_renderer_class({
track: track,
@@ -105,12 +107,12 @@
var genome_arcs = this.chroms_layout(),
radius_start = this.options.radius_start,
radius_end = this.options.radius_end,
- base_arc = svg.append("g").attr("id", "inner-arc"),
+ track_parent_elt = svg.append("g").attr("id", "inner-arc"),
arc_gen = d3.svg.arc()
.innerRadius(radius_start)
.outerRadius(radius_end),
// Draw arcs.
- chroms_elts = base_arc.selectAll("#inner-arc>path")
+ chroms_elts = track_parent_elt.selectAll("#inner-arc>path")
.data(genome_arcs).enter().append("path")
.attr("d", arc_gen)
.style("stroke", "#ccc")
@@ -118,12 +120,13 @@
.append("title").text(function(d) { return d.data.chrom; });
// Render data.
- this.render_data(svg);
+ this.render_data(track_parent_elt);
+ // Apply prefs.
var prefs = this.options.track.get('prefs'),
block_color = prefs.block_color;
if (!block_color) { block_color = prefs.color; }
- svg.selectAll('path.chrom-data').style('stroke', block_color).style('fill', block_color);
+ track_parent_elt.selectAll('path.chrom-data').style('stroke', block_color).style('fill', block_color);
},
/**
@@ -148,7 +151,7 @@
/**
* Render chromosome data and attach elements to svg.
*/
- render_chrom_data: function(chrom_arc, chrom_data, inner_radius, outer_radius, max) {
+ render_chrom_data: function(svg, chrom_arc, data, inner_radius, outer_radius, max) {
},
/**
@@ -167,8 +170,8 @@
// Do dataset layout for each chromosome's data using pie layout.
chroms_data_layout = _.map(layout_and_data, function(chrom_info) {
var chrom_arc = chrom_info[0],
- chrom_data = chrom_info[1];
- return self.render_chrom_data(svg, chrom_arc, chrom_data,
+ data = chrom_info[1];
+ return self.render_chrom_data(svg, chrom_arc, data,
r_start, r_end,
dataset.get('min'), dataset.get('max'));
});
@@ -178,55 +181,15 @@
});
/**
- * Layout for summary tree data in a circster visualization.
+ * Rendered for quantitative data.
*/
-var CircsterSummaryTreeTrackRenderer = CircsterTrackRenderer.extend({
-
- /**
- * Returns layouts for drawing a chromosome's data.
- */
- render_chrom_data: function(svg, chrom_arc, chrom_data, inner_radius, outer_radius, min, max) {
- // FIXME: draw as area:
- /*
- // If no chrom data, return null.
- if (!chrom_data || typeof chrom_data === "string") {
- return null;
- }
-
- var data = chrom_data[0],
- delta = chrom_data[3],
- scale = d3.scale.linear()
- .domain( [min, max] )
- .range( [inner_radius, outer_radius] ),
- arc_layout = d3.layout.pie().value(function(d) {
- return delta;
- })
- .startAngle(chrom_arc.startAngle)
- .endAngle(chrom_arc.endAngle),
- arcs = arc_layout(data);
-
- // Use scale to assign outer radius.
- _.each(data, function(datum, index) {
- arcs[index].outerRadius = scale(datum[1]);
- });
-
- return arcs;
- */
- }
-});
-
-/**
- * Layout for BigWig data in a circster visualization.
- */
-var CircsterBigWigTrackRenderer = CircsterTrackRenderer.extend({
+var CircsterQuantitativeTrackRenderer = CircsterTrackRenderer.extend({
/**
- * Render chromosome data and attach elements to svg.
+ * Renders quantitative data with the form [x, value] and assumes data is equally spaced across
+ * chromosome.
*/
- render_chrom_data: function(svg, chrom_arc, chrom_data, inner_radius, outer_radius, min, max) {
- var data = chrom_data.data;
- if (data.length === 0) { return; }
-
+ render_quantitative_data: function(svg, chrom_arc, data, inner_radius, outer_radius, min, max) {
// Radius scaler.
var radius = d3.scale.linear()
.domain([min, max])
@@ -237,14 +200,8 @@
.domain([0, data.length])
.range([chrom_arc.startAngle, chrom_arc.endAngle]);
- // FIXME:
- // *area is not necessary as long as 0 is used as the inner radius.
- // *could provide fill as an option.
-
- // Use both a line and area; area creates definition and line ensures that something is
- // always drawn.
var line = d3.svg.line.radial()
- .interpolate("linear-closed")
+ .interpolate("linear")
.radius(function(d) { return radius(d[1]); })
.angle(function(d, i) { return angle(i); });
@@ -260,12 +217,42 @@
parent.append("path")
.attr("class", "chrom-data")
.attr("d", area);
-
- parent.append("path")
- .attr("class", "chrom-data")
- .attr("d", line);
}
+})
+
+/**
+ * Layout for summary tree data in a circster visualization.
+ */
+var CircsterSummaryTreeTrackRenderer = CircsterQuantitativeTrackRenderer.extend({
+
+ /**
+ * Renders a chromosome's data.
+ */
+ render_chrom_data: function(svg, chrom_arc, chrom_data, inner_radius, outer_radius, min, max) {
+ // If no chrom data, return null.
+ if (!chrom_data || typeof chrom_data === "string") {
+ return null;
+ }
+
+ return this.render_quantitative_data(svg, chrom_arc, chrom_data[0], inner_radius, outer_radius, min, max);
+ }
+});
+
+/**
+ * Layout for BigWig data in a circster visualization.
+ */
+var CircsterBigWigTrackRenderer = CircsterQuantitativeTrackRenderer.extend({
+
+ /**
+ * Renders a chromosome's data.
+ */
+ render_chrom_data: function(svg, chrom_arc, chrom_data, inner_radius, outer_radius, min, max) {
+ var data = chrom_data.data;
+ if (data.length === 0) { return; }
+
+ return this.render_quantitative_data(svg, chrom_arc, data, inner_radius, outer_radius, min, max);
+ }
});
return {
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: fixed(?) weird permissions on jquery-ui; scatterplot: major adjustments;
by Bitbucket 21 Sep '12
by Bitbucket 21 Sep '12
21 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/ae57c837ab03/
changeset: ae57c837ab03
user: carlfeberhard
date: 2012-09-21 22:42:29
summary: fixed(?) weird permissions on jquery-ui; scatterplot: major adjustments;
affected #: 8 files
diff -r 63a8fb1d8e2b8a8da71a5b618ef65981ff5c4ac3 -r ae57c837ab039ded7f5056252c2e630988080faf lib/galaxy/visualization/data_providers/basic.py
--- a/lib/galaxy/visualization/data_providers/basic.py
+++ b/lib/galaxy/visualization/data_providers/basic.py
@@ -45,31 +45,64 @@
"""
iterator = self.get_iterator( chrom, start, end )
return self.process_data( iterator, start_val, max_vals, **kwargs )
+
def write_data_to_file( self, filename, **kwargs ):
"""
Write data in region defined by chrom, start, and end to a file.
"""
raise Exception( "Unimplemented Function" )
+
class ColumnDataProvider( BaseDataProvider ):
""" Data provider for columnar data """
+ MAX_LINES_RETURNED = 30000
- def __init__( self, original_dataset ):
+ def __init__( self, original_dataset, max_lines_returned=MAX_LINES_RETURNED ):
# Compatibility check.
if not isinstance( original_dataset.datatype, Tabular ):
raise Exception( "Data provider can only be used with tabular data" )
# Attribute init.
self.original_dataset = original_dataset
+ # allow throttling
+ self.max_lines_returned = max_lines_returned
- def get_data( self, cols, start_val=0, max_vals=sys.maxint, **kwargs ):
+ def get_data( self, columns, start_val=0, max_vals=None, skip_comments=True, **kwargs ):
"""
Returns data from specified columns in dataset. Format is list of lists
where each list is a line of data.
"""
-
- cols = from_json_string( cols )
+ #TODO: validate kwargs
+ try:
+ max_vals = int( max_vals )
+ max_vals = min([ max_vals, self.max_lines_returned ])
+ except ( ValueError, TypeError ):
+ max_vals = self.max_lines_returned
+ try:
+ start_val = int( start_val )
+ start_val = max([ start_val, 0 ])
+ except ( ValueError, TypeError ):
+ start_val = 0
+
+ # skip comment lines (if any/avail)
+ # pre: should have original_dataset and
+ if( skip_comments
+ and start_val == 0
+ and self.original_dataset.metadata.comment_lines ):
+ start_val = self.original_dataset.metadata.comment_lines
+
+ response = {}
+ response[ 'data' ] = data = []
+
+ #TODO bail if columns None, not parsable, not within meta.columns
+ # columns is an array of ints for now (should handle column names later)
+ columns = from_json_string( columns )
+ assert( all([ column < self.original_dataset.metadata.columns for column in columns ]) ),(
+ "column index (%d) must be less" % ( column )
+ + " than the number of columns: %d" % ( self.original_dataset.metadata.columns ) )
+
+ # alter meta by column_selectors (if any)
def cast_val( val, type ):
""" Cast value based on type. """
if type == 'int':
@@ -80,23 +113,23 @@
except: pass
return val
- data = []
f = open( self.original_dataset.file_name )
+ #TODO: add f.seek if given fptr in kwargs
for count, line in enumerate( f ):
if count < start_val:
continue
- if max_vals and count-start_val >= max_vals:
- message = self.error_max_vals % ( max_vals, "features" )
+
+ if ( count - start_val ) >= max_vals:
break
fields = line.split()
- #pre: column indeces should be avail in fields
- for col_index in cols:
- assert col_index < len( fields ), (
- "column index (%d) must be less than fields length: %d" % ( col_index, len( fields ) ) )
-
- data.append( [ cast_val( fields[c], self.original_dataset.metadata.column_types[c] ) for c in cols ] )
+ fields_len = len( fields )
+ #TODO: this will return the wrong number of columns for abberrant lines
+ data.append([ cast_val( fields[c], self.original_dataset.metadata.column_types[c] )
+ for c in columns if ( c < fields_len ) ])
+ response[ 'endpoint' ] = dict( last_line=( count - 1 ), file_ptr=f.tell() )
f.close()
-
- return data
+
+ return response
+
diff -r 63a8fb1d8e2b8a8da71a5b618ef65981ff5c4ac3 -r ae57c837ab039ded7f5056252c2e630988080faf lib/galaxy/web/api/datasets.py
--- a/lib/galaxy/web/api/datasets.py
+++ b/lib/galaxy/web/api/datasets.py
@@ -38,26 +38,27 @@
return str( e )
# Use data type to return particular type of data.
- if data_type == 'state':
- rval = self._dataset_state( trans, dataset )
- elif data_type == 'converted_datasets_state':
- rval = self._converted_datasets_state( trans, dataset, kwd.get( 'chrom', None ) )
- elif data_type == 'data':
- rval = self._data( trans, dataset, **kwd )
- elif data_type == 'features':
- rval = self._search_features( trans, dataset, kwd.get( 'query ' ) )
- elif data_type == 'raw_data':
- rval = self._raw_data( trans, dataset, **kwd )
- elif data_type == 'track_config':
- rval = self.get_new_track_config( trans, dataset )
- else:
- # Default: return dataset as API value.
- try:
+ try:
+ if data_type == 'state':
+ rval = self._dataset_state( trans, dataset )
+ elif data_type == 'converted_datasets_state':
+ rval = self._converted_datasets_state( trans, dataset, kwd.get( 'chrom', None ) )
+ elif data_type == 'data':
+ rval = self._data( trans, dataset, **kwd )
+ elif data_type == 'features':
+ rval = self._search_features( trans, dataset, kwd.get( 'query ' ) )
+ elif data_type == 'raw_data':
+ rval = self._raw_data( trans, dataset, **kwd )
+ elif data_type == 'track_config':
+ rval = self.get_new_track_config( trans, dataset )
+ else:
+ # Default: return dataset as API value.
rval = dataset.get_api_value()
- except Exception, e:
- rval = "Error in dataset API at listing contents"
- log.error( rval + ": %s" % str(e) )
- trans.response.status = 500
+
+ except Exception, e:
+ rval = "Error in dataset API at listing contents"
+ log.error( rval + ": %s" % str(e), exc_info=True )
+ trans.response.status = 500
return rval
def _dataset_state( self, trans, dataset, **kwargs ):
diff -r 63a8fb1d8e2b8a8da71a5b618ef65981ff5c4ac3 -r ae57c837ab039ded7f5056252c2e630988080faf lib/galaxy/web/controllers/visualization.py
--- a/lib/galaxy/web/controllers/visualization.py
+++ b/lib/galaxy/web/controllers/visualization.py
@@ -800,26 +800,26 @@
return self.get_visualization( trans, id )
@web.expose
- def scatterplot( self, trans, dataset_id, cols ):
+ def scatterplot( self, trans, dataset_id, **kwargs ):
# Get HDA.
hda = self.get_dataset( trans, dataset_id, check_ownership=False, check_accessible=True )
-
- # get some metadata for the page
hda_dict = hda.get_api_value()
- #title = "Scatter plot of {name}:".format( **hda_dict )
- #subtitle = "{misc_info}".format( **hda_dict )
+ hda_dict[ 'id' ] = dataset_id
+ if( hda_dict[ 'metadata_column_names' ] == None
+ and hasattr( hda.datatype, 'column_names' ) ):
+ hda_dict[ 'metadata_column_names' ] = hda.datatype.column_names
+ history_id = trans.security.encode_id( hda.history.id )
#TODO: add column data
# Read data.
- data_provider = ColumnDataProvider( original_dataset=hda )
- data = data_provider.get_data( cols )
+ #data_provider = ColumnDataProvider( original_dataset=hda, **kwargs )
+ #data = data_provider.get_data()
# Return plot.
return trans.fill_template_mako( "visualization/scatterplot.mako",
- title=hda.name, subtitle=hda.info,
- hda=hda,
- data=data )
-
+ hda=hda_dict,
+ historyID=history_id,
+ kwargs=kwargs )
@web.json
def bookmarks_from_dataset( self, trans, hda_id=None, ldda_id=None ):
diff -r 63a8fb1d8e2b8a8da71a5b618ef65981ff5c4ac3 -r ae57c837ab039ded7f5056252c2e630988080faf static/scripts/viz/scatterplot.js
--- /dev/null
+++ b/static/scripts/viz/scatterplot.js
@@ -0,0 +1,466 @@
+/* =============================================================================
+todo:
+ outside this:
+ BUG: visualization menu doesn't disappear
+ BUG?: get column_names (from datatype if necessary)
+ BUG: single vis in popupmenu should have tooltip with that name NOT 'Visualizations'
+
+ ??: maybe better to do this with a canvas...
+
+ move renderScatter plot to obj, possibly view?
+
+ fix x axis - adjust ticks when tick labels are long - move odds down and extend tick line
+
+ provide callback in view to load data incrementally - for large sets
+
+ paginate
+ handle rerender
+ use endpoint (here and on the server (fileptr))
+ fetch (new?) data
+ handle rerender
+
+ selectable list of preset column comparisons (rnaseq etc.)
+ multiple plots on one page (small multiples)
+
+ where are bad dataprovider params handled? api/datasets?
+
+ config changes to the graph
+ render stats on the data (max, min, count)
+ render warning on long data (> maxDataPoints)
+ adjust endpoint
+
+ loading indicator
+
+ download svg -> base64 encode
+ dress up ui
+
+ incorporate glyphs, glyph state renderers
+
+ validate columns selection (here or server)
+
+ ?? ensure svg styles thru d3 or css?
+ d3: configable (easily)
+ css: standard - better maintenance
+ ? override at config
+
+============================================================================= */
+/**
+ * Two Variable scatterplot visualization using d3
+ * Uses semi transparent circles to show density of data in x, y grid
+ * usage :
+ * var plot = new TwoVarScatterplot({ containerSelector : 'div#my-plot', ... })
+ * plot.render( xColumnData, yColumnData );
+ *
+ * depends on: d3, underscore
+ */
+function TwoVarScatterplot( config ){
+ var plot = this,
+ GUESS_AT_SVG_CHAR_WIDTH = 10,
+ GUESS_AT_SVG_CHAR_HEIGHT = 12,
+ PADDING = 8,
+ X_LABEL_TOO_LONG_AT = 5;
+
+ //this.debugging = true;
+ this.log = function(){
+ if( this.debugging && console && console.debug ){
+ var args = Array.prototype.slice.call( arguments );
+ args.unshift( this.toString() );
+ console.debug.apply( null, args );
+ }
+ };
+ this.log( 'new TwoVarScatterplot:', config );
+
+ // ........................................................ set up chart config
+ // config will default to these values when not passed in
+ //NOTE: called on new
+ this.defaults = {
+ id : 'TwoVarScatterplot',
+ containerSelector : 'body',
+ maxDataPoints : 30000,
+ bubbleRadius : 4,
+ entryAnimDuration : 500,
+ //TODO: no effect?
+ xNumTicks : 10,
+ yNumTicks : 10,
+ xAxisLabelBumpY : 40,
+ yAxisLabelBumpX : -35,
+ width : 500,
+ height : 500,
+ //TODO: anyway to make this a sub-obj?
+ marginTop : 50,
+ marginRight : 50,
+ marginBottom : 50,
+ marginLeft : 50,
+ xMin : null,
+ xMax : null,
+ yMin : null,
+ yMax : null,
+ xLabel : "X",
+ yLabel : "Y"
+ };
+ this.config = _.extend( {}, this.defaults, config );
+
+ this.updateConfig = function( newConfig ){
+ _.extend( this.config, newConfig );
+ };
+
+ // ........................................................ helpers
+ this.toString = function(){
+ return this.config.id;
+ };
+ this.translateStr = function( x, y ){
+ return 'translate(' + x + ',' + y + ')';
+ };
+ this.rotateStr = function( d, x, y ){
+ return 'rotate(' + d + ',' + x + ',' + y + ')';
+ };
+
+ // ........................................................ initial element creation
+ //NOTE: called on new
+ this.svg = d3.select( this.config.containerSelector )
+ .append( "svg:svg" ).attr( "class", "chart" ).style( 'display', 'none' );
+ this.content = this.svg.append( "svg:g" ).attr( "class", "content" );
+
+ this.xAxis = this.content.append( 'g' ).attr( 'class', 'axis' ).attr( 'id', 'x-axis' );
+ this.xAxisLabel = this.xAxis.append( 'text' ).attr( 'class', 'axis-label' ).attr( 'id', 'x-axis-label' );
+
+ this.yAxis = this.content.append( 'g' ).attr( 'class', 'axis' ).attr( 'id', 'y-axis' );
+ this.yAxisLabel = this.yAxis.append( 'text' ).attr( 'class', 'axis-label' ).attr( 'id', 'y-axis-label' );
+
+ this.log( 'built svg:', d3.selectAll( 'svg' ) );
+
+ this.adjustChartDimensions = function(){
+ this.svg
+ .attr( "width", this.config.width + ( this.config.marginRight + this.config.marginLeft ) )
+ .attr( "height", this.config.height + ( this.config.marginTop + this.config.marginBottom ) )
+ // initial is hidden - show it
+ .style( 'display', 'block' );
+
+ // move content group away from margins
+ this.content = this.svg.select( "g.content" )
+ .attr( "transform", this.translateStr( this.config.marginLeft, this.config.marginTop ) );
+ };
+
+ // ........................................................ data and scales
+ this.preprocessData = function( data ){
+ // set a cap on the data, limit to first n points
+ return data.slice( 0, this.config.maxDataPoints );
+ };
+
+ this.setUpDomains = function( xCol, yCol ){
+ this.xMin = this.config.xMin || d3.min( xCol );
+ this.xMax = this.config.xMax || d3.max( xCol );
+ this.yMin = this.config.yMin || d3.min( yCol );
+ this.yMax = this.config.yMax || d3.max( yCol );
+ };
+
+ this.setUpScales = function(){
+ // Interpolation for x, y based on data domains
+ this.xScale = d3.scale.linear()
+ .domain([ this.xMin, this.xMax ])
+ .range([ 0, this.config.width ]),
+ this.yScale = d3.scale.linear()
+ .domain([ this.yMin, this.yMax ])
+ .range([ this.config.height, 0 ]);
+ };
+
+ // ........................................................ axis and ticks
+ this.setUpXAxis = function(){
+ // origin: bottom, left
+ //TODO: incoporate top, right
+ this.xAxisFn = d3.svg.axis()
+ .scale( this.xScale )
+ .ticks( this.config.xNumTicks )
+ .orient( 'bottom' );
+ this.xAxis// = content.select( 'g#x-axis' )
+ .attr( 'transform', this.translateStr( 0, this.config.height ) )
+ .call( this.xAxisFn );
+ this.log( 'xAxis:', this.xAxis );
+
+ //TODO: this isn't reliable with -/+ ranges - better to go thru each tick
+ this.xLongestLabel = d3.max( _.map( [ this.xMin, this.xMax ],
+ function( number ){ return ( String( number ) ).length; } ) );
+ this.log( 'xLongestLabel:', this.xLongestLabel );
+
+ if( this.xLongestLabel >= X_LABEL_TOO_LONG_AT ){
+ //TODO: adjust ticks when tick labels are long - move odds down and extend tick line
+ // (for now) hide them
+ this.xAxis.selectAll( 'g' ).filter( ':nth-child(odd)' ).style( 'display', 'none' );
+ }
+
+ this.xAxisLabel// = xAxis.select( 'text#x-axis-label' )
+ .attr( 'x', this.config.width / 2 )
+ .attr( 'y', this.config.xAxisLabelBumpY )
+ .attr( 'text-anchor', 'middle' )
+ .text( this.config.xLabel );
+ this.log( 'xAxisLabel:', this.xAxisLabel );
+ };
+
+ this.setUpYAxis = function(){
+ this.yAxisFn = d3.svg.axis()
+ .scale( this.yScale )
+ .ticks( this.config.yNumTicks )
+ .orient( 'left' );
+ this.yAxis// = content.select( 'g#y-axis' )
+ .call( this.yAxisFn );
+ this.log( 'yAxis:', this.yAxis );
+
+ this.yLongestLabel = d3.max( _.map( [ this.yMin, this.yMax ],
+ function( number ){ return ( String( number ) ).length; } ) );
+ this.log( 'yLongestLabel:', this.yLongestLabel );
+
+ //TODO: ugh ... so clumsy
+ //TODO: this isn't reliable with -/+ ranges - better to go thru each tick
+ var neededY = this.yLongestLabel * GUESS_AT_SVG_CHAR_WIDTH + ( PADDING );
+
+ // increase width for xLongerStr, increase margin for y
+ //TODO??: (or transform each number: 2k)
+ if( this.config.yAxisLabelBumpX > -( neededY ) ){
+ this.config.yAxisLabelBumpX = -( neededY );
+ }
+ if( this.config.marginLeft < neededY ){
+ this.config.marginLeft = neededY + GUESS_AT_SVG_CHAR_HEIGHT;
+ // update dimensions, translations
+ this.adjustChartDimensions();
+ }
+ this.log( 'this.config.yAxisLableBumpx, this.config.marginLeft:',
+ this.config.yAxisLabelBumpX, this.config.marginLeft );
+
+ this.yAxisLabel// = yAxis.select( 'text#y-axis-label' )
+ .attr( 'x', this.config.yAxisLabelBumpX )
+ .attr( 'y', this.config.height / 2 )
+ .attr( 'text-anchor', 'middle' )
+ .attr( 'transform', this.rotateStr( -90, this.config.yAxisLabelBumpX, this.config.height / 2 ) )
+ .text( this.config.yLabel );
+ this.log( 'yAxisLabel:', this.yAxisLabel );
+ };
+
+ // ........................................................ grid lines
+ this.renderGrid = function(){
+ // VERTICAL
+ // select existing
+ this.vGridLines = this.content.selectAll( 'line.v-grid-line' )
+ .data( this.xScale.ticks( this.xAxisFn.ticks()[0] ) );
+
+ // append any extra lines needed (more ticks)
+ this.vGridLines.enter().append( 'svg:line' )
+ .classed( 'grid-line v-grid-line', true );
+
+ // update the attributes of existing and appended
+ this.vGridLines
+ .attr( 'x1', this.xScale )
+ .attr( 'y1', 0 )
+ .attr( 'x2', this.xScale )
+ .attr( 'y2', this.config.height );
+
+ // remove unneeded (less ticks)
+ this.vGridLines.exit().remove();
+ this.log( 'vGridLines:', this.vGridLines );
+
+ // HORIZONTAL
+ this.hGridLines = this.content.selectAll( 'line.h-grid-line' )
+ .data( this.yScale.ticks( this.yAxisFn.ticks()[0] ) );
+
+ this.hGridLines.enter().append( 'svg:line' )
+ .classed( 'grid-line h-grid-line', true );
+
+ this.hGridLines
+ .attr( 'x1', 0 )
+ .attr( 'y1', this.yScale )
+ .attr( 'x2', this.config.width )
+ .attr( 'y2', this.yScale );
+
+ this.hGridLines.exit().remove();
+ this.log( 'hGridLines:', this.hGridLines );
+ };
+
+ // ........................................................ data points
+ //TODO: these to config ...somehow
+ //TODO: use these in renderDatapoints ...somehow
+ this.glyphEnterState = function( d3Elem ){
+
+ };
+ this.glyphFinalState = function( d3Elem ){
+
+ };
+ this.glyphExitState = function( d3Elem ){
+
+ };
+
+ this.renderDatapoints = function( xCol, yCol ){
+
+ var xPosFn = function( d, i ){
+ return plot.xScale( xCol[ i ] );
+ };
+ var yPosFn = function( d, i ){
+ return plot.yScale( yCol[ i ] );
+ };
+
+ // select all existing glyphs and compare to incoming data
+ // enter() will yield those glyphs that need to be added
+ // exit() will yield existing glyphs that need to be removed
+ this.datapoints = this.content.selectAll( ".glyph" )
+ .data( xCol );
+
+ // enter - new data to be added as glyphs: give them a 'entry' position and style
+ this.datapoints.enter()
+ .append( "svg:circle" ).attr( "class", "glyph" )
+ // start all bubbles at corner...
+ .attr( "cx", xPosFn )
+ .attr( "cy", 0 )
+ .attr( "r", 0 );
+
+ // for all existing glyphs and those that need to be added: transition anim to final state
+ this.datapoints
+ // ...animate to final position
+ .transition().duration( this.config.entryAnimDuration )
+ .attr( "cx", xPosFn )
+ .attr( "cy", yPosFn )
+ .attr( "r", this.config.bubbleRadius );
+
+ // glyphs that need to be removed: transition to from normal state to 'exit' state, remove from DOM
+ this.datapoints.exit()
+ .transition().duration( this.config.entryAnimDuration )
+ .attr( "cy", this.config.height )
+ .attr( "r", 0 )
+ .style( "fill-opacity", 0 )
+ .remove();
+
+ this.log( this.datapoints, 'glyphs rendered' );
+ };
+
+ this.render = function( xCol, yCol ){
+ //pre: columns passed are numeric
+ this.log( 'renderScatterplot', xCol.length, yCol.length, this.config );
+
+ //pre: xCol.len == yCol.len
+ //TODO: ^^ isn't necessarily true with current ColumnDataProvider
+ xCol = this.preprocessData( xCol );
+ yCol = this.preprocessData( yCol );
+ this.log( 'xCol len', xCol.length, 'yCol len', yCol.length );
+
+ //TODO: compute min, max on server.
+ this.setUpDomains( xCol, yCol );
+ this.log( 'xMin, xMax, yMin, yMax:', this.xMin, this.xMax, this.yMin, this.yMax );
+
+ this.setUpScales();
+ this.adjustChartDimensions();
+
+ this.setUpXAxis();
+ this.setUpYAxis();
+
+ this.renderGrid();
+ this.renderDatapoints( xCol, yCol );
+ //TODO: on hover red line to axes, display values
+ };
+}
+
+//// ugh...this seems like the wrong way to use models
+//var ScatterplotModel = BaseModel.extend( LoggableMixin ).extend({
+// logger : console
+//});
+
+var ScatterplotView = BaseView.extend( LoggableMixin ).extend({
+ //logger : console,
+ tagName : 'form',
+ className : 'scatterplot-settings-form',
+
+ events : {
+ 'click #render-button' : 'renderScatterplot'
+ },
+
+ initialize : function( attributes ){
+ if( !attributes.dataset ){
+ throw( "ScatterplotView requires a dataset" );
+ } else {
+ this.dataset = attributes.dataset;
+ }
+ this.apiDatasetsURL = attributes.apiDatasetsURL;
+ this.chartConfig = attributes.chartConfig || {};
+ this.log( 'this.chartConfig:', this.chartConfig );
+
+ this.plot = new TwoVarScatterplot( this.chartConfig );
+ },
+
+ render : function(){
+ //TODO: to template
+ var view = this,
+ html = '',
+ columnHtml = '';
+ // build column select controls for each x, y (based on name if available)
+ // ugh...hafta preprocess
+ this.dataset.metadata_column_types = this.dataset.metadata_column_types.split( ', ' );
+ _.each( this.dataset.metadata_column_types, function( type, index ){
+ // use only numeric columns
+ if( type === 'int' || type === 'float' ){
+ var name = 'column ' + index;
+ // label with the name if available
+ if( view.dataset.metadata_column_names ){
+ name = view.dataset.metadata_column_names[ index ];
+ }
+ columnHtml += '<option value="' + index + '">' + name + '</column>';
+ }
+ });
+
+ // column selector containers
+ html += '<div id="x-column-input">';
+ html += '<label for="">Data column for X: </label><select name="x-column">' + columnHtml + '</select>';
+ html += '</div>';
+
+ html += '<div id="y-column-input">';
+ html += '<label for="">Data column for Y: </label><select name="y-column">' + columnHtml + '</select>';
+ html += '</div>';
+
+ html += '<input id="render-button" type="button" value="Draw" />';
+ html += '<div class="clear"></div>';
+
+ //TODO: other vals: max_vals, start_val, pagination
+
+ this.$el.append( html );
+ this.$el.find( '#render-button' );
+ return this;
+ },
+
+ renderScatterplot : function(){
+ var view = this,
+ url = this.apiDatasetsURL + '/' + this.dataset.id + '?data_type=raw_data&';
+ xSelector = this.$el.find( '[name="x-column"]' ),
+ xVal = xSelector.val(),
+ xName = xSelector.children( '[value="' + xVal + '"]' ).text(),
+ ySelector = this.$el.find( '[name="y-column"]' ),
+ yVal = ySelector.val(),
+ yName = ySelector.children( '[value="' + yVal + '"]' ).text();
+ //TODO
+ this.log( xName, yName );
+ this.chartConfig.xLabel = xName;
+ this.chartConfig.yLabel = yName;
+ //TODO: alter directly
+ view.plot.updateConfig( this.chartConfig );
+
+ //TODO: validate columns - minimally: we can assume either set by selectors or via a good query string
+ //TODO: other vals: max, start, page
+ //TODO: chart config
+
+ url += jQuery.param({
+ columns : '[' + [ xVal, yVal ] + ']'
+ });
+ this.log( 'url:', url );
+
+ jQuery.ajax({
+ url : url,
+ dataType : 'json',
+ success : function( response ){
+ view.endpoint = response.endpoint;
+ view.plot.render(
+ // pull apart first two regardless of number of columns
+ _.map( response.data, function( columns ){ return columns[0]; } ),
+ _.map( response.data, function( columns ){ return columns[1]; } )
+ );
+ },
+ error : function( xhr, status, error ){
+ alert( 'ERROR:' + status + '\n' + error );
+ }
+ });
+ }
+});
+
diff -r 63a8fb1d8e2b8a8da71a5b618ef65981ff5c4ac3 -r ae57c837ab039ded7f5056252c2e630988080faf templates/root/history.mako
--- a/templates/root/history.mako
+++ b/templates/root/history.mako
@@ -194,6 +194,17 @@
};
};
+function create_scatterplot_action_fn( url, params ){
+ action = function() {
+ var galaxy_main = $( window.parent.document ).find( 'iframe#galaxy_main' ),
+ final_url = url + '/scatterplot?' + $.param(params);
+ galaxy_main.attr( 'src', final_url );
+ $( 'div.popmenu-wrapper' ).remove();
+ return false;
+ };
+ return action;
+}
+
/**
* Create popup menu for visualization icon.
*/
@@ -208,14 +219,20 @@
// Create visualization action.
create_viz_action = function(visualization) {
var action;
- if (visualization === 'trackster') {
- action = create_trackster_action_fn(vis_url, params, dbkey);
- }
- else {
- action = function() {
- window.parent.location = vis_url + '/' + visualization + '?' +
- $.param(params);
- };
+ switch( visualization ){
+
+ case 'trackster':
+ action = create_trackster_action_fn(vis_url, params, dbkey);
+ break;
+
+ case 'scatterplot':
+ action = create_scatterplot_action_fn( vis_url, params );
+ break;
+
+ default:
+ action = function(){
+ window.parent.location = vis_url + '/' + visualization + '?' + $.param(params);
+ }
}
return action;
},
@@ -234,6 +251,7 @@
// Set up action or menu.
if (visualizations.length === 1) {
// No need for popup menu because there's a single visualization.
+ icon.attr( 'title', visualizations[0] );
icon.click(create_viz_action(visualizations[0]));
}
else {
diff -r 63a8fb1d8e2b8a8da71a5b618ef65981ff5c4ac3 -r ae57c837ab039ded7f5056252c2e630988080faf templates/root/history_common.mako
--- a/templates/root/history_common.mako
+++ b/templates/root/history_common.mako
@@ -230,10 +230,10 @@
## information--URL base, dataset id, dbkey, visualizations--in anchor.
<%
visualizations = data.get_visualizations()
- ## HACK: if there are visualizations, only provide trackster for now since others
- ## are not ready.
+ ## HACK: if there are visualizations, only provide trackster for now
+ ## since others are not ready. - comment out to see all WIP visualizations
if visualizations:
- visualizations = [ 'trackster' ]
+ visualizations = [ vis for vis in visualizations if vis in [ 'trackster' ] ]
%>
%if visualizations:
<a href="${h.url_for( controller='visualization' )}"
diff -r 63a8fb1d8e2b8a8da71a5b618ef65981ff5c4ac3 -r ae57c837ab039ded7f5056252c2e630988080faf templates/visualization/scatterplot.mako
--- a/templates/visualization/scatterplot.mako
+++ b/templates/visualization/scatterplot.mako
@@ -2,23 +2,61 @@
<%def name="stylesheets()">
${parent.stylesheets()}
-${h.css( "history", "autocomplete_tagging", "trackster", "overcast/jquery-ui-1.8.5.custom", "library" )}
+##${h.css( "history", "autocomplete_tagging", "trackster", "overcast/jquery-ui-1.8.5.custom", "library" )}
<style type="text/css">
* { margin: 0px, padding: 0px; }
+.title {
+ margin: 0px;
+ padding: 8px;
+ background-color: #ebd9b2;
+}
+
.subtitle {
- margin-left: 1em;
- margin-top: -1em;
+ margin: 0px;
+ padding: 0px, 8px, 8px, 16px;
+ background-color: #ebd9b2;
color: grey;
font-size: small;
}
-.chart {
+#chart-settings-form {
+ /*from width + margin of chart?*/
+ float: right;
+ width: 100%;
+ background-color: #ebd9b2;
+ padding-top: 1em;
+}
+
+#chart-settings-form > * {
+ margin: 8px;
+}
+
+#chart-settings-form input, #chart-settings-form select {
+ width: 30%;
+ max-width: 256px;
+}
+
+#chart-settings-form [value=Draw] {
+ float: right;
+}
+
+#chart-holder {
+ overflow: auto;
+}
+
+.clear {
+ clear: both;
+ margin: 0px;
+}
+
+
+svg .chart {
/*shape-rendering: crispEdges;*/
}
-.grid-line {
+svg .grid-line {
fill: none;
stroke: lightgrey;
stroke-opacity: 0.5;
@@ -26,18 +64,18 @@
stroke-dasharray: 3, 3;
}
-.axis path, .axis line {
+svg .axis path, svg .axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
-.axis text {
+svg .axis text {
font-family: sans-serif;
font-size: 12px;
}
-circle.bubble {
+svg .glyph {
stroke: none;
fill: black;
fill-opacity: 0.2;
@@ -49,156 +87,39 @@
<%def name="javascripts()">
${parent.javascripts()}
-${h.js( "libs/d3" )}
+${h.js(
+ "libs/underscore",
+ "libs/backbone/backbone",
+ "libs/backbone/backbone-relational",
+ "libs/d3",
+ "mvc/base-mvc",
+ "viz/scatterplot"
+)}
<script type="text/javascript">
-/* =============================================================================
-todo:
- validate columns (here or server)
- send: type, column title/name in JSON
-
- move to obj, possibly view?
- fetch (new?) data
- config changes to the graph
- download svg (png?)
-
-============================================================================= */
-function translateStr( x, y ){
- return 'translate(' + x + ',' + y + ')';
-}
-function rotateStr( d, x, y ){
- return 'rotate(' + d + ',' + x + ',' + y + ')';
-}
-
$(function() {
- // Constants
- var data = ${data},
- MAX_DATA_POINTS = 30000,
- BUBBLE_RADIUS = 5,
- ENTRY_ANIM_DURATION = 500,
- X_TICKS = 10, Y_TICKS = 10,
- X_AXIS_LABEL_BUMP_Y = 40,
- Y_AXIS_LABEL_BUMP_X = -35,
- WIDTH = 300,
- HEIGHT = 300,
- MARGIN= 50,
- xLabel = "Magnitude",
- yLabel = "Depth";
-
- // set a cap on the data, limit to first n points
- data = data.slice( 0, MAX_DATA_POINTS );
+ var hda = ${h.to_json_string( hda )},
+ historyID = '${historyID}'
+ apiDatasetsURL = "${h.url_for( controller='/api/datasets' )}";
+ //?? hmmmm
+ //kwargs = ${h.to_json_string( kwargs )};
- // split the data into columns
- //TODO: compute min, max on server.
- var col1_data = data.map( function(e) { return e[0] }),
- col2_data = data.map( function(e) { return e[1] }),
- xMin = d3.min( col1_data ),
- xMax = d3.max( col1_data ),
- yMin = d3.min( col2_data ),
- yMax = d3.max( col2_data );
- //console.log( 'col1_data:', col1_data );
- //console.log( 'col2_data:', col2_data );
- //console.log( 'xMin, xMax, yMin, yMax:', xMin, xMax, yMin, yMax );
-
- // Set up.
- d3.select( "body" ).append( "svg:svg" )
- .attr( "width", WIDTH + ( MARGIN * 2 ) )
- .attr( "height", HEIGHT + ( MARGIN * 2 ) )
- .attr( "class", "chart" );
-
- // Scale for x, y based on data domains
- // origin: bottom, left
- var x_scale = d3.scale.linear()
- .domain([ xMin, xMax ])
- .range([ 0, WIDTH ]),
- y_scale = d3.scale.linear()
- .domain([ yMin, yMax ])
- .range([ HEIGHT, 0 ]);
-
- // Selection of SVG, append group (will group our entire chart), give attributes
- // apply a group and transform all coords away from margins
- var chart = d3.select( ".chart" ).append( "svg:g" )
- .attr( "class", "content" )
- .attr( "transform", translateStr( MARGIN, MARGIN ) );
-
- // axes
- var xAxisFn = d3.svg.axis()
- .scale( x_scale )
- .ticks( X_TICKS )
- .orient( 'bottom' );
- var xAxis = chart.append( 'g' ).attr( 'class', 'axis' ).attr( 'id', 'x-axis' )
- .attr( 'transform', translateStr( 0, HEIGHT ) )
- .call( xAxisFn )
- //console.debug( 'xAxis:', xAxis ); window.xAxis = xAxis, window.xAxisFn = xAxisFn;
-
- var xAxisLabel = xAxis.append( 'text' ).attr( 'class', 'axis-label' ).attr( 'id', 'x-axis-label' )
- .attr( 'x', WIDTH / 2 )
- .attr( 'y', X_AXIS_LABEL_BUMP_Y )
- .attr( 'text-anchor', 'middle' )
- .text( xLabel );
- //console.debug( 'xAxisLabel:', xAxisLabel ); window.xAxisLabel = xAxisLabel;
-
- var yAxisFn = d3.svg.axis()
- .scale( y_scale )
- .ticks( Y_TICKS )
- .orient( 'left' );
- var yAxis = chart.append( 'g' ).attr( 'class', 'axis' ).attr( 'id', 'y-axis' )
- .call( yAxisFn );
- //console.debug( 'yAxis:', yAxis ); window.yAxis = yAxis, window.yAxisFn = yAxisFn;
-
- var yAxisLabel = yAxis.append( 'text' ).attr( 'class', 'axis-label' ).attr( 'id', 'y-axis-label' )
- .attr( 'x', Y_AXIS_LABEL_BUMP_X )
- .attr( 'y', HEIGHT / 2 )
- .attr( 'text-anchor', 'middle' )
- .attr( 'transform', rotateStr( -90, Y_AXIS_LABEL_BUMP_X, HEIGHT / 2 ) )
- .text( yLabel );
- //console.debug( 'yAxisLabel:', yAxisLabel ); window.yAxisLabel = yAxisLabel;
-
- // grid lines
- var hGridLines = chart.selectAll( '.h-grid-line' )
- .data( x_scale.ticks( xAxisFn.ticks()[0] ) )
- .enter().append( 'svg:line' )
- .classed( 'grid-line h-grid-line', true )
- .attr( 'x1', x_scale ).attr( 'y1', 0 )
- .attr( 'x2', x_scale ).attr( 'y2', HEIGHT )
- //console.debug( 'hGridLines:', hGridLines ); window.hGridLines = hGridLines;
-
- var vGridLines = chart.selectAll( '.v-grid-line' )
- .data( y_scale.ticks( yAxisFn.ticks()[0] ) )
- .enter().append( 'svg:line' )
- .classed( 'grid-line v-grid-line', true )
- .attr( 'x1', 0 ) .attr( 'y1', y_scale )
- .attr( 'x2', WIDTH ).attr( 'y2', y_scale )
- //console.debug( 'vGridLines:', vGridLines ); window.vGridLines = vGridLines;
-
- // Functions used to render plot.
- var xPosFn = function( d, i ){
- return x_scale( col1_data[ i ] );
- };
- var yPosFn = function( d, i ){
- return y_scale( col2_data[ i ] );
- };
-
- // Create bubbles for each data point.
- chart.selectAll( "circle.bubble" )
- .data(data).enter()
- .append( "svg:circle" ).attr( "class", "bubble" )
- // start all bubbles at corner...
- .attr( "r", 0 )
- .attr( "fill", "white" )
- // ...animate to final position
- .transition().duration( ENTRY_ANIM_DURATION )
- .attr("cx", xPosFn )
- .attr("cy", yPosFn )
- .attr("r", BUBBLE_RADIUS);
-
- //TODO: on hover red line to axes, display values
+ var settingsForm = new ScatterplotView({
+ dataset : hda,
+ el : $( '#chart-settings-form' ),
+ apiDatasetsURL : apiDatasetsURL,
+ chartConfig : {
+ containerSelector : '#chart-holder',
+ marginTop : 20,
+ }
+ }).render();
});
</script></%def><%def name="body()">
- <h1 class="title">Scatterplot of '${title}':</h1>
- <h2 class="subtitle">${subtitle}</h2>
-
+ <h2 class="title">Scatterplot of '${hda['name']}'</h2>
+ <p class="subtitle">${hda['misc_info']}</p>
+ <div id="chart-holder"></div>
+ <div id="chart-settings-form"></div></%def>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
21 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/63a8fb1d8e2b/
changeset: 63a8fb1d8e2b
user: jgoecks
date: 2012-09-21 20:05:37
summary: Small additions based on bdd35af2a18a
affected #: 2 files
diff -r bdd35af2a18afde11396fcf919b5fb4cacc986ff -r 63a8fb1d8e2b8a8da71a5b618ef65981ff5c4ac3 lib/galaxy/visualization/data_providers/registry.py
--- a/lib/galaxy/visualization/data_providers/registry.py
+++ b/lib/galaxy/visualization/data_providers/registry.py
@@ -30,7 +30,8 @@
def get_data_provider( self, trans, name=None, source='data', raw=False, original_dataset=None ):
"""
- Returns data provider class by name and/or original dataset.
+ Returns data provider matching parameter values. For standalone data
+ sources, source parameter is ignored.
"""
data_provider = None
diff -r bdd35af2a18afde11396fcf919b5fb4cacc986ff -r 63a8fb1d8e2b8a8da71a5b618ef65981ff5c4ac3 lib/galaxy/web/controllers/visualization.py
--- a/lib/galaxy/web/controllers/visualization.py
+++ b/lib/galaxy/web/controllers/visualization.py
@@ -756,15 +756,9 @@
# Get dataset and indexed datatype.
dataset = self.get_hda_or_ldda( trans, track[ 'hda_ldda'], track[ 'dataset_id' ] )
data_sources = dataset.get_datasources( trans )
- data_provider_registry = trans.app.data_provider_registry
- if 'data_standalone' in data_sources:
- indexed_type = data_sources['data_standalone']['name']
- data_provider = data_provider_registry.get_data_provider( indexed_type )( dataset )
- else:
- indexed_type = data_sources['index']['name']
- # Get converted dataset and append track's genome data.
- converted_dataset = dataset.get_converted_dataset( trans, indexed_type )
- data_provider = data_provider_registry.get_data_provider( indexed_type )( converted_dataset, dataset )
+ data_provider = trans.app.data_provider_registry.get_data_provider( trans,
+ original_dataset=dataset,
+ source='index' )
# HACK: pass in additional params, which are only used for summary tree data, not BBI data.
track[ 'genome_wide_data' ] = { 'data': data_provider.get_genome_data( chroms_info, level=4, detail_cutoff=0, draw_cutoff=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: jgoecks: Viz framework: (a) push data provider creation to registry to simplify provider creation; (b) fix bugs in filters module naming; (c) enable deeper sampling in BBI data provider.
by Bitbucket 21 Sep '12
by Bitbucket 21 Sep '12
21 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/bdd35af2a18a/
changeset: bdd35af2a18a
user: jgoecks
date: 2012-09-21 20:00:09
summary: Viz framework: (a) push data provider creation to registry to simplify provider creation; (b) fix bugs in filters module naming; (c) enable deeper sampling in BBI data provider.
affected #: 8 files
diff -r 8a8dcc0d36687fcfaaa1ae26e136e76b4a0d7bb0 -r bdd35af2a18afde11396fcf919b5fb4cacc986ff lib/galaxy/visualization/data_providers/basic.py
--- a/lib/galaxy/visualization/data_providers/basic.py
+++ b/lib/galaxy/visualization/data_providers/basic.py
@@ -100,62 +100,3 @@
f.close()
return data
-
-class DataProviderRegistry( object ):
- """
- Registry for data providers that enables listing and lookup.
- """
-
- def __init__( self ):
- # Mapping from dataset type name to a class that can fetch data from a file of that
- # type. First key is converted dataset type; if result is another dict, second key
- # is original dataset type. TODO: This needs to be more flexible.
- self.dataset_type_name_to_data_provider = {
- "tabix": {
- Vcf: VcfTabixDataProvider,
- Bed: BedTabixDataProvider,
- Gtf: GtfTabixDataProvider,
- ENCODEPeak: ENCODEPeakTabixDataProvider,
- Interval: IntervalTabixDataProvider,
- ChromatinInteractions: ChromatinInteractionsTabixDataProvider,
- "default" : TabixDataProvider
- },
- "interval_index": IntervalIndexDataProvider,
- "bai": BamDataProvider,
- "bam": SamDataProvider,
- "summary_tree": SummaryTreeDataProvider,
- "bigwig": BigWigDataProvider,
- "bigbed": BigBedDataProvider
- }
-
- def get_data_provider( name=None, original_dataset=None ):
- """
- Returns data provider class by name and/or original dataset.
- """
- data_provider = None
- if name:
- value = dataset_type_name_to_data_provider[ name ]
- if isinstance( value, dict ):
- # Get converter by dataset extension; if there is no data provider,
- # get the default.
- data_provider = value.get( original_dataset.datatype.__class__, value.get( "default" ) )
- else:
- data_provider = value
- elif original_dataset:
- # Look up data provider from datatype's informaton.
- try:
- # Get data provider mapping and data provider for 'data'. If
- # provider available, use it; otherwise use generic provider.
- _ , data_provider_mapping = original_dataset.datatype.get_track_type()
- if 'data_standalone' in data_provider_mapping:
- data_provider_name = data_provider_mapping[ 'data_standalone' ]
- else:
- data_provider_name = data_provider_mapping[ 'data' ]
- if data_provider_name:
- data_provider = self.get_data_provider( name=data_provider_name, original_dataset=original_dataset )
- else:
- data_provider = GenomeDataProvider
- except:
- pass
- return data_provider
-
\ No newline at end of file
diff -r 8a8dcc0d36687fcfaaa1ae26e136e76b4a0d7bb0 -r bdd35af2a18afde11396fcf919b5fb4cacc986ff lib/galaxy/visualization/data_providers/genome.py
--- a/lib/galaxy/visualization/data_providers/genome.py
+++ b/lib/galaxy/visualization/data_providers/genome.py
@@ -112,6 +112,8 @@
class GenomeDataProvider( BaseDataProvider ):
""" Base class for genome data providers. """
+
+ data_type = None
"""
Mapping from column name to payload data; this mapping is used to create
@@ -314,6 +316,8 @@
class TabixDataProvider( FilterableMixin, GenomeDataProvider ):
+ data_type = 'tabix'
+
"""
Tabix index data provider for the Galaxy track browser.
"""
@@ -354,8 +358,10 @@
#
class IntervalDataProvider( GenomeDataProvider ):
+ data_type = 'interval_index'
+
"""
- Processes BED data from native format to payload format.
+ Processes interval data from native format to payload format.
Payload format: [ uid (offset), start, end, name, strand, thick_start, thick_end, blocks ]
"""
@@ -437,6 +443,8 @@
Payload format: [ uid (offset), start, end, name, strand, thick_start, thick_end, blocks ]
"""
+
+ data_type = 'interval_index'
def get_iterator( self, chrom, start, end ):
raise Exception( "Unimplemented Method" )
@@ -533,6 +541,8 @@
for large datasets.
"""
+ data_type = 'interval_index'
+
def get_iterator( self, chrom=None, start=None, end=None ):
# Read first line in order to match chrom naming format.
line = source.readline()
@@ -570,6 +580,8 @@
"""
col_name_data_attr_mapping = { 'Qual' : { 'index': 6 , 'name' : 'Qual' } }
+
+ data_type = 'bai'
def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ):
"""
@@ -675,6 +687,8 @@
for large datasets.
"""
+ data_type = 'tabix'
+
def get_iterator( self, chrom, start, end ):
# Read first line in order to match chrom naming format.
line = source.readline()
@@ -706,6 +720,8 @@
"""
Summary tree data provider for the Galaxy track browser.
"""
+
+ data_type = 'summary_tree'
CACHE = LRUCache( 20 ) # Store 20 recently accessed indices for performance
@@ -771,6 +787,8 @@
Provides access to intervals from a sorted indexed BAM file. Position data
is reported in 1-based, closed format, i.e. SAM/BAM format.
"""
+
+ data_type = 'bai'
def get_filters( self ):
"""
@@ -951,6 +969,8 @@
return { 'data': results, 'message': message, 'max_low': max_low, 'max_high': max_high }
class SamDataProvider( BamDataProvider ):
+
+ data_type = 'bai'
def __init__( self, converted_dataset=None, original_dataset=None, dependencies=None ):
""" Create SamDataProvider. """
@@ -966,6 +986,9 @@
"""
BBI data provider for the Galaxy track browser.
"""
+
+ data_type = 'bigwig'
+
def valid_chroms( self ):
# No way to return this info as of now
return None
@@ -976,7 +999,7 @@
f.close()
return all_dat is not None
- def get_data( self, chrom, start, end, start_val=0, max_vals=None, **kwargs ):
+ def get_data( self, chrom, start, end, start_val=0, max_vals=None, num_samples=1000, **kwargs ):
# Bigwig can be a standalone bigwig file, in which case we use
# original_dataset, or coming from wig->bigwig conversion in
# which we use converted_dataset
@@ -1009,14 +1032,11 @@
return dict( data=dict( min=min, max=max, mean=mean, sd=sd ) )
- # Sample from region using approximately this many samples.
- N = 1000
-
def summarize_region( bbi, chrom, start, end, num_points ):
'''
Returns results from summarizing a region using num_points.
NOTE: num_points cannot be greater than end - start or BBI
- will return None for all positions.s
+ will return None for all positions.
'''
result = []
@@ -1042,7 +1062,8 @@
return result
# Approach is different depending on region size.
- if end - start < N:
+ num_samples = int( num_samples )
+ if end - start < num_samples:
# Get values for individual bases in region, including start and end.
# To do this, need to increase end to next base and request number of points.
num_points = end - start + 1
@@ -1050,10 +1071,10 @@
else:
#
# The goal is to sample the region between start and end uniformly
- # using ~N data points. The challenge is that the size of sampled
- # intervals rarely is full bases, so sampling using N points will
- # leave the end of the region unsampled due to remainders for each
- # interval. To recitify this, a new N is calculated based on the
+ # using ~N (num_samples) data points. The challenge is that the size of
+ # sampled intervals rarely is full bases, so sampling using N points
+ # will leave the end of the region unsampled due to remainders for
+ # each interval. To recitify this, a new N is calculated based on the
# step size that covers as much of the region as possible.
#
# However, this still leaves some of the region unsampled. This
@@ -1063,7 +1084,7 @@
#
# Start with N samples.
- num_points = N
+ num_points = num_samples
step_size = ( end - start ) / num_points
# Add additional points to sample in the remainder not covered by
# the initial N samples.
@@ -1100,6 +1121,8 @@
Interval index files used only for GFF files.
"""
col_name_data_attr_mapping = { 4 : { 'index': 4 , 'name' : 'Score' } }
+
+ data_type = 'interval_index'
def write_data_to_file( self, regions, filename ):
source = open( self.original_dataset.file_name )
@@ -1177,6 +1200,8 @@
NOTE: this data provider does not use indices, and hence will be very slow
for large datasets.
"""
+
+ data_type = 'interval_index'
def get_iterator( self, chrom, start, end ):
"""
diff -r 8a8dcc0d36687fcfaaa1ae26e136e76b4a0d7bb0 -r bdd35af2a18afde11396fcf919b5fb4cacc986ff lib/galaxy/visualization/data_providers/registry.py
--- a/lib/galaxy/visualization/data_providers/registry.py
+++ b/lib/galaxy/visualization/data_providers/registry.py
@@ -28,46 +28,63 @@
"bigbed": BigBedDataProvider
}
- def get_data_provider( self, name=None, raw=False, original_dataset=None ):
+ def get_data_provider( self, trans, name=None, source='data', raw=False, original_dataset=None ):
"""
Returns data provider class by name and/or original dataset.
"""
- # If getting raw data, use original dataset type to get data provider.
+ data_provider = None
if raw:
+ # Working with raw data.
if isinstance( original_dataset.datatype, Gff ):
- return RawGFFDataProvider
+ data_provider_class = RawGFFDataProvider
elif isinstance( original_dataset.datatype, Bed ):
- return RawBedDataProvider
+ data_provider_class = RawBedDataProvider
elif isinstance( original_dataset.datatype, Vcf ):
- return RawVcfDataProvider
+ data_provider_class = RawVcfDataProvider
elif isinstance( original_dataset.datatype, Tabular ):
- return ColumnDataProvider
+ data_provider_class = ColumnDataProvider
- # Using converted dataset, so get corrsponding data provider.
- data_provider = None
- if name:
- value = self.dataset_type_name_to_data_provider[ name ]
- if isinstance( value, dict ):
- # Get converter by dataset extension; if there is no data provider,
- # get the default.
- data_provider = value.get( original_dataset.datatype.__class__, value.get( "default" ) )
- else:
- data_provider = value
- elif original_dataset:
- # Look up data provider from datatype's informaton.
- try:
- # Get data provider mapping and data provider for 'data'. If
- # provider available, use it; otherwise use generic provider.
+ data_provider = data_provider_class( original_dataset=original_dataset )
+
+ else:
+ # Working with converted or standalone dataset.
+
+ if name:
+ # Provider requested by name; get from mappings.
+ value = self.dataset_type_name_to_data_provider[ name ]
+ if isinstance( value, dict ):
+ # Get converter by dataset extension; if there is no data provider,
+ # get the default.
+ data_provider_class = value.get( original_dataset.datatype.__class__, value.get( "default" ) )
+ else:
+ data_provider_class = value
+
+ # If name is the same as original dataset's type, dataset is standalone.
+ # Otherwise, a converted dataset is being used.
+ if name == original_dataset.ext:
+ data_provider = data_provider_class( original_dataset=original_dataset )
+ else:
+ converted_dataset = original_dataset.get_converted_dataset( trans, name )
+ deps = original_dataset.get_converted_dataset_deps( trans, name )
+ data_provider = data_provider_class( original_dataset=original_dataset,
+ converted_dataset=converted_dataset,
+ dependencies=deps )
+
+ elif original_dataset:
+ # No name, so look up a provider name from datatype's information.
+
+ # Dataset must have get_track_type function to get data.
+ if not hasattr( original_dataset.datatype, 'get_track_type'):
+ return None
+
+ # Get data provider mapping and data provider.
_ , data_provider_mapping = original_dataset.datatype.get_track_type()
if 'data_standalone' in data_provider_mapping:
data_provider_name = data_provider_mapping[ 'data_standalone' ]
else:
- data_provider_name = data_provider_mapping[ 'data' ]
- if data_provider_name:
- data_provider = self.get_data_provider( name=data_provider_name, original_dataset=original_dataset )
- else:
- data_provider = GenomeDataProvider
- except:
- pass
+ data_provider_name = data_provider_mapping[ source ]
+
+ data_provider = self.get_data_provider( trans, name=data_provider_name, original_dataset=original_dataset )
+
return data_provider
\ No newline at end of file
diff -r 8a8dcc0d36687fcfaaa1ae26e136e76b4a0d7bb0 -r bdd35af2a18afde11396fcf919b5fb4cacc986ff lib/galaxy/web/api/datasets.py
--- a/lib/galaxy/web/api/datasets.py
+++ b/lib/galaxy/web/api/datasets.py
@@ -86,28 +86,14 @@
if msg:
return msg
- # NOTE: finding valid chroms is prohibitive for large summary trees and is not currently used by
- # the client.
- valid_chroms = None
# Check for data in the genome window.
data_provider_registry = trans.app.data_provider_registry
- if data_sources.get( 'index' ):
- tracks_dataset_type = data_sources['index']['name']
- converted_dataset = dataset.get_converted_dataset( trans, tracks_dataset_type )
- indexer = data_provider_registry.get_data_provider( tracks_dataset_type )( converted_dataset, dataset )
- if not indexer.has_data( chrom ):
- return messages.NO_DATA
- #valid_chroms = indexer.valid_chroms()
- else:
- # Standalone data provider
- standalone_provider = data_provider_registry.get_data_provider( data_sources['data_standalone']['name'] )( dataset )
- kwargs = {"stats": True}
- if not standalone_provider.has_data( chrom ):
- return messages.NO_DATA
- #valid_chroms = standalone_provider.valid_chroms()
+ data_provider = trans.app.data_provider_registry.get_data_provider( trans, original_dataset= dataset, source='index' )
+ if not data_provider.has_data( chrom ):
+ return messages.NO_DATA
# Have data if we get here
- return { "status": messages.DATA, "valid_chroms": valid_chroms }
+ return { "status": messages.DATA, "valid_chroms": None }
def _search_features( self, trans, dataset, query ):
"""
@@ -151,45 +137,32 @@
data_provider_registry = trans.app.data_provider_registry
if mode == "Coverage":
# Get summary using minimal cutoffs.
- tracks_dataset_type = data_sources['index']['name']
- converted_dataset = dataset.get_converted_dataset( trans, tracks_dataset_type )
- indexer = data_provider_registry.get_data_provider( tracks_dataset_type )( converted_dataset, dataset )
+ indexer = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='index' )
summary = indexer.get_data( chrom, low, high, resolution=kwargs[ 'resolution' ], detail_cutoff=0, draw_cutoff=0 )
if summary == "detail":
# Use maximum level of detail--2--to get summary data no matter the resolution.
summary = indexer.get_data( chrom, low, high, resolution=kwargs[ 'resolution' ], level=2, detail_cutoff=0, draw_cutoff=0 )
frequencies, max_v, avg_v, delta = summary
- return { 'dataset_type': tracks_dataset_type, 'data': frequencies, 'max': max_v, 'avg': avg_v, 'delta': delta }
+ return { 'dataset_type': indexer.data_type, 'data': frequencies, 'max': max_v, 'avg': avg_v, 'delta': delta }
if 'index' in data_sources and data_sources['index']['name'] == "summary_tree" and mode == "Auto":
# Only check for summary_tree if it's Auto mode (which is the default)
#
# Have to choose between indexer and data provider
- tracks_dataset_type = data_sources['index']['name']
- converted_dataset = dataset.get_converted_dataset( trans, tracks_dataset_type )
- indexer = data_provider_registry.get_data_provider( tracks_dataset_type )( converted_dataset, dataset )
+ indexer = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='index' )
summary = indexer.get_data( chrom, low, high, resolution=kwargs[ 'resolution' ] )
if summary is None:
- return { 'dataset_type': tracks_dataset_type, 'data': None }
+ return { 'dataset_type': indexer.data_type, 'data': None }
if summary == "draw":
kwargs["no_detail"] = True # meh
extra_info = "no_detail"
elif summary != "detail":
frequencies, max_v, avg_v, delta = summary
- return { 'dataset_type': tracks_dataset_type, 'data': frequencies, 'max': max_v, 'avg': avg_v, 'delta': delta }
+ return { 'dataset_type': indexer.data_type, 'data': frequencies, 'max': max_v, 'avg': avg_v, 'delta': delta }
# Get data provider.
- if "data_standalone" in data_sources:
- tracks_dataset_type = data_sources['data_standalone']['name']
- data_provider_class = data_provider_registry.get_data_provider( name=tracks_dataset_type, original_dataset=dataset )
- data_provider = data_provider_class( original_dataset=dataset )
- else:
- tracks_dataset_type = data_sources['data']['name']
- data_provider_class = data_provider_registry.get_data_provider( name=tracks_dataset_type, original_dataset=dataset )
- converted_dataset = dataset.get_converted_dataset( trans, tracks_dataset_type )
- deps = dataset.get_converted_dataset_deps( trans, tracks_dataset_type )
- data_provider = data_provider_class( converted_dataset=converted_dataset, original_dataset=dataset, dependencies=deps )
+ data_provider = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='data' )
# Allow max_vals top be data provider set if not passed
if max_vals is None:
@@ -197,7 +170,7 @@
# Get and return data from data_provider.
result = data_provider.get_data( chrom, int( low ), int( high ), int( start_val ), int( max_vals ), **kwargs )
- result.update( { 'dataset_type': tracks_dataset_type, 'extra_info': extra_info } )
+ result.update( { 'dataset_type': data_provider.data_type, 'extra_info': extra_info } )
return result
def _raw_data( self, trans, dataset, **kwargs ):
@@ -214,7 +187,7 @@
# Return data.
data = None
- data_provider = trans.app.data_provider_registry.get_data_provider( raw=True, original_dataset=dataset )
+ data_provider = trans.app.data_provider_registry.get_data_provider( trans, raw=True, original_dataset=dataset )
if data_provider == ColumnDataProvider:
#pre: should have column kwargs
@@ -222,13 +195,13 @@
#TODO??: could default to first two here
assert 'cols' in kwargs, (
"ColumnDataProvider needs a 'cols' parameter in the query string" )
- data = data_provider( original_dataset=dataset ).get_data( **kwargs )
+ data = data_provider.get_data( **kwargs )
else:
# Default to genomic data.
# FIXME: need better way to set dataset_type.
low, high = int( kwargs.get( 'low' ) ), int( kwargs.get( 'high' ) )
- data = data_provider( original_dataset=dataset ).get_data( start=low, end=high, **kwargs )
+ data = data_provider.get_data( start=low, end=high, **kwargs )
data[ 'dataset_type' ] = 'interval_index'
data[ 'extra_info' ] = None
if isinstance( dataset.datatype, Vcf ):
diff -r 8a8dcc0d36687fcfaaa1ae26e136e76b4a0d7bb0 -r bdd35af2a18afde11396fcf919b5fb4cacc986ff lib/galaxy/web/api/tools.py
--- a/lib/galaxy/web/api/tools.py
+++ b/lib/galaxy/web/api/tools.py
@@ -204,15 +204,12 @@
if run_on_regions:
for jida in original_job.input_datasets:
input_dataset = jida.dataset
- if data_provider_registry.get_data_provider( original_dataset=input_dataset ):
- # Can index dataset.
- track_type, data_sources = input_dataset.datatype.get_track_type()
- # Convert to datasource that provides 'data' because we need to
- # extract the original data.
- data_source = data_sources[ 'data' ]
- msg = self.convert_dataset( trans, input_dataset, data_source )
- if msg is not None:
- messages_list.append( msg )
+ data_provider = data_provider_registry.get_data_provider( trans, original_dataset=input_dataset, source='data' )
+ if data_provider:
+ if not data_provider.converted_dataset:
+ msg = self.convert_dataset( trans, input_dataset, data_source )
+ if msg is not None:
+ messages_list.append( msg )
# Return any messages generated during conversions.
return_message = get_highest_priority_msg( messages_list )
@@ -326,10 +323,7 @@
trans.app.security_agent.set_all_dataset_permissions( new_dataset.dataset, hda_permissions )
# Write subset of data to new dataset
- data_provider_class = data_provider_registry.get_data_provider( original_dataset=input_dataset )
- data_provider = data_provider_class( original_dataset=input_dataset,
- converted_dataset=converted_dataset,
- dependencies=deps )
+ data_provider = data_provider_registry.get_data_provider( trans, original_dataset=input_dataset, source='data' )
trans.app.object_store.create( new_dataset.dataset )
data_provider.write_data_to_file( regions, new_dataset.file_name )
diff -r 8a8dcc0d36687fcfaaa1ae26e136e76b4a0d7bb0 -r bdd35af2a18afde11396fcf919b5fb4cacc986ff lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -460,8 +460,9 @@
prefs = {}
track_type, _ = dataset.datatype.get_track_type()
- track_data_provider_class = trans.app.data_provider_registry.get_data_provider( original_dataset=dataset )
- track_data_provider = track_data_provider_class( original_dataset=dataset )
+ track_data_provider = trans.app.data_provider_registry.get_data_provider( trans,
+ original_dataset=dataset,
+ source='data' )
return {
"track_type": track_type,
@@ -536,8 +537,8 @@
"""
# Get data provider.
track_type, _ = dataset.datatype.get_track_type()
- track_data_provider_class = trans.app.data_provider_registry.get_data_provider( original_dataset=dataset )
- track_data_provider = track_data_provider_class( original_dataset=dataset )
+ track_data_provider = trans.app.data_provider_registry.get_data_provider( trans, original_dataset=dataset )
+
if isinstance( dataset, trans.app.model.HistoryDatasetAssociation ):
hda_ldda = "hda"
diff -r 8a8dcc0d36687fcfaaa1ae26e136e76b4a0d7bb0 -r bdd35af2a18afde11396fcf919b5fb4cacc986ff static/scripts/viz/trackster/filters.js
--- a/static/scripts/viz/trackster/filters.js
+++ b/static/scripts/viz/trackster/filters.js
@@ -617,7 +617,8 @@
});
return {
- FiltersManager: FiltersManager
+ FiltersManager: FiltersManager,
+ NumberFilter: NumberFilter
};
});
diff -r 8a8dcc0d36687fcfaaa1ae26e136e76b4a0d7bb0 -r bdd35af2a18afde11396fcf919b5fb4cacc986ff static/scripts/viz/trackster/tracks.js
--- a/static/scripts/viz/trackster/tracks.js
+++ b/static/scripts/viz/trackster/tracks.js
@@ -1,7 +1,7 @@
define( ["libs/underscore", "viz/visualization", "viz/trackster/util",
"viz/trackster/slotting", "viz/trackster/painters", "mvc/data",
"viz/trackster/filters" ],
- function( _, visualization, util, slotting, painters, data, filters ) {
+ function( _, visualization, util, slotting, painters, data, filters_mod ) {
var extend = _.extend;
var get_random_color = util.get_random_color;
@@ -587,7 +587,7 @@
moveable(this.container_div, this.drag_handle_class, ".group", this);
// Set up filters.
- this.filters_manager = new filters.FiltersManager(this);
+ this.filters_manager = new filters_mod.FiltersManager(this);
this.header_div.after(this.filters_manager.parent_div);
// For saving drawables' filter managers when group-level filtering is done:
this.saved_filters_managers = [];
@@ -601,7 +601,7 @@
if ('filters' in obj_dict) {
// FIXME: Pass collection_dict to DrawableCollection/Drawable will make this easier.
var old_manager = this.filters_manager;
- this.filters_manager = new filters.FiltersManager(this, obj_dict.filters);
+ this.filters_manager = new filters_mod.FiltersManager(this, obj_dict.filters);
old_manager.parent_div.replaceWith(this.filters_manager.parent_div);
if (obj_dict.filters.visible) {
@@ -761,7 +761,7 @@
if (filters.length === num_feature_tracks) {
// Add new filter.
// FIXME: can filter.copy() be used?
- new_filter = new NumberFilter( {
+ new_filter = new filters_mod.NumberFilter( {
name: filters[0].name,
index: filters[0].index
} );
@@ -2612,7 +2612,7 @@
moveable(track.container_div, track.drag_handle_class, ".group", track);
// Attribute init.
- this.filters_manager = new filters.FiltersManager(this, ('filters' in obj_dict ? obj_dict.filters : null));
+ this.filters_manager = new filters_mod.FiltersManager(this, ('filters' in obj_dict ? obj_dict.filters : null));
// HACK: set filters manager for data manager.
// FIXME: prolly need function to set filters and update data_manager reference.
this.data_manager.set('filters_manager', this.filters_manager);
@@ -4188,7 +4188,6 @@
}
};
-// Exports
return {
View: View,
DrawableGroup: DrawableGroup,
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jgoecks: Push data sources/converter methods from controller mixin to dataset object.
by Bitbucket 21 Sep '12
by Bitbucket 21 Sep '12
21 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/8a8dcc0d3668/
changeset: 8a8dcc0d3668
user: jgoecks
date: 2012-09-21 17:52:47
summary: Push data sources/converter methods from controller mixin to dataset object.
affected #: 4 files
diff -r c56a600064a4add89de1c970e99983ae438b8db2 -r 8a8dcc0d36687fcfaaa1ae26e136e76b4a0d7bb0 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1248,6 +1248,56 @@
def get_visualizations( self ):
return self.datatype.get_visualizations( self )
+ def get_datasources( self, trans ):
+ """
+ Returns datasources for dataset; if datasources are not available
+ due to indexing, indexing is started. Return value is a dictionary
+ with entries of type
+ (<datasource_type> : {<datasource_name>, <indexing_message>}).
+ """
+ track_type, data_sources = self.datatype.get_track_type()
+ data_sources_dict = {}
+ msg = None
+ for source_type, data_source in data_sources.iteritems():
+ if source_type == "data_standalone":
+ # Nothing to do.
+ msg = None
+ else:
+ # Convert.
+ msg = self.convert_dataset( trans, data_source )
+
+ # Store msg.
+ data_sources_dict[ source_type ] = { "name" : data_source, "message": msg }
+
+ return data_sources_dict
+
+ def convert_dataset( self, trans, target_type ):
+ """
+ Converts a dataset to the target_type and returns a message indicating
+ status of the conversion. None is returned to indicate that dataset
+ was converted successfully.
+ """
+
+ # Get converted dataset; this will start the conversion if necessary.
+ try:
+ converted_dataset = self.get_converted_dataset( trans, target_type )
+ except NoConverterException:
+ return messages.NO_CONVERTER
+ except ConverterDependencyException, dep_error:
+ return { 'kind': messages.ERROR, 'message': dep_error.value }
+
+ # Check dataset state and return any messages.
+ msg = None
+ if converted_dataset and converted_dataset.state == trans.app.model.Dataset.states.ERROR:
+ job_id = trans.sa_session.query( trans.app.model.JobToOutputDatasetAssociation ) \
+ .filter_by( dataset_id=converted_dataset.id ).first().job_id
+ job = trans.sa_session.query( trans.app.model.Job ).get( job_id )
+ msg = { 'kind': messages.ERROR, 'message': job.stderr }
+ elif not converted_dataset or converted_dataset.state != trans.app.model.Dataset.states.OK:
+ msg = messages.PENDING
+
+ return msg
+
class HistoryDatasetAssociation( DatasetInstance ):
def __init__( self,
hid = None,
diff -r c56a600064a4add89de1c970e99983ae438b8db2 -r 8a8dcc0d36687fcfaaa1ae26e136e76b4a0d7bb0 lib/galaxy/web/api/datasets.py
--- a/lib/galaxy/web/api/datasets.py
+++ b/lib/galaxy/web/api/datasets.py
@@ -80,7 +80,7 @@
return msg
# Get datasources and check for messages.
- data_sources = self._get_datasources( trans, dataset )
+ data_sources = dataset.get_datasources( trans )
messages_list = [ data_source_dict[ 'message' ] for data_source_dict in data_sources.values() ]
msg = get_highest_priority_msg( messages_list )
if msg:
@@ -139,7 +139,7 @@
return msg
# Get datasources and check for messages.
- data_sources = self._get_datasources( trans, dataset )
+ data_sources = dataset.get_datasources( trans )
messages_list = [ data_source_dict[ 'message' ] for data_source_dict in data_sources.values() ]
return_message = get_highest_priority_msg( messages_list )
if return_message:
diff -r c56a600064a4add89de1c970e99983ae438b8db2 -r 8a8dcc0d36687fcfaaa1ae26e136e76b4a0d7bb0 lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -312,33 +312,6 @@
if dataset.state != trans.app.model.Job.states.OK:
return messages.PENDING
return None
-
- def convert_dataset( self, trans, dataset, target_type ):
- """
- Converts a dataset to the target_type and returns a message indicating
- status of the conversion. None is returned to indicate that dataset
- was converted successfully.
- """
-
- # Get converted dataset; this will start the conversion if necessary.
- try:
- converted_dataset = dataset.get_converted_dataset( trans, target_type )
- except NoConverterException:
- return messages.NO_CONVERTER
- except ConverterDependencyException, dep_error:
- return { 'kind': messages.ERROR, 'message': dep_error.value }
-
- # Check dataset state and return any messages.
- msg = None
- if converted_dataset and converted_dataset.state == trans.app.model.Dataset.states.ERROR:
- job_id = trans.sa_session.query( trans.app.model.JobToOutputDatasetAssociation ) \
- .filter_by( dataset_id=converted_dataset.id ).first().job_id
- job = trans.sa_session.query( trans.app.model.Job ).get( job_id )
- msg = { 'kind': messages.ERROR, 'message': job.stderr }
- elif not converted_dataset or converted_dataset.state != trans.app.model.Dataset.states.OK:
- msg = messages.PENDING
-
- return msg
class UsesLibraryMixin:
@@ -625,29 +598,6 @@
return visualization
- def _get_datasources( self, trans, dataset ):
- """
- Returns datasources for dataset; if datasources are not available
- due to indexing, indexing is started. Return value is a dictionary
- with entries of type
- (<datasource_type> : {<datasource_name>, <indexing_message>}).
- """
- track_type, data_sources = dataset.datatype.get_track_type()
- data_sources_dict = {}
- msg = None
- for source_type, data_source in data_sources.iteritems():
- if source_type == "data_standalone":
- # Nothing to do.
- msg = None
- else:
- # Convert.
- msg = self.convert_dataset( trans, dataset, data_source )
-
- # Store msg.
- data_sources_dict[ source_type ] = { "name" : data_source, "message": msg }
-
- return data_sources_dict
-
class UsesStoredWorkflowMixin( SharableItemSecurityMixin ):
""" Mixin for controllers that use StoredWorkflow objects. """
def get_stored_workflow( self, trans, id, check_ownership=True, check_accessible=False ):
diff -r c56a600064a4add89de1c970e99983ae438b8db2 -r 8a8dcc0d36687fcfaaa1ae26e136e76b4a0d7bb0 lib/galaxy/web/controllers/visualization.py
--- a/lib/galaxy/web/controllers/visualization.py
+++ b/lib/galaxy/web/controllers/visualization.py
@@ -755,7 +755,7 @@
for track in tracks:
# Get dataset and indexed datatype.
dataset = self.get_hda_or_ldda( trans, track[ 'hda_ldda'], track[ 'dataset_id' ] )
- data_sources = self._get_datasources( trans, dataset )
+ data_sources = dataset.get_datasources( trans )
data_provider_registry = trans.app.data_provider_registry
if 'data_standalone' in data_sources:
indexed_type = data_sources['data_standalone']['name']
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Add the ability to review tools migration stages that are available for the current Galaxy instance.
by Bitbucket 21 Sep '12
by Bitbucket 21 Sep '12
21 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/c56a600064a4/
changeset: c56a600064a4
user: greg
date: 2012-09-21 17:21:38
summary: Add the ability to review tools migration stages that are available for the current Galaxy instance.
affected #: 4 files
diff -r cd739bd3dca4859fecfe499fc976b0e0b6478238 -r c56a600064a4add89de1c970e99983ae438b8db2 lib/galaxy/tool_shed/migrate/versions/0004_tools.py
--- a/lib/galaxy/tool_shed/migrate/versions/0004_tools.py
+++ b/lib/galaxy/tool_shed/migrate/versions/0004_tools.py
@@ -1,9 +1,9 @@
"""
The NCBI BLAST+ tools have been eliminated from the distribution. The tools and
-datatypes are are now available in repositories named ncbi_blast_plus and
-blast_datatypes, respectively, from the main Galaxy tool shed at
-http://toolshed.g2.bx.psu.edu will be installed into your local Galaxy instance
-at the location discussed above by running the following command.
+datatypes are now available in repositories named ncbi_blast_plus and
+blast_datatypes, in the main Galaxy tool shed at http://toolshed.g2.bx.psu.edu.
+These repositories will be installed into your local Galaxy instance at the
+location discussed above by running the following command.
"""
import sys
diff -r cd739bd3dca4859fecfe499fc976b0e0b6478238 -r c56a600064a4add89de1c970e99983ae438b8db2 lib/galaxy/web/controllers/admin.py
--- a/lib/galaxy/web/controllers/admin.py
+++ b/lib/galaxy/web/controllers/admin.py
@@ -8,8 +8,10 @@
from galaxy.actions.admin import AdminActions
from galaxy.web.params import QuotaParamParser
from galaxy.exceptions import *
+from galaxy.util.odict import *
+from galaxy.tool_shed.encoding_util import *
import galaxy.datatypes.registry
-import logging
+import logging, imp, subprocess, urllib2
log = logging.getLogger( __name__ )
@@ -685,6 +687,75 @@
if emails is None:
emails = [ u.email for u in trans.sa_session.query( trans.app.model.User ).enable_eagerloads( False ).all() ]
return trans.fill_template( 'admin/impersonate.mako', emails=emails, message=message, status=status )
+ def get_tool_shed_url_from_tools_xml_file_path( self, trans, tool_shed ):
+ search_str = '://%s' % tool_shed
+ for shed_name, shed_url in trans.app.tool_shed_registry.tool_sheds.items():
+ if shed_url.find( search_str ) >= 0:
+ if shed_url.endswith( '/' ):
+ shed_url = shed_url.rstrip( '/' )
+ return shed_url
+ return None
+ def check_for_tool_dependencies( self, trans, migration_stage ):
+ # Get the 000x_tools.xml file associated with migration_stage.
+ tools_xml_file_path = os.path.abspath( os.path.join( trans.app.config.root, 'scripts', 'migrate_tools', '%04d_tools.xml' % migration_stage ) )
+ tree = util.parse_xml( tools_xml_file_path )
+ root = tree.getroot()
+ tool_shed = root.get( 'name' )
+ tool_shed_url = self.get_tool_shed_url_from_tools_xml_file_path( trans, tool_shed )
+ repo_name_dependency_tups = []
+ if tool_shed_url:
+ for elem in root:
+ if elem.tag == 'repository':
+ tool_dependencies = []
+ tool_dependencies_dict = {}
+ repository_name = elem.get( 'name' )
+ changeset_revision = elem.get( 'changeset_revision' )
+ url = '%s/repository/get_tool_dependencies?name=%s&owner=devteam&changeset_revision=%s&webapp=install_manager' % \
+ ( tool_shed_url, repository_name, changeset_revision )
+ response = urllib2.urlopen( url )
+ text = response.read()
+ response.close()
+ if text:
+ tool_dependencies_dict = tool_shed_decode( text )
+ for dependency_key, requirements_dict in tool_dependencies_dict.items():
+ tool_dependency_name = requirements_dict[ 'name' ]
+ tool_dependency_version = requirements_dict[ 'version' ]
+ tool_dependency_type = requirements_dict[ 'type' ]
+ tool_dependency_readme = requirements_dict.get( 'readme', '' )
+ tool_dependencies.append( ( tool_dependency_name, tool_dependency_version, tool_dependency_type, tool_dependency_readme ) )
+ repo_name_dependency_tups.append( ( repository_name, tool_dependencies ) )
+ return repo_name_dependency_tups
+ @web.expose
+ @web.require_admin
+ def review_tool_migration_stages( self, trans, **kwd ):
+ message = util.restore_text( kwd.get( 'message', '' ) )
+ status = util.restore_text( kwd.get( 'status', 'done' ) )
+ migration_stages_dict = odict()
+ migration_modules = []
+ migration_scripts_dir = os.path.abspath( os.path.join( trans.app.config.root, 'lib', 'galaxy', 'tool_shed', 'migrate', 'versions' ) )
+ migration_scripts_dir_contents = os.listdir( migration_scripts_dir )
+ for item in migration_scripts_dir_contents:
+ if os.path.isfile( os.path.join( migration_scripts_dir, item ) ) and item.endswith( '.py' ):
+ module = item.replace( '.py', '' )
+ migration_modules.append( module )
+ if migration_modules:
+ migration_modules.sort()
+ # Remove the 0001_tools.py script since it is the seed.
+ migration_modules = migration_modules[ 1: ]
+ # Reverse the list so viewing will be newest to oldest.
+ migration_modules.reverse()
+ for migration_module in migration_modules:
+ migration_stage = int( migration_module.replace( '_tools', '' ) )
+ repo_name_dependency_tups = self.check_for_tool_dependencies( trans, migration_stage )
+ open_file_obj, file_name, description = imp.find_module( migration_module, [ migration_scripts_dir ] )
+ imported_module = imp.load_module( 'upgrade', open_file_obj, file_name, description )
+ migration_info = imported_module.__doc__
+ open_file_obj.close()
+ migration_stages_dict[ migration_stage ] = ( migration_info, repo_name_dependency_tups )
+ return trans.fill_template( 'admin/review_tool_migration_stages.mako',
+ migration_stages_dict=migration_stages_dict,
+ message=message,
+ status=status )
@web.expose
@web.require_admin
def view_datatypes_registry( self, trans, **kwd ):
diff -r cd739bd3dca4859fecfe499fc976b0e0b6478238 -r c56a600064a4add89de1c970e99983ae438b8db2 templates/admin/review_tool_migration_stages.mako
--- /dev/null
+++ b/templates/admin/review_tool_migration_stages.mako
@@ -0,0 +1,120 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolForm">
+ <div class="toolFormTitle">Tool migrations that can be performed on this Galaxy instance</div>
+ <div class="toolFormBody">
+ <div class="form-row">
+ <p>
+ The list of tool migration stages below, displayed most recent to oldest, provides information about the repositories in the
+ main Galaxy tool shed that will be cloned at each stage if you run the shell command for that stage. This enables you to execute
+ the migration process for any stage at any time.
+ </p>
+ <p>
+ Keep in mind that tools included in a repository that you want to be displayed in the Galaxy tool panel when the repository is
+ installed must be defined in the <b>tool_conf.xml</b> (or equivalent) config file prior to execution of the migration process for a
+ stage. Executing a migration process multiple times will have no affect unless the repositories associated with that stage have been
+ uninstalled prior to the execution of the migration process.
+ </p>
+ <p>
+ When you initiate a migration process, the associated repositories will be cloned from the Galaxy tool shed at
+ <a href="http://toolshed.g2.bx.psu.edu" target="_blank">http://toolshed.g2.bx.psu.edu</a>. The location in which the tool repositories
+ will be installed is the value of the 'tool_path' attribute in the <tool> tag of the file named ./migrated_tool_conf.xml
+ (i.e., <b><toolbox tool_path="../shed_tools"></b>). The default location setting is <b>'../shed_tools'</b>, which may be problematic
+ for some cluster environments, so make sure to change it before you execute the installation process if appropriate. The configured location
+ must be outside of the Galaxy installation directory or it must be in a sub-directory protected by a properly configured <b>.hgignore</b>
+ file if the directory is within the Galaxy installation directory hierarchy. This is because tool shed repositories will be installed using
+ mercurial's clone feature, which creates .hg directories and associated mercurial repository files. Not having <b>.hgignore</b> properly
+ configured could result in undesired behavior when modifying or updating your local Galaxy instance or the tool shed repositories if they are
+ in directories that pose conflicts. See mercurial's .hgignore documentation at
+ <a href="http://mercurial.selenic.com/wiki/.hgignore" target="_blank">http://mercurial.selenic.com/wiki/.hgignore</a> for details.
+ </p>
+ </div>
+ <table class="grid">
+ %for stage in migration_stages_dict.keys():
+ <%
+ migration_command = 'sh ./scripts/migrate_tools/%04d_tools.sh' % stage
+ install_dependencies = '%s install_dependencies' % migration_command
+ migration_tup = migration_stages_dict[ stage ]
+ migration_info, repo_name_dependency_tups = migration_tup
+ repository_names = []
+ for repo_name_dependency_tup in repo_name_dependency_tups:
+ repository_name, tool_dependencies = repo_name_dependency_tup
+ if repository_name not in repository_names:
+ repository_names.append( repository_name )
+ if repository_names:
+ repository_names.sort()
+ repository_names = ', '.join( repository_names )
+ %>
+ <tr><td bgcolor="#D8D8D8"><b>Tool migration stage ${stage} - repositories: ${repository_names}</b></td></tr>
+ <tr>
+ <td bgcolor="#FFFFCC">
+ <div class="form-row">
+ <p>${migration_info} <b>Run commands from the Galaxy installation directory!</b></p>
+ <p>
+ %if tool_dependencies:
+ This migration stage includes tools that have tool dependencies that can be automatically installed. To install them, run:<br/>
+ <b>${install_dependencies}</b><br/><br/>
+ To skip tool dependency installation run:<br/>
+ <b>${migration_command}</b>
+ %else:
+ <b>${migration_command}</b>
+ %endif
+ </p>
+ </div>
+ </td>
+ </tr>
+ %for repo_name_dependency_tup in repo_name_dependency_tups:
+ <% repository_name, tool_dependencies = repo_name_dependency_tup %>
+ <tr>
+ <td bgcolor="#DADFEF">
+ <div class="form-row">
+ <b>Repository:</b> ${repository_name}
+ </div>
+ </td>
+ </tr>
+ %if tool_dependencies:
+ <tr>
+ <td>
+ <div class="form-row">
+ <b>Tool dependencies</b>
+ </div>
+ </td>
+ </tr>
+ %for tool_dependencies_tup in tool_dependencies:
+ <%
+ tool_dependency_name = tool_dependencies_tup[0]
+ tool_dependency_version = tool_dependencies_tup[1]
+ tool_dependency_type = tool_dependencies_tup[2]
+ installation_requirements = tool_dependencies_tup[3].replace( '\n', '<br/>' )
+ %>
+ <tr>
+ <td>
+ <div class="form-row">
+ <b>Name:</b> ${tool_dependency_name} <b>Version:</b> ${tool_dependency_version} <b>Type:</b> ${tool_dependency_type}
+ </div>
+ <div class="form-row">
+ <b>Requirements and installation information:</b><br/>
+ ${installation_requirements}
+ </div>
+ </td>
+ </tr>
+ %endfor
+ %else:
+ <tr>
+ <td>
+ <div class="form-row">
+ No tool dependencies have been defined for this repository.
+ </div>
+ </td>
+ </tr>
+ %endif
+ %endfor
+ %endfor
+ </table>
+ </div>
+</div>
diff -r cd739bd3dca4859fecfe499fc976b0e0b6478238 -r c56a600064a4add89de1c970e99983ae438b8db2 templates/webapps/galaxy/admin/index.mako
--- a/templates/webapps/galaxy/admin/index.mako
+++ b/templates/webapps/galaxy/admin/index.mako
@@ -66,10 +66,11 @@
<div class="toolSectionBody"><div class="toolSectionBg"><div class="toolTitle"><a href="${h.url_for( controller='admin', action='view_datatypes_registry' )}" target="galaxy_main">View datatypes registry</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='tool_versions' )}" target="galaxy_main">Tool versions</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='tool_versions' )}" target="galaxy_main">View tool lineage</a></div><div class="toolTitle"><a href="${h.url_for( controller='admin', action='reload_tool' )}" target="galaxy_main">Reload a tool's configuration</a></div><div class="toolTitle"><a href="${h.url_for( controller='admin', action='memdump' )}" target="galaxy_main">Profile memory usage</a></div><div class="toolTitle"><a href="${h.url_for( controller='admin', action='jobs' )}" target="galaxy_main">Manage jobs</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='admin', action='review_tool_migration_stages' )}" target="galaxy_main">Review tool migration stages</a></div>
%if installing_repository_ids:
<div class="toolTitle"><a href="${h.url_for( controller='admin_toolshed', action='monitor_repository_installation', tool_shed_repository_ids=installing_repository_ids )}" target="galaxy_main">Monitor installing tool shed repositories</a></div>
%endif
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
21 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/cd739bd3dca4/
changeset: cd739bd3dca4
user: jgoecks
date: 2012-09-21 17:14:56
summary: Place track filters in their own file.
affected #: 2 files
diff -r 040555fcc6b5341e2878c0d4ec33f54c712273c2 -r cd739bd3dca4859fecfe499fc976b0e0b6478238 static/scripts/viz/trackster/filters.js
--- /dev/null
+++ b/static/scripts/viz/trackster/filters.js
@@ -0,0 +1,623 @@
+define( ["libs/underscore"], function(_) {
+
+var extend = _.extend;
+
+/**
+ * Filters that enable users to show/hide data points dynamically.
+ */
+var Filter = function(obj_dict) {
+ this.manager = null;
+ this.name = obj_dict.name;
+ // Index into payload to filter.
+ this.index = obj_dict.index;
+ this.tool_id = obj_dict.tool_id;
+ // Name to use for filter when building expression for tool.
+ this.tool_exp_name = obj_dict.tool_exp_name;
+};
+
+extend(Filter.prototype, {
+ /**
+ * Convert filter to dictionary.
+ */
+ to_dict: function() {
+ return {
+ name: this.name,
+ index: this.index,
+ tool_id: this.tool_id,
+ tool_exp_name: this.tool_exp_name
+ };
+ }
+});
+
+/**
+ * Creates an action icon.
+ */
+var create_action_icon = function(title, css_class, on_click_fn) {
+ return $("<a/>").attr("href", "javascript:void(0);").attr("title", title)
+ .addClass("icon-button").addClass(css_class).tooltip()
+ .click(on_click_fn);
+};
+
+/**
+ * Number filters have a min, max as well as a low, high; low and high are used
+ */
+var NumberFilter = function(obj_dict) {
+ //
+ // Attribute init.
+ //
+ Filter.call(this, obj_dict);
+ // Filter low/high. These values are used to filter elements.
+ this.low = ('low' in obj_dict ? obj_dict.low : -Number.MAX_VALUE);
+ this.high = ('high' in obj_dict ? obj_dict.high : Number.MAX_VALUE);
+ // Slide min/max. These values are used to set/update slider.
+ this.min = ('min' in obj_dict ? obj_dict.min : Number.MAX_VALUE);
+ this.max = ('max' in obj_dict ? obj_dict.max : -Number.MAX_VALUE);
+ // UI elements associated with filter.
+ this.container = null;
+ this.slider = null;
+ this.slider_label = null;
+
+ //
+ // Create HTML.
+ //
+
+ // Function that supports inline text editing of slider values.
+ // Enable users to edit parameter's value via a text box.
+ var edit_slider_values = function(container, span, slider) {
+ container.click(function() {
+ var cur_value = span.text(),
+ max = parseFloat(slider.slider("option", "max")),
+ input_size = (max <= 1 ? 4 : max <= 1000000 ? max.toString().length : 6),
+ multi_value = false,
+ slider_row = $(this).parents(".slider-row");
+
+ // Row now has input.
+ slider_row.addClass("input");
+
+ // Increase input size if there are two values.
+ if (slider.slider("option", "values")) {
+ input_size = 2*input_size + 1;
+ multi_value = true;
+ }
+ span.text("");
+ // Temporary input for changing value.
+ $("<input type='text'/>").attr("size", input_size).attr("maxlength", input_size)
+ .attr("value", cur_value).appendTo(span).focus().select()
+ .click(function(e) {
+ // Don't want click to propogate up to values_span and restart everything.
+ e.stopPropagation();
+ }).blur(function() {
+ $(this).remove();
+ span.text(cur_value);
+ slider_row.removeClass("input");
+ }).keyup(function(e) {
+ if (e.keyCode === 27) {
+ // Escape key.
+ $(this).trigger("blur");
+ } else if (e.keyCode === 13) {
+ //
+ // Enter/return key initiates callback. If new value(s) are in slider range,
+ // change value (which calls slider's change() function).
+ //
+ var slider_min = slider.slider("option", "min"),
+ slider_max = slider.slider("option", "max"),
+ invalid = function(a_val) {
+ return (isNaN(a_val) || a_val > slider_max || a_val < slider_min);
+ },
+ new_value = $(this).val();
+ if (!multi_value) {
+ new_value = parseFloat(new_value);
+ if (invalid(new_value)) {
+ alert("Parameter value must be in the range [" + slider_min + "-" + slider_max + "]");
+ return $(this);
+ }
+ }
+ else { // Multi value.
+ new_value = new_value.split("-");
+ new_value = [parseFloat(new_value[0]), parseFloat(new_value[1])];
+ if (invalid(new_value[0]) || invalid(new_value[1])) {
+ alert("Parameter value must be in the range [" + slider_min + "-" + slider_max + "]");
+ return $(this);
+ }
+ }
+
+ // Updating the slider also updates slider values and removes input.
+ slider.slider((multi_value ? "values" : "value"), new_value);
+ slider_row.removeClass("input");
+ }
+ });
+ });
+ };
+
+ var filter = this;
+
+ filter.parent_div = $("<div/>").addClass("filter-row slider-row");
+
+ // Set up filter label (name, values).
+ var filter_label = $("<div/>").addClass("elt-label").appendTo(filter.parent_div),
+ name_span = $("<span/>").addClass("slider-name").text(filter.name + " ").appendTo(filter_label),
+ values_span = $("<span/>").text(this.low + "-" + this.high),
+ values_span_container = $("<span/>").addClass("slider-value").appendTo(filter_label).append("[").append(values_span).append("]");
+ filter.values_span = values_span;
+
+ // Set up slider for filter.
+ var slider_div = $("<div/>").addClass("slider").appendTo(filter.parent_div);
+ filter.control_element = $("<div/>").attr("id", filter.name + "-filter-control").appendTo(slider_div);
+ filter.control_element.slider({
+ range: true,
+ min: this.min,
+ max: this.max,
+ step: this.get_slider_step(this.min, this.max),
+ values: [this.low, this.high],
+ slide: function(event, ui) {
+ filter.slide(event, ui);
+ },
+ change: function(event, ui) {
+ filter.control_element.slider("option", "slide").call(filter.control_element, event, ui);
+ }
+ });
+ filter.slider = filter.control_element;
+ filter.slider_label = values_span;
+
+ // Enable users to edit slider values via text box.
+ edit_slider_values(values_span_container, values_span, filter.control_element);
+
+ // Set up filter display controls.
+ var display_controls_div = $("<div/>").addClass("display-controls").appendTo(filter.parent_div);
+ this.transparency_icon = create_action_icon("Use filter for data transparency", "layer-transparent",
+ function() {
+ if (filter.manager.alpha_filter !== filter) {
+ // Setting this filter as the alpha filter.
+ filter.manager.alpha_filter = filter;
+ // Update UI for new filter.
+ filter.manager.parent_div.find(".layer-transparent").removeClass("active").hide();
+ filter.transparency_icon.addClass("active").show();
+ }
+ else {
+ // Clearing filter as alpha filter.
+ filter.manager.alpha_filter = null;
+ filter.transparency_icon.removeClass("active");
+ }
+ filter.manager.track.request_draw(true, true);
+ } )
+ .appendTo(display_controls_div).hide();
+ this.height_icon = create_action_icon("Use filter for data height", "arrow-resize-090",
+ function() {
+ if (filter.manager.height_filter !== filter) {
+ // Setting this filter as the height filter.
+ filter.manager.height_filter = filter;
+ // Update UI for new filter.
+ filter.manager.parent_div.find(".arrow-resize-090").removeClass("active").hide();
+ filter.height_icon.addClass("active").show();
+ }
+ else {
+ // Clearing filter as alpha filter.
+ filter.manager.height_filter = null;
+ filter.height_icon.removeClass("active");
+ }
+ filter.manager.track.request_draw(true, true);
+ } )
+ .appendTo(display_controls_div).hide();
+ filter.parent_div.hover( function() {
+ filter.transparency_icon.show();
+ filter.height_icon.show();
+ },
+ function() {
+ if (filter.manager.alpha_filter !== filter) {
+ filter.transparency_icon.hide();
+ }
+ if (filter.manager.height_filter !== filter) {
+ filter.height_icon.hide();
+ }
+ } );
+
+ // Add to clear floating layout.
+ $("<div style='clear: both;'/>").appendTo(filter.parent_div);
+};
+extend(NumberFilter.prototype, {
+ /**
+ * Convert filter to dictionary.
+ */
+ to_dict: function() {
+ var obj_dict = Filter.prototype.to_dict.call(this);
+ return extend(obj_dict, {
+ type: 'number',
+ min: this.min,
+ max: this.max,
+ low: this.low,
+ high: this.high
+ });
+ },
+ /**
+ * Return a copy of filter.
+ */
+ copy: function() {
+ return new NumberFilter(
+ {
+ name: this.name,
+ index: this.index,
+ tool_id: this.tool_id,
+ tool_exp_name: this.tool_exp_name
+ });
+ },
+ /**
+ * Get step for slider.
+ */
+ // FIXME: make this a "static" function.
+ get_slider_step: function(min, max) {
+ var range = max - min;
+ return (range <= 2 ? 0.01 : 1);
+ },
+ /**
+ * Handle slide events.
+ */
+ slide: function(event, ui) {
+ var values = ui.values;
+
+ // Set new values in UI.
+ this.values_span.text(values[0] + "-" + values[1]);
+
+ // Set new values in filter.
+ this.low = values[0];
+ this.high = values[1];
+
+ // Set timeout to update if filter low, high are stable.
+ var self = this;
+ setTimeout(function() {
+ if (values[0] === self.low && values[1] === self.high) {
+ self.manager.track.request_draw(true, true);
+ }
+ }, 25);
+
+ },
+ /**
+ * Returns true if filter can be applied to element.
+ */
+ applies_to: function(element) {
+ if (element.length > this.index) {
+ return true;
+ }
+ return false;
+ },
+ /**
+ * Helper function: returns true if value in in filter's [low, high] range.
+ */
+ _keep_val: function(val) {
+ return (isNaN(val) || (val >= this.low && val <= this.high));
+ },
+ /**
+ * Returns true if (a) element's value(s) is in [low, high] (range is inclusive)
+ * or (b) if value is non-numeric and hence unfilterable.
+ */
+ keep: function(element) {
+ if ( !this.applies_to( element ) ) {
+ // No element to filter on.
+ return true;
+ }
+
+ // Keep value function.
+ var filter = this;
+
+ // Do filtering.
+ var to_filter = element[this.index];
+ if (to_filter instanceof Array) {
+ var returnVal = true;
+ for (var i = 0; i < to_filter.length; i++) {
+ if (!this._keep_val(to_filter[i])) {
+ // Exclude element.
+ returnVal = false;
+ break;
+ }
+ }
+ return returnVal;
+ }
+ else {
+ return this._keep_val(element[this.index]);
+ }
+ },
+ /**
+ * Update filter's min and max values based on element's values.
+ */
+ update_attrs: function(element) {
+ var updated = false;
+ if (!this.applies_to(element) ) {
+ return updated;
+ }
+
+ //
+ // Update filter's min, max based on element values.
+ //
+
+ // Make value(s) into an Array.
+ var values = element[this.index];
+ if (!(values instanceof Array)) {
+ values = [values];
+ }
+
+ // Loop through values and update min, max.
+ for (var i = 0; i < values.length; i++) {
+ var value = values[i];
+ if (value < this.min) {
+ this.min = Math.floor(value);
+ updated = true;
+ }
+ if (value > this.max) {
+ this.max = Math.ceil(value);
+ updated = true;
+ }
+ }
+ return updated;
+ },
+ /**
+ * Update filter's slider.
+ */
+ update_ui_elt: function () {
+ // Only show filter if min < max because filter is not useful otherwise. This
+ // covers all corner cases, such as when min, max have not been defined and
+ // when min == max.
+ if (this.min < this.max) {
+ this.parent_div.show();
+ }
+ else {
+ this.parent_div.hide();
+ }
+
+ var
+ slider_min = this.slider.slider("option", "min"),
+ slider_max = this.slider.slider("option", "max");
+ if (this.min < slider_min || this.max > slider_max) {
+ // Update slider min, max, step.
+ this.slider.slider("option", "min", this.min);
+ this.slider.slider("option", "max", this.max);
+ this.slider.slider("option", "step", this.get_slider_step(this.min, this.max));
+ // Refresh slider:
+ // TODO: do we want to keep current values or reset to min/max?
+ // Currently we reset values:
+ this.slider.slider("option", "values", [this.min, this.max]);
+ // To use the current values.
+ //var values = this.slider.slider( "option", "values" );
+ //this.slider.slider( "option", "values", values );
+ }
+ }
+});
+
+/**
+ * Manages a set of filters.
+ */
+var FiltersManager = function(track, obj_dict) {
+ this.track = track;
+ this.alpha_filter = null;
+ this.height_filter = null;
+ this.filters = [];
+
+ //
+ // Create HTML.
+ //
+
+ //
+ // Create parent div.
+ //
+ this.parent_div = $("<div/>").addClass("filters").hide();
+ // Disable dragging, double clicking, keys on div so that actions on slider do not impact viz.
+ this.parent_div.bind("drag", function(e) {
+ e.stopPropagation();
+ }).click(function(e) {
+ e.stopPropagation();
+ }).bind("dblclick", function(e) {
+ e.stopPropagation();
+ }).bind("keydown", function(e) {
+ e.stopPropagation();
+ });
+
+ //
+ // Restore state from dict.
+ //
+ if (obj_dict && 'filters' in obj_dict) { // Second condition needed for backward compatibility.
+ var
+ alpha_filter_name = ('alpha_filter' in obj_dict ? obj_dict.alpha_filter : null),
+ height_filter_name = ('height_filter' in obj_dict ? obj_dict.height_filter : null),
+ filters_dict = obj_dict.filters,
+ filter;
+ for (var i = 0; i < filters_dict.length; i++) {
+ if (filters_dict[i].type === 'number') {
+ filter = new NumberFilter(filters_dict[i]);
+ this.add_filter(filter);
+ if (filter.name === alpha_filter_name) {
+ this.alpha_filter = filter;
+ filter.transparency_icon.addClass("active").show();
+ }
+ if (filter.name === height_filter_name) {
+ this.height_filter = filter;
+ filter.height_icon.addClass("active").show();
+ }
+ }
+ else {
+ console.log("ERROR: unsupported filter: ", name, type);
+ }
+ }
+
+
+ if ('visible' in obj_dict && obj_dict.visible) {
+ this.parent_div.show();
+ }
+ }
+
+ // Add button to filter complete dataset.
+ if (this.filters.length !== 0) {
+ var run_buttons_row = $("<div/>").addClass("param-row").appendTo(this.parent_div);
+ var run_on_dataset_button = $("<input type='submit'/>").attr("value", "Run on complete dataset").appendTo(run_buttons_row);
+ var filter_manager = this;
+ run_on_dataset_button.click( function() {
+ filter_manager.run_on_dataset();
+ });
+ }
+
+};
+
+extend(FiltersManager.prototype, {
+ // HTML manipulation and inspection.
+ show: function() { this.parent_div.show(); },
+ hide: function() { this.parent_div.hide(); },
+ toggle: function() { this.parent_div.toggle(); },
+ visible: function() { return this.parent_div.is(":visible"); },
+ /**
+ * Returns dictionary for manager.
+ */
+ to_dict: function() {
+ var obj_dict = {},
+ filter_dicts = [],
+ filter;
+
+ // Include individual filter states.
+ for (var i = 0; i < this.filters.length; i++) {
+ filter = this.filters[i];
+ filter_dicts.push(filter.to_dict());
+ }
+ obj_dict.filters = filter_dicts;
+
+ // Include transparency, height filters.
+ obj_dict.alpha_filter = (this.alpha_filter ? this.alpha_filter.name : null);
+ obj_dict.height_filter = (this.height_filter ? this.height_filter.name : null);
+
+ // Include visibility.
+ obj_dict.visible = this.parent_div.is(":visible");
+
+ return obj_dict;
+ },
+ /**
+ * Return a copy of the manager.
+ */
+ copy: function(new_track) {
+ var copy = new FiltersManager(new_track);
+ for (var i = 0; i < this.filters.length; i++) {
+ copy.add_filter(this.filters[i].copy());
+ }
+ return copy;
+ },
+ /**
+ * Add a filter to the manager.
+ */
+ add_filter: function(filter) {
+ filter.manager = this;
+ this.parent_div.append(filter.parent_div);
+ this.filters.push(filter);
+ },
+ /**
+ * Remove all filters from manager.
+ */
+ remove_all: function() {
+ this.filters = [];
+ this.parent_div.children().remove();
+ },
+ /**
+ * Initialize filters.
+ */
+ init_filters: function() {
+ for (var i = 0; i < this.filters.length; i++) {
+ var filter = this.filters[i];
+ filter.update_ui_elt();
+ }
+ },
+ /**
+ * Clear filters so that they do not impact track display.
+ */
+ clear_filters: function() {
+ for (var i = 0; i < this.filters.length; i++) {
+ var filter = this.filters[i];
+ filter.slider.slider("option", "values", [filter.min, filter.max]);
+ }
+ this.alpha_filter = null;
+ this.height_filter = null;
+
+ // Hide icons for setting filters.
+ this.parent_div.find(".icon-button").hide();
+ },
+ run_on_dataset: function() {
+ // Get or create dictionary item.
+ var get_or_create_dict_item = function(dict, key, new_item) {
+ // Add new item to dict if
+ if (!(key in dict)) {
+ dict[key] = new_item;
+ }
+ return dict[key];
+ };
+
+ //
+ // Find and group active filters. Active filters are those being used to hide data.
+ // Filters with the same tool id are grouped.
+ //
+ var active_filters = {},
+ filter,
+ tool_filter_conditions;
+ for (var i = 0; i < this.filters.length; i++) {
+ filter = this.filters[i];
+ if (filter.tool_id) {
+ // Add filtering conditions if filter low/high are set.
+ if (filter.min !== filter.low) {
+ tool_filter_conditions = get_or_create_dict_item(active_filters, filter.tool_id, []);
+ tool_filter_conditions[tool_filter_conditions.length] = filter.tool_exp_name + " >= " + filter.low;
+ }
+ if (filter.max !== filter.high) {
+ tool_filter_conditions = get_or_create_dict_item(active_filters, filter.tool_id, []);
+ tool_filter_conditions[tool_filter_conditions.length] = filter.tool_exp_name + " <= " + filter.high;
+ }
+ }
+ }
+
+ //
+ // Use tools to run filters.
+ //
+
+ // Create list of (tool_id, tool_filters) tuples.
+ var active_filters_list = [];
+ for (var tool_id in active_filters) {
+ active_filters_list[active_filters_list.length] = [tool_id, active_filters[tool_id]];
+ }
+
+ // Invoke recursive function to run filters; this enables chaining of filters via
+ // iteratively application.
+ (function run_filter(input_dataset_id, filters) {
+ var
+ // Set up filtering info and params.
+ filter_tuple = filters[0],
+ tool_id = filter_tuple[0],
+ tool_filters = filter_tuple[1],
+ tool_filter_str = "(" + tool_filters.join(") and (") + ")",
+ url_params = {
+ cond: tool_filter_str,
+ input: input_dataset_id,
+ target_dataset_id: input_dataset_id,
+ tool_id: tool_id
+ };
+
+ // Remove current filter.
+ filters = filters.slice(1);
+
+ $.getJSON(run_tool_url, url_params, function(response) {
+ if (response.error) {
+ // General error.
+ show_modal("Filter Dataset",
+ "Error running tool " + tool_id,
+ { "Close" : hide_modal } );
+ }
+ else if (filters.length === 0) {
+ // No more filters to run.
+ show_modal("Filtering Dataset",
+ "Filter(s) are running on the complete dataset. Outputs are in dataset's history.",
+ { "Close" : hide_modal } );
+ }
+ else {
+ // More filters to run.
+ run_filter(response.dataset_id, filters);
+ }
+ });
+
+ })(this.track.dataset_id, active_filters_list);
+ }
+});
+
+return {
+ FiltersManager: FiltersManager
+};
+
+});
diff -r 040555fcc6b5341e2878c0d4ec33f54c712273c2 -r cd739bd3dca4859fecfe499fc976b0e0b6478238 static/scripts/viz/trackster/tracks.js
--- a/static/scripts/viz/trackster/tracks.js
+++ b/static/scripts/viz/trackster/tracks.js
@@ -1,6 +1,7 @@
define( ["libs/underscore", "viz/visualization", "viz/trackster/util",
- "viz/trackster/slotting", "viz/trackster/painters", "mvc/data" ],
- function( _, visualization, util, slotting, painters, data ) {
+ "viz/trackster/slotting", "viz/trackster/painters", "mvc/data",
+ "viz/trackster/filters" ],
+ function( _, visualization, util, slotting, painters, data, filters ) {
var extend = _.extend;
var get_random_color = util.get_random_color;
@@ -586,7 +587,7 @@
moveable(this.container_div, this.drag_handle_class, ".group", this);
// Set up filters.
- this.filters_manager = new FiltersManager(this);
+ this.filters_manager = new filters.FiltersManager(this);
this.header_div.after(this.filters_manager.parent_div);
// For saving drawables' filter managers when group-level filtering is done:
this.saved_filters_managers = [];
@@ -600,7 +601,7 @@
if ('filters' in obj_dict) {
// FIXME: Pass collection_dict to DrawableCollection/Drawable will make this easier.
var old_manager = this.filters_manager;
- this.filters_manager = new FiltersManager(this, obj_dict.filters);
+ this.filters_manager = new filters.FiltersManager(this, obj_dict.filters);
old_manager.parent_div.replaceWith(this.filters_manager.parent_div);
if (obj_dict.filters.visible) {
@@ -1831,619 +1832,6 @@
});
/**
- * Filters that enable users to show/hide data points dynamically.
- */
-var Filter = function(obj_dict) {
- this.manager = null;
- this.name = obj_dict.name;
- // Index into payload to filter.
- this.index = obj_dict.index;
- this.tool_id = obj_dict.tool_id;
- // Name to use for filter when building expression for tool.
- this.tool_exp_name = obj_dict.tool_exp_name;
-};
-
-extend(Filter.prototype, {
- /**
- * Convert filter to dictionary.
- */
- to_dict: function() {
- return {
- name: this.name,
- index: this.index,
- tool_id: this.tool_id,
- tool_exp_name: this.tool_exp_name
- };
- }
-});
-
-/**
- * Creates an action icon.
- */
-var create_action_icon = function(title, css_class, on_click_fn) {
- return $("<a/>").attr("href", "javascript:void(0);").attr("title", title)
- .addClass("icon-button").addClass(css_class).tooltip()
- .click(on_click_fn);
-};
-
-/**
- * Number filters have a min, max as well as a low, high; low and high are used
- */
-var NumberFilter = function(obj_dict) {
- //
- // Attribute init.
- //
- Filter.call(this, obj_dict);
- // Filter low/high. These values are used to filter elements.
- this.low = ('low' in obj_dict ? obj_dict.low : -Number.MAX_VALUE);
- this.high = ('high' in obj_dict ? obj_dict.high : Number.MAX_VALUE);
- // Slide min/max. These values are used to set/update slider.
- this.min = ('min' in obj_dict ? obj_dict.min : Number.MAX_VALUE);
- this.max = ('max' in obj_dict ? obj_dict.max : -Number.MAX_VALUE);
- // UI elements associated with filter.
- this.container = null;
- this.slider = null;
- this.slider_label = null;
-
- //
- // Create HTML.
- //
-
- // Function that supports inline text editing of slider values.
- // Enable users to edit parameter's value via a text box.
- var edit_slider_values = function(container, span, slider) {
- container.click(function() {
- var cur_value = span.text(),
- max = parseFloat(slider.slider("option", "max")),
- input_size = (max <= 1 ? 4 : max <= 1000000 ? max.toString().length : 6),
- multi_value = false,
- slider_row = $(this).parents(".slider-row");
-
- // Row now has input.
- slider_row.addClass("input");
-
- // Increase input size if there are two values.
- if (slider.slider("option", "values")) {
- input_size = 2*input_size + 1;
- multi_value = true;
- }
- span.text("");
- // Temporary input for changing value.
- $("<input type='text'/>").attr("size", input_size).attr("maxlength", input_size)
- .attr("value", cur_value).appendTo(span).focus().select()
- .click(function(e) {
- // Don't want click to propogate up to values_span and restart everything.
- e.stopPropagation();
- }).blur(function() {
- $(this).remove();
- span.text(cur_value);
- slider_row.removeClass("input");
- }).keyup(function(e) {
- if (e.keyCode === 27) {
- // Escape key.
- $(this).trigger("blur");
- } else if (e.keyCode === 13) {
- //
- // Enter/return key initiates callback. If new value(s) are in slider range,
- // change value (which calls slider's change() function).
- //
- var slider_min = slider.slider("option", "min"),
- slider_max = slider.slider("option", "max"),
- invalid = function(a_val) {
- return (isNaN(a_val) || a_val > slider_max || a_val < slider_min);
- },
- new_value = $(this).val();
- if (!multi_value) {
- new_value = parseFloat(new_value);
- if (invalid(new_value)) {
- alert("Parameter value must be in the range [" + slider_min + "-" + slider_max + "]");
- return $(this);
- }
- }
- else { // Multi value.
- new_value = new_value.split("-");
- new_value = [parseFloat(new_value[0]), parseFloat(new_value[1])];
- if (invalid(new_value[0]) || invalid(new_value[1])) {
- alert("Parameter value must be in the range [" + slider_min + "-" + slider_max + "]");
- return $(this);
- }
- }
-
- // Updating the slider also updates slider values and removes input.
- slider.slider((multi_value ? "values" : "value"), new_value);
- slider_row.removeClass("input");
- }
- });
- });
- };
-
- var filter = this;
-
- filter.parent_div = $("<div/>").addClass("filter-row slider-row");
-
- // Set up filter label (name, values).
- var filter_label = $("<div/>").addClass("elt-label").appendTo(filter.parent_div),
- name_span = $("<span/>").addClass("slider-name").text(filter.name + " ").appendTo(filter_label),
- values_span = $("<span/>").text(this.low + "-" + this.high),
- values_span_container = $("<span/>").addClass("slider-value").appendTo(filter_label).append("[").append(values_span).append("]");
- filter.values_span = values_span;
-
- // Set up slider for filter.
- var slider_div = $("<div/>").addClass("slider").appendTo(filter.parent_div);
- filter.control_element = $("<div/>").attr("id", filter.name + "-filter-control").appendTo(slider_div);
- filter.control_element.slider({
- range: true,
- min: this.min,
- max: this.max,
- step: this.get_slider_step(this.min, this.max),
- values: [this.low, this.high],
- slide: function(event, ui) {
- filter.slide(event, ui);
- },
- change: function(event, ui) {
- filter.control_element.slider("option", "slide").call(filter.control_element, event, ui);
- }
- });
- filter.slider = filter.control_element;
- filter.slider_label = values_span;
-
- // Enable users to edit slider values via text box.
- edit_slider_values(values_span_container, values_span, filter.control_element);
-
- // Set up filter display controls.
- var display_controls_div = $("<div/>").addClass("display-controls").appendTo(filter.parent_div);
- this.transparency_icon = create_action_icon("Use filter for data transparency", "layer-transparent",
- function() {
- if (filter.manager.alpha_filter !== filter) {
- // Setting this filter as the alpha filter.
- filter.manager.alpha_filter = filter;
- // Update UI for new filter.
- filter.manager.parent_div.find(".layer-transparent").removeClass("active").hide();
- filter.transparency_icon.addClass("active").show();
- }
- else {
- // Clearing filter as alpha filter.
- filter.manager.alpha_filter = null;
- filter.transparency_icon.removeClass("active");
- }
- filter.manager.track.request_draw(true, true);
- } )
- .appendTo(display_controls_div).hide();
- this.height_icon = create_action_icon("Use filter for data height", "arrow-resize-090",
- function() {
- if (filter.manager.height_filter !== filter) {
- // Setting this filter as the height filter.
- filter.manager.height_filter = filter;
- // Update UI for new filter.
- filter.manager.parent_div.find(".arrow-resize-090").removeClass("active").hide();
- filter.height_icon.addClass("active").show();
- }
- else {
- // Clearing filter as alpha filter.
- filter.manager.height_filter = null;
- filter.height_icon.removeClass("active");
- }
- filter.manager.track.request_draw(true, true);
- } )
- .appendTo(display_controls_div).hide();
- filter.parent_div.hover( function() {
- filter.transparency_icon.show();
- filter.height_icon.show();
- },
- function() {
- if (filter.manager.alpha_filter !== filter) {
- filter.transparency_icon.hide();
- }
- if (filter.manager.height_filter !== filter) {
- filter.height_icon.hide();
- }
- } );
-
- // Add to clear floating layout.
- $("<div style='clear: both;'/>").appendTo(filter.parent_div);
-};
-extend(NumberFilter.prototype, {
- /**
- * Convert filter to dictionary.
- */
- to_dict: function() {
- var obj_dict = Filter.prototype.to_dict.call(this);
- return extend(obj_dict, {
- type: 'number',
- min: this.min,
- max: this.max,
- low: this.low,
- high: this.high
- });
- },
- /**
- * Return a copy of filter.
- */
- copy: function() {
- return new NumberFilter(
- {
- name: this.name,
- index: this.index,
- tool_id: this.tool_id,
- tool_exp_name: this.tool_exp_name
- });
- },
- /**
- * Get step for slider.
- */
- // FIXME: make this a "static" function.
- get_slider_step: function(min, max) {
- var range = max - min;
- return (range <= 2 ? 0.01 : 1);
- },
- /**
- * Handle slide events.
- */
- slide: function(event, ui) {
- var values = ui.values;
-
- // Set new values in UI.
- this.values_span.text(values[0] + "-" + values[1]);
-
- // Set new values in filter.
- this.low = values[0];
- this.high = values[1];
-
- // Set timeout to update if filter low, high are stable.
- var self = this;
- setTimeout(function() {
- if (values[0] === self.low && values[1] === self.high) {
- self.manager.track.request_draw(true, true);
- }
- }, 25);
-
- },
- /**
- * Returns true if filter can be applied to element.
- */
- applies_to: function(element) {
- if (element.length > this.index) {
- return true;
- }
- return false;
- },
- /**
- * Helper function: returns true if value in in filter's [low, high] range.
- */
- _keep_val: function(val) {
- return (isNaN(val) || (val >= this.low && val <= this.high));
- },
- /**
- * Returns true if (a) element's value(s) is in [low, high] (range is inclusive)
- * or (b) if value is non-numeric and hence unfilterable.
- */
- keep: function(element) {
- if ( !this.applies_to( element ) ) {
- // No element to filter on.
- return true;
- }
-
- // Keep value function.
- var filter = this;
-
- // Do filtering.
- var to_filter = element[this.index];
- if (to_filter instanceof Array) {
- var returnVal = true;
- for (var i = 0; i < to_filter.length; i++) {
- if (!this._keep_val(to_filter[i])) {
- // Exclude element.
- returnVal = false;
- break;
- }
- }
- return returnVal;
- }
- else {
- return this._keep_val(element[this.index]);
- }
- },
- /**
- * Update filter's min and max values based on element's values.
- */
- update_attrs: function(element) {
- var updated = false;
- if (!this.applies_to(element) ) {
- return updated;
- }
-
- //
- // Update filter's min, max based on element values.
- //
-
- // Make value(s) into an Array.
- var values = element[this.index];
- if (!(values instanceof Array)) {
- values = [values];
- }
-
- // Loop through values and update min, max.
- for (var i = 0; i < values.length; i++) {
- var value = values[i];
- if (value < this.min) {
- this.min = Math.floor(value);
- updated = true;
- }
- if (value > this.max) {
- this.max = Math.ceil(value);
- updated = true;
- }
- }
- return updated;
- },
- /**
- * Update filter's slider.
- */
- update_ui_elt: function () {
- // Only show filter if min < max because filter is not useful otherwise. This
- // covers all corner cases, such as when min, max have not been defined and
- // when min == max.
- if (this.min < this.max) {
- this.parent_div.show();
- }
- else {
- this.parent_div.hide();
- }
-
- var
- slider_min = this.slider.slider("option", "min"),
- slider_max = this.slider.slider("option", "max");
- if (this.min < slider_min || this.max > slider_max) {
- // Update slider min, max, step.
- this.slider.slider("option", "min", this.min);
- this.slider.slider("option", "max", this.max);
- this.slider.slider("option", "step", this.get_slider_step(this.min, this.max));
- // Refresh slider:
- // TODO: do we want to keep current values or reset to min/max?
- // Currently we reset values:
- this.slider.slider("option", "values", [this.min, this.max]);
- // To use the current values.
- //var values = this.slider.slider( "option", "values" );
- //this.slider.slider( "option", "values", values );
- }
- }
-});
-
-/**
- * Manages a set of filters.
- */
-var FiltersManager = function(track, obj_dict) {
- this.track = track;
- this.alpha_filter = null;
- this.height_filter = null;
- this.filters = [];
-
- //
- // Create HTML.
- //
-
- //
- // Create parent div.
- //
- this.parent_div = $("<div/>").addClass("filters").hide();
- // Disable dragging, double clicking, keys on div so that actions on slider do not impact viz.
- this.parent_div.bind("drag", function(e) {
- e.stopPropagation();
- }).click(function(e) {
- e.stopPropagation();
- }).bind("dblclick", function(e) {
- e.stopPropagation();
- }).bind("keydown", function(e) {
- e.stopPropagation();
- });
-
- //
- // Restore state from dict.
- //
- if (obj_dict && 'filters' in obj_dict) { // Second condition needed for backward compatibility.
- var
- alpha_filter_name = ('alpha_filter' in obj_dict ? obj_dict.alpha_filter : null),
- height_filter_name = ('height_filter' in obj_dict ? obj_dict.height_filter : null),
- filters_dict = obj_dict.filters,
- filter;
- for (var i = 0; i < filters_dict.length; i++) {
- if (filters_dict[i].type === 'number') {
- filter = new NumberFilter(filters_dict[i]);
- this.add_filter(filter);
- if (filter.name === alpha_filter_name) {
- this.alpha_filter = filter;
- filter.transparency_icon.addClass("active").show();
- }
- if (filter.name === height_filter_name) {
- this.height_filter = filter;
- filter.height_icon.addClass("active").show();
- }
- }
- else {
- console.log("ERROR: unsupported filter: ", name, type);
- }
- }
-
-
- if ('visible' in obj_dict && obj_dict.visible) {
- this.parent_div.show();
- }
- }
-
- // Add button to filter complete dataset.
- if (this.filters.length !== 0) {
- var run_buttons_row = $("<div/>").addClass("param-row").appendTo(this.parent_div);
- var run_on_dataset_button = $("<input type='submit'/>").attr("value", "Run on complete dataset").appendTo(run_buttons_row);
- var filter_manager = this;
- run_on_dataset_button.click( function() {
- filter_manager.run_on_dataset();
- });
- }
-
-};
-
-extend(FiltersManager.prototype, {
- // HTML manipulation and inspection.
- show: function() { this.parent_div.show(); },
- hide: function() { this.parent_div.hide(); },
- toggle: function() { this.parent_div.toggle(); },
- visible: function() { return this.parent_div.is(":visible"); },
- /**
- * Returns dictionary for manager.
- */
- to_dict: function() {
- var obj_dict = {},
- filter_dicts = [],
- filter;
-
- // Include individual filter states.
- for (var i = 0; i < this.filters.length; i++) {
- filter = this.filters[i];
- filter_dicts.push(filter.to_dict());
- }
- obj_dict.filters = filter_dicts;
-
- // Include transparency, height filters.
- obj_dict.alpha_filter = (this.alpha_filter ? this.alpha_filter.name : null);
- obj_dict.height_filter = (this.height_filter ? this.height_filter.name : null);
-
- // Include visibility.
- obj_dict.visible = this.parent_div.is(":visible");
-
- return obj_dict;
- },
- /**
- * Return a copy of the manager.
- */
- copy: function(new_track) {
- var copy = new FiltersManager(new_track);
- for (var i = 0; i < this.filters.length; i++) {
- copy.add_filter(this.filters[i].copy());
- }
- return copy;
- },
- /**
- * Add a filter to the manager.
- */
- add_filter: function(filter) {
- filter.manager = this;
- this.parent_div.append(filter.parent_div);
- this.filters.push(filter);
- },
- /**
- * Remove all filters from manager.
- */
- remove_all: function() {
- this.filters = [];
- this.parent_div.children().remove();
- },
- /**
- * Initialize filters.
- */
- init_filters: function() {
- for (var i = 0; i < this.filters.length; i++) {
- var filter = this.filters[i];
- filter.update_ui_elt();
- }
- },
- /**
- * Clear filters so that they do not impact track display.
- */
- clear_filters: function() {
- for (var i = 0; i < this.filters.length; i++) {
- var filter = this.filters[i];
- filter.slider.slider("option", "values", [filter.min, filter.max]);
- }
- this.alpha_filter = null;
- this.height_filter = null;
-
- // Hide icons for setting filters.
- this.parent_div.find(".icon-button").hide();
- },
- run_on_dataset: function() {
- // Get or create dictionary item.
- var get_or_create_dict_item = function(dict, key, new_item) {
- // Add new item to dict if
- if (!(key in dict)) {
- dict[key] = new_item;
- }
- return dict[key];
- };
-
- //
- // Find and group active filters. Active filters are those being used to hide data.
- // Filters with the same tool id are grouped.
- //
- var active_filters = {},
- filter,
- tool_filter_conditions;
- for (var i = 0; i < this.filters.length; i++) {
- filter = this.filters[i];
- if (filter.tool_id) {
- // Add filtering conditions if filter low/high are set.
- if (filter.min !== filter.low) {
- tool_filter_conditions = get_or_create_dict_item(active_filters, filter.tool_id, []);
- tool_filter_conditions[tool_filter_conditions.length] = filter.tool_exp_name + " >= " + filter.low;
- }
- if (filter.max !== filter.high) {
- tool_filter_conditions = get_or_create_dict_item(active_filters, filter.tool_id, []);
- tool_filter_conditions[tool_filter_conditions.length] = filter.tool_exp_name + " <= " + filter.high;
- }
- }
- }
-
- //
- // Use tools to run filters.
- //
-
- // Create list of (tool_id, tool_filters) tuples.
- var active_filters_list = [];
- for (var tool_id in active_filters) {
- active_filters_list[active_filters_list.length] = [tool_id, active_filters[tool_id]];
- }
-
- // Invoke recursive function to run filters; this enables chaining of filters via
- // iteratively application.
- (function run_filter(input_dataset_id, filters) {
- var
- // Set up filtering info and params.
- filter_tuple = filters[0],
- tool_id = filter_tuple[0],
- tool_filters = filter_tuple[1],
- tool_filter_str = "(" + tool_filters.join(") and (") + ")",
- url_params = {
- cond: tool_filter_str,
- input: input_dataset_id,
- target_dataset_id: input_dataset_id,
- tool_id: tool_id
- },
- // Remove current filter.
- filters = filters.slice(1);
-
- $.getJSON(run_tool_url, url_params, function(response) {
- if (response.error) {
- // General error.
- show_modal("Filter Dataset",
- "Error running tool " + tool_id,
- { "Close" : hide_modal } );
- }
- else if (filters.length === 0) {
- // No more filters to run.
- show_modal("Filtering Dataset",
- "Filter(s) are running on the complete dataset. Outputs are in dataset's history.",
- { "Close" : hide_modal } );
- }
- else {
- // More filters to run.
- run_filter(response.dataset_id, filters);
- }
- });
-
- })(this.track.dataset_id, active_filters_list);
- }
-});
-
-/**
* Generates scale values based on filter and feature's value for filter.
*/
var FilterScaler = function(filter, default_val) {
@@ -3224,7 +2612,7 @@
moveable(track.container_div, track.drag_handle_class, ".group", track);
// Attribute init.
- this.filters_manager = new FiltersManager(this, ('filters' in obj_dict ? obj_dict.filters : null));
+ this.filters_manager = new filters.FiltersManager(this, ('filters' in obj_dict ? obj_dict.filters : null));
// HACK: set filters manager for data manager.
// FIXME: prolly need function to set filters and update data_manager reference.
this.data_manager.set('filters_manager', this.filters_manager);
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