galaxy-commits
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions
details: http://www.bx.psu.edu/hg/galaxy/rev/9057a05dc7d3
changeset: 3739:9057a05dc7d3
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Tue May 04 13:45:57 2010 -0400
description:
Update types for cuffcompare output files.
diffstat:
tools/ngs_rna/cuffcompare_wrapper.xml | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)
diffs (31 lines):
diff -r a3f32c37d423 -r 9057a05dc7d3 tools/ngs_rna/cuffcompare_wrapper.xml
--- a/tools/ngs_rna/cuffcompare_wrapper.xml Tue May 04 12:50:48 2010 -0400
+++ b/tools/ngs_rna/cuffcompare_wrapper.xml Tue May 04 13:45:57 2010 -0400
@@ -50,21 +50,21 @@
</inputs>
<outputs>
- <data format="tmap" name="input1_tmap" label="${tool.name} on ${on_string}: data ${input1.hid} tmap file"/>
- <data format="refmap" name="input1_refmap" label="${tool.name} on ${on_string}: data ${input1.hid} refmap file"/>
- <data format="tmap" name="input2_tmap" label="${tool.name} on ${on_string}: data ${second_gtf.input2.hid} tmap file">
+ <data format="tabular" name="input1_tmap" label="${tool.name} on ${on_string}: data ${input1.hid} tmap file"/>
+ <data format="tabular" name="input1_refmap" label="${tool.name} on ${on_string}: data ${input1.hid} refmap file"/>
+ <data format="tabular" name="input2_tmap" label="${tool.name} on ${on_string}: data ${second_gtf.input2.hid} tmap file">
<filter>second_gtf['use_second_gtf'] == "Yes"</filter>
</data>
- <data format="refmap" name="input2_refmap" label="${tool.name} on ${on_string}: data ${second_gtf.input2.hid} refmap file">
+ <data format="tabular" name="input2_refmap" label="${tool.name} on ${on_string}: data ${second_gtf.input2.hid} refmap file">
<filter>second_gtf['use_second_gtf'] == "Yes"</filter>
</data>
<data format="gtf" name="transcripts_combined" label="${tool.name} on ${on_string}: combined transcripts">
<filter>second_gtf['use_second_gtf'] == "Yes"</filter>
</data>
- <data format="tracking" name="transcripts_tracking" label="${tool.name} on ${on_string}: transcript tracking">
+ <data format="tabular" name="transcripts_tracking" label="${tool.name} on ${on_string}: transcript tracking">
<filter>second_gtf['use_second_gtf'] == "Yes"</filter>
</data>
- <data format="gtf" name="transcripts_accuracy" label="${tool.name} on ${on_string}: transcript accuracy"/>
+ <data format="txt" name="transcripts_accuracy" label="${tool.name} on ${on_string}: transcript accuracy"/>
</outputs>
<tests>
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/a3f32c37d423
changeset: 3738:a3f32c37d423
user: Kanwei Li <kanwei(a)gmail.com>
date: Tue May 04 12:50:48 2010 -0400
description:
trackster: add filled line display
diffstat:
static/scripts/trackster.js | 48 ++++++++++++++++++++++++++++++---------------
1 files changed, 32 insertions(+), 16 deletions(-)
diffs (128 lines):
diff -r 3b81af3f51ab -r a3f32c37d423 static/scripts/trackster.js
--- a/static/scripts/trackster.js Tue May 04 10:18:42 2010 -0400
+++ b/static/scripts/trackster.js Tue May 04 12:50:48 2010 -0400
@@ -352,7 +352,6 @@
TiledTrack.call( this );
this.height_px = 100;
- this.container_div.addClass( "line-track" );
this.dataset_id = dataset_id;
this.data_cache = new Cache(CACHED_DATA);
this.tile_cache = new Cache(CACHED_TILES_LINE);
@@ -369,6 +368,8 @@
track.vertical_range = undefined;
this.init_each({ stats: true, chrom: track.view.chrom, low: null, high: null,
dataset_id: track.dataset_id }, function(result) {
+
+ track.container_div.addClass( "line-track" );
data = result.data;
if ( isNaN(parseFloat(track.prefs.min_value)) || isNaN(parseFloat(track.prefs.max_value)) ) {
track.prefs.min_value = data.min;
@@ -387,10 +388,10 @@
var min_label = $("<div></div>").addClass('yaxislabel').attr("id", 'linetrack_' + track_id + '_minval').text(track.prefs.min_value);
var max_label = $("<div></div>").addClass('yaxislabel').attr("id", 'linetrack_' + track_id + '_maxval').text(track.prefs.max_value);
- max_label.css({ position: "relative", top: "25px" });
+ max_label.css({ position: "relative", top: "25px", left: "10px" });
max_label.prependTo(track.container_div);
- min_label.css({ position: "relative", top: track.height_px + 55 + "px" });
+ min_label.css({ position: "relative", top: track.height_px + 55 + "px", left: "10px" });
min_label.prependTo(track.container_div);
});
},
@@ -455,7 +456,8 @@
max_value = this.prefs.max_value,
vertical_range = this.vertical_range,
total_frequency = this.total_frequency,
- height_px = this.height_px;
+ height_px = this.height_px,
+ mode = this.prefs.mode;
ctx.beginPath();
@@ -466,16 +468,17 @@
var delta_x_px = 10;
}
+ var x_scaled, y;
+
for ( var i = 0; i < data.length; i++ ) {
- var x = data[i][0] - tile_low;
- var y = data[i][1];
+ x_scaled = (data[i][0] - tile_low) * w_scale;
+ y = data[i][1];
- if ( this.prefs.mode == "Intensity" ) {
+ if ( mode == "Intensity" ) {
// DRAW INTENSITY
if (y === null) {
continue;
}
- x = x * w_scale;
if (y <= min_value) {
y = min_value;
} else if (y >= max_value) {
@@ -483,16 +486,17 @@
}
y = 255 - Math.floor( (y - min_value) / vertical_range * 255 );
ctx.fillStyle = "rgb(" +y+ "," +y+ "," +y+ ")";
- ctx.fillRect(x, 0, delta_x_px, this.height_px);
+ ctx.fillRect(x_scaled, 0, delta_x_px, this.height_px);
}
else {
// Missing data causes us to stop drawing
if (y === null) {
+ if (in_path && mode === "Filled") {
+ ctx.lineTo(x_scaled, height_px);
+ }
in_path = false;
continue;
} else {
- // Translate
- x = x * w_scale;
// console.log(y, this.min_value, this.vertical_range, (y - this.min_value) / this.vertical_range * this.height_px);
if (y <= min_value) {
y = min_value;
@@ -501,16 +505,28 @@
}
y = Math.round( height_px - (y - min_value) / vertical_range * height_px );
// console.log(canvas.get(0).height, canvas.get(0).width);
- if ( in_path ) {
- ctx.lineTo( x, y );
+ if (in_path) {
+ ctx.lineTo(x_scaled, y);
} else {
- ctx.moveTo( x, y );
in_path = true;
+ if (mode === "Filled") {
+ ctx.moveTo(x_scaled, height_px);
+ ctx.lineTo(x_scaled, y);
+ } else {
+ ctx.moveTo(x_scaled, y);
+ }
}
}
}
}
- ctx.stroke();
+ if (mode === "Filled") {
+ if (in_path) {
+ ctx.lineTo(x_scaled, height_px);
+ }
+ ctx.fill();
+ } else {
+ ctx.stroke();
+ }
parent_element.append( canvas );
return canvas;
}, gen_options: function(track_id) {
@@ -527,7 +543,7 @@
max_input = $('<input></input>').attr("id", maxval).val(max_val),
mode_label = $('<label></label>').attr("for", mode).text("Display mode:"),
mode_val = (this.prefs.mode === undefined ? "Line" : this.prefs.mode),
- mode_input = $('<select id="' +mode+ '"><option value="Line" id="mode_Line">Line</option><option value="Intensity" id="mode_Intensity">Intensity</option></select>');
+ mode_input = $('<select id="' +mode+ '"><option value="Line" id="mode_Line">Line</option><option value="Filled" id="mode_Filled">Filled</option><option value="Intensity" id="mode_Intensity">Intensity</option></select>');
mode_input.children("#mode_"+mode_val).attr('selected', 'selected');
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/3b81af3f51ab
changeset: 3737:3b81af3f51ab
user: Nate Coraor <nate(a)bx.psu.edu>
date: Tue May 04 10:18:42 2010 -0400
description:
Add a tip to the upload tool about using url paste for big files
diffstat:
tools/data_source/upload.xml | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diffs (12 lines):
diff -r eec050533545 -r 3b81af3f51ab tools/data_source/upload.xml
--- a/tools/data_source/upload.xml Mon May 03 17:35:54 2010 -0400
+++ b/tools/data_source/upload.xml Tue May 04 10:18:42 2010 -0400
@@ -25,7 +25,7 @@
</param>
<param name="async_datasets" type="hidden" value="None"/>
<upload_dataset name="files" title="Specify Files for Dataset" file_type_name="file_type" metadata_ref="files_metadata">
- <param name="file_data" type="file" size="30" label="File" ajax-upload="true">
+ <param name="file_data" type="file" size="30" label="File" ajax-upload="true" help="TIP: For NGS and other large files (>~2GB), uploading via HTTP/FTP URL (below) is the most reliable method.">
<validator type="expression" message="You will need to reselect the file you specified (%s)." substitute_value_in_message="True">not ( ( isinstance( value, unicode ) or isinstance( value, str ) ) and value != "" )</validator> <!-- use validator to post message to user about needing to reselect the file, since most browsers won't accept the value attribute for file inputs -->
</param>
<param name="url_paste" type="text" area="true" size="5x35" label="URL/Text" help="Here you may specify a list of URLs (one per line) or paste the contents of a file."/>
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/eec050533545
changeset: 3736:eec050533545
user: James Taylor <james(a)jamestaylor.org>
date: Mon May 03 17:35:54 2010 -0400
description:
Very basic support for exporting and importing workflows among instances
diffstat:
lib/galaxy/tools/__init__.py | 24 +++--
lib/galaxy/web/__init__.py | 2 +-
lib/galaxy/web/controllers/workflow.py | 153 ++++++++++++++++++++++++++++++++-
lib/galaxy/web/framework/__init__.py | 9 +
lib/galaxy/workflow/modules.py | 14 +-
templates/form.mako | 6 +-
6 files changed, 187 insertions(+), 21 deletions(-)
diffs (327 lines):
diff -r c75c0f9b0bf7 -r eec050533545 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py Mon May 03 16:32:35 2010 -0400
+++ b/lib/galaxy/tools/__init__.py Mon May 03 17:35:54 2010 -0400
@@ -203,7 +203,7 @@
def __init__( self ):
self.page = 0
self.inputs = None
- def encode( self, tool, app ):
+ def encode( self, tool, app, secure=True ):
"""
Convert the data to a string
"""
@@ -213,18 +213,22 @@
value["__page__"] = self.page
value = simplejson.dumps( value )
# Make it secure
- a = hmac_new( app.config.tool_secret, value )
- b = binascii.hexlify( value )
- return "%s:%s" % ( a, b )
- def decode( self, value, tool, app ):
+ if secure:
+ a = hmac_new( app.config.tool_secret, value )
+ b = binascii.hexlify( value )
+ return "%s:%s" % ( a, b )
+ else:
+ return value
+ def decode( self, value, tool, app, secure=True ):
"""
Restore the state from a string
"""
- # Extract and verify hash
- a, b = value.split( ":" )
- value = binascii.unhexlify( b )
- test = hmac_new( app.config.tool_secret, value )
- assert a == test
+ if secure:
+ # Extract and verify hash
+ a, b = value.split( ":" )
+ value = binascii.unhexlify( b )
+ test = hmac_new( app.config.tool_secret, value )
+ assert a == test
# Restore from string
values = json_fix( simplejson.loads( value ) )
self.page = values.pop( "__page__" )
diff -r c75c0f9b0bf7 -r eec050533545 lib/galaxy/web/__init__.py
--- a/lib/galaxy/web/__init__.py Mon May 03 16:32:35 2010 -0400
+++ b/lib/galaxy/web/__init__.py Mon May 03 17:35:54 2010 -0400
@@ -2,6 +2,6 @@
The Galaxy web application.
"""
-from framework import expose, json, require_login, require_admin, url_for, error, form, FormBuilder
+from framework import expose, json, json_pretty, require_login, require_admin, url_for, error, form, FormBuilder
from framework.base import httpexceptions
diff -r c75c0f9b0bf7 -r eec050533545 lib/galaxy/web/controllers/workflow.py
--- a/lib/galaxy/web/controllers/workflow.py Mon May 03 16:32:35 2010 -0400
+++ b/lib/galaxy/web/controllers/workflow.py Mon May 03 17:35:54 2010 -0400
@@ -18,6 +18,8 @@
from galaxy.model.mapping import desc
from galaxy.model.orm import *
+import urllib2
+
class StoredWorkflowListGrid( grids.Grid ):
class StepsColumn( grids.GridColumn ):
def get_value(self, trans, grid, workflow):
@@ -621,7 +623,6 @@
step_dict['position'] = step.position
# Add to return value
data['steps'][step.order_index] = step_dict
- print data['upgrade_messages']
return data
@web.json
@@ -660,7 +661,6 @@
workflow.has_errors = True
# Stick this in the step temporarily
step.temp_input_connections = step_dict['input_connections']
-
# Save step annotation.
annotation = step_dict[ 'annotation' ]
if annotation:
@@ -698,6 +698,155 @@
rval['name'] = workflow.name
return rval
+ @web.json_pretty
+ def export_workflow( self, trans, id ):
+ """
+ Get the latest Workflow for the StoredWorkflow identified by `id` and
+ encode it as a json string that can be imported back into Galaxy
+
+ This has slightly different information than the above. In particular,
+ it does not attempt to decode forms and build UIs, it just stores
+ the raw state.
+ """
+ user = trans.get_user()
+ id = trans.security.decode_id( id )
+ trans.workflow_building_mode = True
+ # Load encoded workflow from database
+ stored = trans.sa_session.query( model.StoredWorkflow ).get( id )
+ self.security_check( trans.get_user(), stored, False, True )
+ workflow = stored.latest_workflow
+ # Pack workflow data into a dictionary and return
+ data = {}
+ data['name'] = workflow.name
+ data['steps'] = {}
+ # For each step, rebuild the form and encode the state
+ for step in workflow.steps:
+ # Load from database representation
+ module = module_factory.from_workflow_step( trans, step )
+ # Get user annotation.
+ step_annotation = self.get_item_annotation_obj( trans, trans.user, step )
+ annotation_str = ""
+ if step_annotation:
+ annotation_str = step_annotation.annotation
+ # Pack attributes into plain dictionary
+ step_dict = {
+ 'id': step.order_index,
+ 'type': module.type,
+ 'tool_id': module.get_tool_id(),
+ 'name': module.get_name(),
+ 'tool_state': module.get_state( secure=False ),
+ 'tool_errors': module.get_errors(),
+ ## 'data_inputs': module.get_data_inputs(),
+ ## 'data_outputs': module.get_data_outputs(),
+ 'annotation' : annotation_str
+ }
+ # Connections
+ input_connections = step.input_connections
+ if step.type is None or step.type == 'tool':
+ # Determine full (prefixed) names of valid input datasets
+ data_input_names = {}
+ def callback( input, value, prefixed_name, prefixed_label ):
+ if isinstance( input, DataToolParameter ):
+ data_input_names[ prefixed_name ] = True
+ visit_input_values( module.tool.inputs, module.state.inputs, callback )
+ # Filter
+ # FIXME: this removes connection without displaying a message currently!
+ input_connections = [ conn for conn in input_connections if conn.input_name in data_input_names ]
+ # Encode input connections as dictionary
+ input_conn_dict = {}
+ for conn in input_connections:
+ input_conn_dict[ conn.input_name ] = \
+ dict( id=conn.output_step.order_index, output_name=conn.output_name )
+ step_dict['input_connections'] = input_conn_dict
+ # Position
+ step_dict['position'] = step.position
+ # Add to return value
+ data['steps'][step.order_index] = step_dict
+ return data
+
+ @web.expose
+ def import_workflow( self, trans, workflow_text=None, url=None ):
+ if workflow_text is None and url is None:
+ return form( url_for(), "Import Workflow", submit_text="Import" ) \
+ .add_text( "url", "URL to load workflow from", "" ) \
+ .add_input( "textarea", "Encoded workflow (as generated by export workflow)", "workflow_text", "" )
+ if url:
+ # Load workflow from external URL
+ # NOTE: blocks the web thread.
+ try:
+ workflow_data = urllib2.urlopen( url ).read()
+ except Exception, e:
+ return trans.show_error_message( "Failed to open URL %s<br><br>Message: %s" % ( url, str( e ) ) )
+ else:
+ workflow_data = workflow_text
+ # Convert incoming workflow data from json
+ try:
+ data = simplejson.loads( workflow_data )
+ except Exception, e:
+ return trans.show_error_message( "Data at '%s' does not appear to be a Galaxy workflow<br><br>Message: %s" % ( url, str( e ) ) )
+ # Put parameters in workflow mode
+ trans.workflow_building_mode = True
+ # Create new workflow from incoming data
+ workflow = model.Workflow()
+ # Just keep the last name (user can rename later)
+ workflow.name = data['name']
+ # Assume no errors until we find a step that has some
+ workflow.has_errors = False
+ # Create each step
+ steps = []
+ # The editor will provide ids for each step that we don't need to save,
+ # but do need to use to make connections
+ steps_by_external_id = {}
+ # First pass to build step objects and populate basic values
+ for key, step_dict in data['steps'].iteritems():
+ # Create the model class for the step
+ step = model.WorkflowStep()
+ steps.append( step )
+ steps_by_external_id[ step_dict['id' ] ] = step
+ # FIXME: Position should be handled inside module
+ step.position = step_dict['position']
+ module = module_factory.from_dict( trans, step_dict, secure=False )
+ module.save_to_step( step )
+ if step.tool_errors:
+ workflow.has_errors = True
+ # Stick this in the step temporarily
+ step.temp_input_connections = step_dict['input_connections']
+ # Save step annotation.
+ annotation = step_dict[ 'annotation' ]
+ if annotation:
+ annotation = sanitize_html( annotation, 'utf-8', 'text/html' )
+ self.add_item_annotation( trans, step, annotation )
+ # Second pass to deal with connections between steps
+ for step in steps:
+ # Input connections
+ for input_name, conn_dict in step.temp_input_connections.iteritems():
+ if conn_dict:
+ conn = model.WorkflowStepConnection()
+ conn.input_step = step
+ conn.input_name = input_name
+ conn.output_name = conn_dict['output_name']
+ conn.output_step = steps_by_external_id[ conn_dict['id'] ]
+ del step.temp_input_connections
+ # Order the steps if possible
+ attach_ordered_steps( workflow, steps )
+ # Connect up
+ stored = model.StoredWorkflow()
+ stored.name = workflow.name
+ workflow.stored_workflow = stored
+ stored.latest_workflow = workflow
+ stored.user = trans.user
+ # Persist
+ trans.sa_session.add( stored )
+ trans.sa_session.flush()
+ # Return something informative
+ errors = []
+ if workflow.has_errors:
+ return trans.show_warn_message( "Imported, but some steps in this workflow have validation errors" )
+ if workflow.has_cycles:
+ return trans.show_warn_message( "Imported, but this workflow contains cycles" )
+ else:
+ return trans.show_message( "Workflow '%s' imported" % workflow.name )
+
@web.json
def get_datatypes( self, trans ):
ext_to_class_name = dict()
diff -r c75c0f9b0bf7 -r eec050533545 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py Mon May 03 16:32:35 2010 -0400
+++ b/lib/galaxy/web/framework/__init__.py Mon May 03 17:35:54 2010 -0400
@@ -66,6 +66,15 @@
decorator.exposed = True
return decorator
+def json_pretty( func ):
+ def decorator( self, trans, *args, **kwargs ):
+ trans.response.set_content_type( "text/javascript" )
+ return simplejson.dumps( func( self, trans, *args, **kwargs ), indent=4, sort_keys=True )
+ if not hasattr(func, '_orig'):
+ decorator._orig = func
+ decorator.exposed = True
+ return decorator
+
def require_login( verb="perform this action", use_panels=False, webapp='galaxy' ):
def argcatcher( func ):
def decorator( self, trans, *args, **kwargs ):
diff -r c75c0f9b0bf7 -r eec050533545 lib/galaxy/workflow/modules.py
--- a/lib/galaxy/workflow/modules.py Mon May 03 16:32:35 2010 -0400
+++ b/lib/galaxy/workflow/modules.py Mon May 03 17:35:54 2010 -0400
@@ -96,7 +96,7 @@
module.state = dict( name="Input Dataset" )
return module
@classmethod
- def from_dict( Class, trans, d ):
+ def from_dict( Class, trans, d, secure=False ):
module = Class( trans )
state = from_json_string( d["tool_state"] )
module.state = dict( name=state.get( "name", "Input Dataset" ) )
@@ -172,11 +172,11 @@
module.state = module.tool.new_state( trans, all_pages=True )
return module
@classmethod
- def from_dict( Class, trans, d ):
+ def from_dict( Class, trans, d, secure=False ):
tool_id = d['tool_id']
module = Class( trans, tool_id )
module.state = DefaultToolState()
- module.state.decode( d["tool_state"], module.tool, module.trans.app )
+ module.state.decode( d["tool_state"], module.tool, module.trans.app, secure=False )
module.errors = d.get( "tool_errors", None )
return module
@@ -199,8 +199,8 @@
return self.tool.name
def get_tool_id( self ):
return self.tool_id
- def get_state( self ):
- return self.state.encode( self.tool, self.trans.app )
+ def get_state( self, secure=True ):
+ return self.state.encode( self.tool, self.trans.app, secure=secure )
def get_errors( self ):
return self.errors
def get_tooltip( self ):
@@ -278,13 +278,13 @@
"""
assert type in self.module_types
return self.module_types[type].new( trans, tool_id )
- def from_dict( self, trans, d ):
+ def from_dict( self, trans, d, **kwargs ):
"""
Return module initialized from the data in dictionary `d`.
"""
type = d['type']
assert type in self.module_types
- return self.module_types[type].from_dict( trans, d )
+ return self.module_types[type].from_dict( trans, d, **kwargs )
def from_workflow_step( self, trans, step ):
"""
Return module initializd from the WorkflowStep object `step`.
diff -r c75c0f9b0bf7 -r eec050533545 templates/form.mako
--- a/templates/form.mako Mon May 03 16:32:35 2010 -0400
+++ b/templates/form.mako Mon May 03 17:35:54 2010 -0400
@@ -60,7 +60,11 @@
</label>
%endif
<div class="form-row-input">
- <input type="${input.type}" name="${input.name}" value="${input.value}" size="40">
+ %if input.type == 'textarea':
+ <textarea name="${input.name}" cols="40">${input.value}</textarea>
+ %else:
+ <input type="${input.type}" name="${input.name}" value="${input.value}" size="40">
+ %endif
</div>
%if input.error:
<div class="form-row-error-message">${input.error}</div>
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/5ebac527fa31
changeset: 3734:5ebac527fa31
user: Nate Coraor <nate(a)bx.psu.edu>
date: Mon May 03 15:33:34 2010 -0400
description:
Community: add help for tool-related operations
diffstat:
lib/galaxy/webapps/community/controllers/tool.py | 6 +
lib/galaxy/webapps/community/security/__init__.py | 2 +-
templates/webapps/community/base_panels.mako | 1 +
templates/webapps/community/tool/help.mako | 125 ++++++++++++++++++++++
templates/webapps/community/tool/view_tool.mako | 8 +-
templates/webapps/community/upload/upload.mako | 8 +-
6 files changed, 139 insertions(+), 11 deletions(-)
diffs (205 lines):
diff -r 8bdbd5fc8721 -r 5ebac527fa31 lib/galaxy/webapps/community/controllers/tool.py
--- a/lib/galaxy/webapps/community/controllers/tool.py Mon May 03 14:03:01 2010 -0400
+++ b/lib/galaxy/webapps/community/controllers/tool.py Mon May 03 15:33:34 2010 -0400
@@ -402,3 +402,9 @@
tool_file = tarfile.open( tool.file_name ).extractfile( tool_file_name )
trans.response.set_content_type( 'text/plain' )
return tool_file
+ @web.expose
+ def help( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ return trans.fill_template( '/webapps/community/tool/help.mako', message=message, status=status, **kwd )
diff -r 8bdbd5fc8721 -r 5ebac527fa31 lib/galaxy/webapps/community/security/__init__.py
--- a/lib/galaxy/webapps/community/security/__init__.py Mon May 03 14:03:01 2010 -0400
+++ b/lib/galaxy/webapps/community/security/__init__.py Mon May 03 15:33:34 2010 -0400
@@ -170,7 +170,7 @@
def can_upload_new_version( self, user, item, versions ):
state_ok = True
for version in versions:
- if version.is_new() or version.is_approved():
+ if version.is_new() or version.is_waiting():
state_ok = False
return user and user==item.user and state_ok
diff -r 8bdbd5fc8721 -r 5ebac527fa31 templates/webapps/community/base_panels.mako
--- a/templates/webapps/community/base_panels.mako Mon May 03 14:03:01 2010 -0400
+++ b/templates/webapps/community/base_panels.mako Mon May 03 15:33:34 2010 -0400
@@ -33,6 +33,7 @@
<a>Help</a>
<div class="submenu">
<ul>
+ <li><a target="galaxy_main" href="${h.url_for( controller='tool', action='help' )}">How to upload, download and install tools</a></li>
<li><a href="${app.config.get( "bugs_email", "mailto:galaxy-bugs@bx.psu.edu" )}">Email comments, bug reports, or suggestions</a></li>
<li><a target="_blank" href="${app.config.get( "wiki_url", "http://bitbucket.org/galaxy/galaxy-central/wiki" )}">Galaxy Wiki</a></li>
<li><a target="_blank" href="${app.config.get( "screencasts_url", "http://galaxycast.org" )}">Video tutorials (screencasts)</a></li>
diff -r 8bdbd5fc8721 -r 5ebac527fa31 templates/webapps/community/tool/help.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/webapps/community/tool/help.mako Mon May 03 15:33:34 2010 -0400
@@ -0,0 +1,125 @@
+<%namespace file="/message.mako" import="render_msg" />
+
+<%!
+ def inherit(context):
+ if context.get('use_panels'):
+ return '/webapps/community/base_panels.mako'
+ else:
+ return '/base.mako'
+%>
+<%inherit file="${inherit(context)}"/>
+
+<%def name="title()">Tool Help</%def>
+
+<h2>Tool Help</h2>
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<h3>Uploading Tools</h3>
+
+<p><strong>Tool File Format</strong></p>
+<p>
+
+ A tool file is a tar-format (bzipped or gzipped tar are valid) archive
+ containing all the files necessary to load the tool in Galaxy. At the very
+ least, it must contain a
+ <a href="http://bitbucket.org/galaxy/galaxy-central/wiki/ToolConfigSyntax" target="_blank">Tool XML File</a>,
+ and will probably also include a tool script. If any steps are necessary
+ to install your tool beyond the basic instructions below, please include a
+ README file which details these steps. If the tool (or parts of it) are
+ written in C, the source code can be included (or put links to the source
+ in the README). Please do not include precompiled binaries without source,
+ since Galaxy is run on a wide variety of platforms. Also, if you are only
+ wrapping or providing a Galaxy config for a tool that is not your own,
+ please be sure the license allows for redistribution before including any
+ part of that tool in the tar archive!
+</p>
+<p>
+ There are no requirements about the directory structure inside the tar
+ archive, but for ease of use, it's generally a good idea to put everything
+ inside of a subdirectory, instead of directly at the top level.
+</p>
+
+<p><strong>Tool File Example</strong></p>
+<p>
+ To package up the LASTZ tool's config file, Galaxy wrapper, and the C source:
+ <pre>
+ user@host:~% tar jcvf ~/Desktop/galaxy_lastz_tool.tar.bz2 lastz
+ lastz/
+ lastz/README
+ lastz/lastz_wrapper.py
+ lastz/lastz_wrapper.xml
+ lastz/lastz-distrib-1.02.00/
+ lastz/lastz-distrib-1.02.00/src/
+ lastz/lastz-distrib-1.02.00/src/Makefile
+ lastz/lastz-distrib-1.02.00/src/version.mak
+ lastz/lastz-distrib-1.02.00/src/lastz.c
+ lastz/lastz-distrib-1.02.00/src/lastz.h
+ ...
+ </pre>
+ <code>~/Desktop/galaxy_lastz_tool.tar.bz2</code> is now ready to be uploaded.
+</p>
+
+<p><strong>Editing Information, Categories, and Submitting For Approval</strong></p>
+
+<p>
+ Simply uploading a tool to the Community will not allow other users to find
+ and download your tool. It will need to be approved by an administrator
+ before it appears in the tool list.
+</p>
+<p>
+ After the tool has successfully uploaded, you will be redirected to the
+ Edit Tool page. Please provide a detailed description of what the tool
+ does - this will be used by administrators to understand the tool before
+ approving it for display on the site. Once approved, this information will
+ be displayed to users who view your tool. In addition, the site
+ administrators will have configured a number of categories with which you
+ can associate your tool to make it easily findable by users looking to
+ solve specific problems. Please associate as many categories as are
+ relevant to your tool. You may change the description and associated
+ categories as often as you'd like until you click the "<strong>Submit for
+ approval</strong>" button. Once submitted, the tool will be approved or
+ rejected by an administrator. Once approved, it will be visible to
+ everyone. At that point, the description and associated categories can
+ only be changed by an administrator.
+</p>
+<p>
+ Once the tool has been approved or rejected, you may upload a new version
+ by browsing to the tool's "View Tool" page, clicking the context menu to
+ the right of the tool's name, and selecting "Upload a new version."
+</p>
+
+<hr/>
+
+<h3>Downloading and Installing Tools</h3>
+
+<p>
+ A tool's download link will send you the tool tar archive. Once
+ downloaded, unpack the tool on your local Galaxy instance's server:
+ <pre>
+ user@host:~% tar xvf galaxy_tool.tar
+ ...
+ user@host:~% tar zxvf galaxy_tool.tar.gz
+ ...
+ user@host:~% tar jxvf galaxy_tool.tar.bz2
+ ...
+ </pre>
+ If the tar archive includes a README file, consult it for installation
+ instructions. If not, follow these basic steps:
+ <ol>
+ <li>Create a directory under <code>galaxy_dist/tools/</code> to house downloaded tool(s).</li>
+ <li>In the new directory, place the XML and any script file(s) which were contained in the tar archive.</li>
+ <li>If the tool includes binaries, you'll need to copy them to a directory on your <code>$PATH</code>. If the tool depends on C binaries but does not come with them (only source), you'll need to compile the source first.</li>
+ <li>Add the tool to <code>galaxy_dist/tool_conf.xml</code>.</li>
+ <li>Restart the Galaxy server process.</li>
+ </ol>
+</p>
+
+<p>
+ We plan to implement a more direct method to install tools via the Galaxy
+ administrator user interface instead of placing files on the filesystem and
+ managing the <code>tool_conf.xml</code> file by hand. In the meantime,
+ this is the process.
+</p>
diff -r 8bdbd5fc8721 -r 5ebac527fa31 templates/webapps/community/tool/view_tool.mako
--- a/templates/webapps/community/tool/view_tool.mako Mon May 03 14:03:01 2010 -0400
+++ b/templates/webapps/community/tool/view_tool.mako Mon May 03 15:33:34 2010 -0400
@@ -125,9 +125,11 @@
<div class="form-row">
<label>Categories:</label>
%if categories:
- %for category in categories:
- ${category.name}
- %endfor
+ <ul>
+ %for category in categories:
+ <li>${category.name}</li>
+ %endfor
+ </ul>
%else:
none set
%endif
diff -r 8bdbd5fc8721 -r 5ebac527fa31 templates/webapps/community/upload/upload.mako
--- a/templates/webapps/community/upload/upload.mako Mon May 03 14:03:01 2010 -0400
+++ b/templates/webapps/community/upload/upload.mako Mon May 03 15:33:34 2010 -0400
@@ -39,7 +39,7 @@
</select>
</div>
<div class="toolParamHelp" style="clear: both;">
- Need help creating a tool file? See help below.
+ Need help creating a tool file? See the <a href="${h.url_for( controller='tool', action='help' )}">Tool Help</a> page.
</div>
<div style="clear: both"></div>
</div>
@@ -77,9 +77,3 @@
</form>
</div>
</div>
-<div class="toolHelp">
- <div class="toolHelpBody">
- <p><strong>Creating tool files</strong></p>
- <p>Help coming...</p>
- </div>
-</div>
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/c75c0f9b0bf7
changeset: 3735:c75c0f9b0bf7
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Mon May 03 16:32:35 2010 -0400
description:
Enable users not logged in to view a shared/accessible/published visualization.
diffstat:
lib/galaxy/web/base/controller.py | 3 ++-
lib/galaxy/web/controllers/tracks.py | 21 ++++++++++++++-------
lib/galaxy/web/framework/__init__.py | 5 +++++
templates/tracks/browser.mako | 2 +-
templates/visualization/display.mako | 2 +-
5 files changed, 23 insertions(+), 10 deletions(-)
diffs (105 lines):
diff -r 5ebac527fa31 -r c75c0f9b0bf7 lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py Mon May 03 15:33:34 2010 -0400
+++ b/lib/galaxy/web/base/controller.py Mon May 03 16:32:35 2010 -0400
@@ -208,7 +208,8 @@
if dbkey is None: dbkey = dataset.dbkey # Hack for backward compat
## TODO: chrom needs to be able to be set; right now it's empty.
- config = { "title": visualization.title, "vis_id": id, "tracks": tracks, "chrom": "", "dbkey": dbkey }
+ config = { "title": visualization.title, "vis_id": trans.security.encode_id( visualization.id ),
+ "tracks": tracks, "chrom": "", "dbkey": dbkey }
return config
diff -r 5ebac527fa31 -r c75c0f9b0bf7 lib/galaxy/web/controllers/tracks.py
--- a/lib/galaxy/web/controllers/tracks.py Mon May 03 15:33:34 2010 -0400
+++ b/lib/galaxy/web/controllers/tracks.py Mon May 03 16:32:35 2010 -0400
@@ -72,7 +72,7 @@
.filter( model.HistoryDatasetAssociation.extension.in_(self.available_tracks) ) \
.filter( model.Dataset.state != "error")
-class TracksController( BaseController ):
+class TracksController( BaseController, UsesVisualization ):
"""
Controller for track browser interface. Handles building a new browser from
datasets in the current history, and display of the resulting browser.
@@ -158,8 +158,7 @@
return trans.fill_template( 'tracks/browser.mako', config=config )
@web.json
- @web.require_login()
- def chroms(self, trans, dbkey=None ):
+ def chroms(self, trans, vis_id=None ):
"""
Returns a naturally sorted list of chroms/contigs for the given dbkey
"""
@@ -171,18 +170,26 @@
def split_by_number(s):
return [ check_int(c) for c in re.split('([0-9]+)', s) ]
-
- chroms = self._chroms( trans, dbkey )
+
+ # Get visualization and config.
+ visualization = self.get_visualization( trans, vis_id, check_ownership=False, check_accessible=True )
+ visualization.config = self.get_visualization_config( trans, visualization )
+ if visualization is None:
+ raise web.httpexceptions.HTTPNotFound()
+
+ # Get chroms data.
+ chroms = self._chroms( trans, visualization )
to_sort = [{ 'chrom': chrom, 'len': length } for chrom, length in chroms.iteritems()]
to_sort.sort(lambda a,b: cmp( split_by_number(a['chrom']), split_by_number(b['chrom']) ))
return to_sort
- def _chroms( self, trans, dbkey ):
+ def _chroms( self, trans, visualization ):
"""
Called by the browser to get a list of valid chromosomes and lengths
"""
# If there is any dataset in the history of extension `len`, this will use it
- user = trans.get_user()
+ user = visualization.user
+ dbkey = visualization.config['dbkey']
if 'dbkeys' in user.preferences:
user_keys = from_json_string( user.preferences['dbkeys'] )
if dbkey in user_keys:
diff -r 5ebac527fa31 -r c75c0f9b0bf7 lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py Mon May 03 15:33:34 2010 -0400
+++ b/lib/galaxy/web/framework/__init__.py Mon May 03 16:32:35 2010 -0400
@@ -675,6 +675,11 @@
"""
Returns the db_file dataset associated/needed by `dataset`, or `None`.
"""
+
+ # If no history, return None.
+ if self.history is None:
+ return None
+
datasets = self.sa_session.query( self.app.model.HistoryDatasetAssociation ) \
.filter_by( deleted=False, history_id=self.history.id, extension="len" )
for ds in datasets:
diff -r 5ebac527fa31 -r c75c0f9b0bf7 templates/tracks/browser.mako
--- a/templates/tracks/browser.mako Mon May 03 15:33:34 2010 -0400
+++ b/templates/tracks/browser.mako Mon May 03 16:32:35 2010 -0400
@@ -272,7 +272,7 @@
$.ajax({
url: "${h.url_for( action='chroms' )}",
- data: { dbkey: view.dbkey },
+ data: { vis_id: view.vis_id },
dataType: "json",
success: function ( data ) {
view.chrom_data = data;
diff -r 5ebac527fa31 -r c75c0f9b0bf7 templates/visualization/display.mako
--- a/templates/visualization/display.mako Mon May 03 15:33:34 2010 -0400
+++ b/templates/visualization/display.mako Mon May 03 16:32:35 2010 -0400
@@ -124,7 +124,7 @@
$.ajax({
## JG: added controller name
url: "${h.url_for( controller='/tracks', action='chroms' )}",
- data: { dbkey: view.dbkey },
+ data: { vis_id: view.vis_id },
dataType: "json",
success: function ( data ) {
view.chrom_data = data;
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/8bdbd5fc8721
changeset: 3733:8bdbd5fc8721
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Mon May 03 14:03:01 2010 -0400
description:
Pack scripts.
diffstat:
static/scripts/packed/trackster.js | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diffs (8 lines):
diff -r 2cf1ad5edaa6 -r 8bdbd5fc8721 static/scripts/packed/trackster.js
--- a/static/scripts/packed/trackster.js Mon May 03 14:02:08 2010 -0400
+++ b/static/scripts/packed/trackster.js Mon May 03 14:03:01 2010 -0400
@@ -1,1 +1,1 @@
-var DEBUG=false;var DENSITY=200,FEATURE_LEVELS=10,DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="../images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="../images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="../images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,"!
repeat")};var left_img_inv=new Image();left_img_inv.src="../images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var Drawer=function(){};$.extend(Drawer.prototype,{intensity:function(b,a,c){},});drawer=new Drawer();var View=function(b,d,c,a){this.vis_id=c;this.dbkey=a;this.title=d;this.chrom=b;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_hi!
gh=0;this.center=(this.max_high-this.max_low)/2;this.zoom_factor=3;thi
s.zoom_level=0;this.track_id_counter=0};$.extend(View.prototype,{add_track:function(a){a.view=this;a.track_id=this.track_id_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[a]},update_options:function(){var b=$("ul#sortable-ul").sortable("toArray");var d=[];var c=$("#viewport > div").sort(function(g,f){return b.indexOf($(g).attr("id"))>b.indexOf($(f).attr("id"))});$("#viewport > div").remove();$("#viewport").html(c);for(var e in view.tracks){var a=view.tracks[e];if(a.update_options){a.update_options(e)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.center=this.center=(this.max_high-this.max_low)/2;this.zoom_level=0;$(".yaxislabel").remove()},redraw:function(f){this.span=this.max_high-this.max_low;var d=this.span!
/Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){e=this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(a,b){if(this.max_high===0||this.high-this.low<30){return}if(a){this.center=a/b.width()*(this.hi!
gh-this.low)+this.low}this.zoom_level+=1;this.redraw()},zoom_out:funct
ion(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1;this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div></div>").addClass("track").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.content_div.css("height","30px");if(!a.content_div.text()){a.content_div.text(DATA_LOADING)}a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NO!
CONVERTER)}else{if(d.data&&d.data.length===0||d.data===null){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,e=this.view.high,f=e-i,d=this.view.resolution;if(DEBUG){$("#debug").text(d+" "+this.view.zoom_res)}var k=$("<div style='position: relative;'></div>"),l=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(k),this.max_height=0;var a=Math.floor(i/d/DENSITY);while((a*DENSITY*d)<e){var j=this.content_div.width()+"_"+this.view.zoom_level+"_"+a;var c=this.tile_cache.get(j);if(c){var g=a*DENSITY*d;!
var b=(g-i)*l;if(this.left_offset){b-=this.left_offset}c.css({left:b})
;k.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height+"px")}else{this.delayed_draw(this,j,i,e,a,d,k,l)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)}});var LabelTrack=function(a){Track.call(this,null,a);this.track_type="LabelTrack";this.hidden=true;this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute!
",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var LineTrack=function(c,a,b){this.track_type="LineTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("line-track");this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={min_value:undefined,max_value:undefined,mode:"Line"};if(b.min_value!==undefined){this.prefs.min_value=b.min_value}if(b.max_value!==undefined){this.prefs.max_value=b.max_value}if(b.mode!==undefined){this.prefs.mode=b.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("!
#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").
val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var d=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,q,c,e){if(this.vert!
ical_range===undefined){return}var r=q*DENSITY*o,a=DENSITY*o,b=$("<canvas class='tile'></canvas>"),u=o+"_"+q;if(this.data_cache.get(u)===undefined){this.get_data(o,q);return}var t=this.data_cache.get(u);if(t===null){return}b.css({position:"absolute",top:0,left:(r-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,s=this.total_frequency,d=this.height_px;n.beginPath();if(t.length>1){var f=Math.ceil((t[1][0]-t[0][0])*e)}else{var f=10}for(var p=0;p<t.length;p++){var j=t[p][0]-r;var h=t[p][1];if(this.prefs.mode=="Intensity"){if(h===null){continue}j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(j,0,f,this.height_px)}else{if(h===null){k=false;continue}else{j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/m*d);if(k){n.lineTo(j,h)}else{n.moveTo(j,h);k=true}}}}n.stroke();c!
.append(b);return b},gen_options:function(n){var a=$("<div></div>").ad
dClass("form-row");var h="track_"+n+"_minval",k="track_"+n+"_maxval",e="track_"+n+"_mode",l=$("<label></label>").attr("for",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),m=$("<input></input>").attr("id",h).val(b),g=$("<label></label>").attr("for",k).text("Max value:"),j=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",k).val(j),d=$("<label></label>").attr("for",e).text("Display mode:"),i=(this.prefs.mode===undefined?"Line":this.prefs.mode),c=$('<select id="'+e+'"><option value="Line" id="mode_Line">Line</option><option value="Intensity" id="mode_Intensity">Intensity</option></select>');c.children("#mode_"+i).attr("selected","selected");return a.append(l).append(m).append(g).append(f).append(d).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_mode option:selected").val();if(a!==this.prefs.min_value||c!==this.prefs.max_val!
ue||b!=this.prefs.mode){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.mode=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(c,a,b){this.track_type="FeatureTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=3;this.default_font="9px Monaco, Lucida Console, monospace";this.left_offset=200;this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.prefs={block_color:"black",label_color:"black",show_counts:false};if(b.block_color!==undefined){this.prefs.block_!
color=b.block_color}if(b.label_color!==undefined){this.prefs.label_col
or=b.label_color}if(b.show_counts!==undefined){this.prefs.show_counts=b.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution},function(c){a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.resolution},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,c){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,v=[],h=0,b=$("<canvas></canvas>").get(0).getContext("2d"),n=this.view.max_low;var d,f,x=[];for(var s=0,t=g.length;s<t;s++){var e=g[s],l=e[0];if(this.inc_slots[a][l]!==undefined){h!
=Math.max(h,this.inc_slots[a][l]);x.push(this.inc_slots[a][l])}else{v.push(s)}}for(var s=0,t=v.length;s<t;s++){var e=g[v[s]];l=e[0],feature_start=e[1],feature_end=e[2],feature_name=e[3];d=Math.floor((feature_start-n)*m);f=Math.ceil((feature_end-n)*m);if(!c){var p=b.measureText(feature_name).width;if(d-p<0){f+=p}else{d-=p}}var r=0;while(true){var o=true;if(this.s_e_by_tile[a][r]!==undefined){for(var q=0,w=this.s_e_by_tile[a][r].length;q<w;q++){var u=this.s_e_by_tile[a][r][q];if(f>u[0]&&d<u[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][r]===undefined){this.s_e_by_tile[a][r]=[]}this.s_e_by_tile[a][r].push([d,f]);this.inc_slots[a][l]=r;h=Math.max(h,r);break}r++}}return h},rect_or_text:function(n,o,g,f,m,b,d,k,e,i){n.textAlign="center";var j=Math.round(o/2);if(d!==undefined&&o>g){n.fillStyle="#555";n.fillRect(k,i+1,e,9);n.fillStyle="#eee";for(var h=0,l=d.length;h<l;h++){if(b+h>=f&&b+h<=m){var a=Math.floor(Math.max(0,(b+h-f)*o));n.fillText(d[h],a+this.left_offset+j,i+9)}}}else!
{n.fillStyle="#555";n.fillRect(k,i+4,e,3)}},draw_tile:function(W,h,m,a
k){var D=h*DENSITY*W,ac=(h+1)*DENSITY*W,C=DENSITY*W;var aj,r;var ad=D+"_"+ac;var w=this.data_cache.get(ad);if(w===undefined){this.data_queue[[D,ac]]=true;this.get_data(D,ac);return}if(w.dataset_type==="summary_tree"){r=30}else{var U=(w.extra_info==="no_detail");var al=(U?this.vertical_nodetail_px:this.vertical_detail_px);r=this.incremental_slots(this.view.zoom_res,w.data,U)*al+15;m.parent().css("height",Math.max(this.height_px,r)+"px");aj=this.inc_slots[this.view.zoom_res]}var a=Math.ceil(C*ak),K=$("<canvas class='tile'></canvas>"),Y=this.prefs.label_color,f=this.prefs.block_color,O=this.left_offset;K.css({position:"absolute",top:0,left:(D-this.view.low)*ak-O});K.get(0).width=a+O;K.get(0).height=r;var z=K.get(0).getContext("2d"),ah=z.measureText("A").width;z.fillStyle=this.prefs.block_color;z.font=this.default_font;z.textAlign="right";if(w.dataset_type=="summary_tree"){var J,G=55,ab=255-G,g=ab*2/3,Q=w.data,B=w.max,l=w.avg;if(Q.length>2){var b=Math.ceil((Q[1][0]-Q[0][0])*ak)}!
else{var b=50}for(var af=0,v=Q.length;af<v;af++){var S=Math.ceil((Q[af][0]-D)*ak);var R=Q[af][1];if(!R){continue}J=Math.floor(ab-(R/B)*ab);z.fillStyle="rgb("+J+","+J+","+J+")";z.fillRect(S+O,0,b,20);if(this.prefs.show_counts){if(J>g){z.fillStyle="black"}else{z.fillStyle="#ddd"}z.textAlign="center";z.fillText(Q[af][1],S+O+(b/2),12)}}m.append(K);return K}var ai=w.data;var ae=0;for(var af=0,v=ai.length;af<v;af++){var L=ai[af],I=L[0],ag=L[1],T=L[2],E=L[3];if(ag<=ac&&T>=D){var V=Math.floor(Math.max(0,(ag-D)*ak)),A=Math.ceil(Math.min(a,Math.max(0,(T-D)*ak))),P=aj[I]*al;if(w.dataset_type==="bai"){z.fillStyle="#555";if(L[4] instanceof Array){var s=Math.floor(Math.max(0,(L[4][0]-D)*ak)),H=Math.ceil(Math.min(a,Math.max(0,(L[4][1]-D)*ak))),q=Math.floor(Math.max(0,(L[5][0]-D)*ak)),o=Math.ceil(Math.min(a,Math.max(0,(L[5][1]-D)*ak)));if(L[4][1]>=D&&L[4][0]<=ac){this.rect_or_text(z,ak,ah,D,ac,L[4][0],L[4][2],s+O,H-s,P)}if(L[5][1]>=D&&L[5][0]<=ac){this.rect_or_text(z,ak,ah,D,ac,L[5][0],L[5!
][2],q+O,o-q,P)}if(q>H){z.fillStyle="#999";z.fillRect(H+O,P+5,q-H,1)}}
else{z.fillStyle="#555";this.rect_or_text(z,ak,ah,D,ac,ag,E,V+O,A-V,P)}if(!U&&ag>D){z.fillStyle=this.prefs.label_color;if(h===0&&V-z.measureText(E).width<0){z.textAlign="left";z.fillText(I,A+2+O,P+8)}else{z.textAlign="right";z.fillText(I,V-2+O,P+8)}z.fillStyle="#555"}}else{if(w.dataset_type==="interval_index"){if(U){z.fillRect(V+O,P+5,A-V,1)}else{var u=L[4],N=L[5],X=L[6],e=L[7];var t,Z,F=null,am=null;if(N&&X){F=Math.floor(Math.max(0,(N-D)*ak));am=Math.ceil(Math.min(a,Math.max(0,(X-D)*ak)))}if(E!==undefined&&ag>D){z.fillStyle=Y;if(h===0&&V-z.measureText(E).width<0){z.textAlign="left";z.fillText(E,A+2+O,P+8)}else{z.textAlign="right";z.fillText(E,V-2+O,P+8)}z.fillStyle=f}if(e){if(u){if(u=="+"){z.fillStyle=RIGHT_STRAND}else{if(u=="-"){z.fillStyle=LEFT_STRAND}}z.fillRect(V+O,P,A-V,10);z.fillStyle=f}for(var ad=0,d=e.length;ad<d;ad++){var n=e[ad],c=Math.floor(Math.max(0,(n[0]-D)*ak)),M=Math.ceil(Math.min(a,Math.max((n[1]-D)*ak)));if(c>M){continue}t=5;Z=3;z.fillRect(c+O,P+Z,M-c,t);i!
f(F!==undefined&&!(c>am||M<F)){t=9;Z=1;var aa=Math.max(c,F),p=Math.min(M,am);z.fillRect(aa+O,P+Z,p-aa,t)}}}else{t=9;Z=1;z.fillRect(V+O,P+Z,A-V,t);if(L.strand){if(L.strand=="+"){z.fillStyle=RIGHT_STRAND_INV}else{if(L.strand=="-"){z.fillStyle=LEFT_STRAND_INV}}z.fillRect(V+O,P,A-V,10);z.fillStyle=prefs.block_color}}}}}ae++}}m.append(K);return K},gen_options:function(i){var a=$("<div></div>").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label></label>").attr("for",e).text("Block color:"),l=$("<input></input>").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label></label>").attr("for",j).text("Text color:"),h=$("<input></input>").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label></label>").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div></div>").appen!
d(b).append(c);return a.append(k).append(l).append(g).append(h).append
(d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!=this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(c,a,b){FeatureTrack.call(this,c,a,b);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
\ No newline at end of file
+var DEBUG=false;var DENSITY=200,FEATURE_LEVELS=10,DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="/static/images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="/static/images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="/static/images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(!
right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src="/static/images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var Drawer=function(){};$.extend(Drawer.prototype,{intensity:function(b,a,c){},});drawer=new Drawer();var View=function(b,d,c,a){this.vis_id=c;this.dbkey=a;this.title=d;this.chrom=b;this.tracks=[];this.label_tracks=[];this.m!
ax_low=0;this.max_high=0;this.center=(this.max_high-this.max_low)/2;th
is.zoom_factor=3;this.zoom_level=0;this.track_id_counter=0};$.extend(View.prototype,{add_track:function(a){a.view=this;a.track_id=this.track_id_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[a]},update_options:function(){var b=$("ul#sortable-ul").sortable("toArray");var d=[];var c=$("#viewport > div").sort(function(g,f){return b.indexOf($(g).attr("id"))>b.indexOf($(f).attr("id"))});$("#viewport > div").remove();$("#viewport").html(c);for(var e in view.tracks){var a=view.tracks[e];if(a.update_options){a.update_options(e)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.center=this.center=(this.max_high-this.max_low)/2;this.zoom_level=0;$(".yaxislabel").remove()},redraw:function(f){this.span=this.max_high-this.max!
_low;var d=this.span/Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){e=this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(a,b){if(this.max_high===0||this.high-this.low<30){return}if(a){this.center=!
a/b.width()*(this.high-this.low)+this.low}this.zoom_level+=1;this.redr
aw()},zoom_out:function(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1;this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div></div>").addClass("track").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.content_div.css("height","30px");if(!a.content_div.text()){a.content_div.text(DATA_LOADING)}a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(d==="no converter"){a.container_div.addClass("error");a.cont!
ent_div.text(DATA_NOCONVERTER)}else{if(d.data&&d.data.length===0||d.data===null){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,e=this.view.high,f=e-i,d=this.view.resolution;if(DEBUG){$("#debug").text(d+" "+this.view.zoom_res)}var k=$("<div style='position: relative;'></div>"),l=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(k),this.max_height=0;var a=Math.floor(i/d/DENSITY);while((a*DENSITY*d)<e){var j=this.content_div.width()+"_"+this.view.zoom_level+"_"+a;var c=this.tile_cache.get(j);if(c!
){var g=a*DENSITY*d;var b=(g-i)*l;if(this.left_offset){b-=this.left_of
fset}c.css({left:b});k.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height+"px")}else{this.delayed_draw(this,j,i,e,a,d,k,l)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)}});var LabelTrack=function(a){Track.call(this,null,a);this.track_type="LabelTrack";this.hidden=true;this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css!
({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var LineTrack=function(c,a,b){this.track_type="LineTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("line-track");this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={min_value:undefined,max_value:undefined,mode:"Line"};if(b.min_value!==undefined){this.prefs.min_value=b.min_value}if(b.max_value!==undefined){this.prefs.max_value=b.max_value}if(b.mode!==undefined){this.prefs.mode=b.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.ma!
x_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#t
rack_"+b+"_maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var d=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o!
,q,c,e){if(this.vertical_range===undefined){return}var r=q*DENSITY*o,a=DENSITY*o,b=$("<canvas class='tile'></canvas>"),u=o+"_"+q;if(this.data_cache.get(u)===undefined){this.get_data(o,q);return}var t=this.data_cache.get(u);if(t===null){return}b.css({position:"absolute",top:0,left:(r-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,s=this.total_frequency,d=this.height_px;n.beginPath();if(t.length>1){var f=Math.ceil((t[1][0]-t[0][0])*e)}else{var f=10}for(var p=0;p<t.length;p++){var j=t[p][0]-r;var h=t[p][1];if(this.prefs.mode=="Intensity"){if(h===null){continue}j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(j,0,f,this.height_px)}else{if(h===null){k=false;continue}else{j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/m*d);if(k){n.lineTo(j,h)}else{n.moveTo(j,h);k=!
true}}}}n.stroke();c.append(b);return b},gen_options:function(n){var a
=$("<div></div>").addClass("form-row");var h="track_"+n+"_minval",k="track_"+n+"_maxval",e="track_"+n+"_mode",l=$("<label></label>").attr("for",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),m=$("<input></input>").attr("id",h).val(b),g=$("<label></label>").attr("for",k).text("Max value:"),j=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",k).val(j),d=$("<label></label>").attr("for",e).text("Display mode:"),i=(this.prefs.mode===undefined?"Line":this.prefs.mode),c=$('<select id="'+e+'"><option value="Line" id="mode_Line">Line</option><option value="Intensity" id="mode_Intensity">Intensity</option></select>');c.children("#mode_"+i).attr("selected","selected");return a.append(l).append(m).append(g).append(f).append(d).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_mode option:selected").val();if(a!==this.prefs.min_value||c!!
==this.prefs.max_value||b!=this.prefs.mode){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.mode=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(c,a,b){this.track_type="FeatureTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=3;this.default_font="9px Monaco, Lucida Console, monospace";this.left_offset=200;this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.prefs={block_color:"black",label_color:"black",show_counts:false};if(b.block_color!==undefine!
d){this.prefs.block_color=b.block_color}if(b.label_color!==undefined){
this.prefs.label_color=b.label_color}if(b.show_counts!==undefined){this.prefs.show_counts=b.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution},function(c){a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.resolution},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,c){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,v=[],h=0,b=$("<canvas></canvas>").get(0).getContext("2d"),n=this.view.max_low;var d,f,x=[];for(var s=0,t=g.length;s<t;s++){var e=g[s],l=e[0];if(this.inc_slots[!
a][l]!==undefined){h=Math.max(h,this.inc_slots[a][l]);x.push(this.inc_slots[a][l])}else{v.push(s)}}for(var s=0,t=v.length;s<t;s++){var e=g[v[s]];l=e[0],feature_start=e[1],feature_end=e[2],feature_name=e[3];d=Math.floor((feature_start-n)*m);f=Math.ceil((feature_end-n)*m);if(!c){var p=b.measureText(feature_name).width;if(d-p<0){f+=p}else{d-=p}}var r=0;while(true){var o=true;if(this.s_e_by_tile[a][r]!==undefined){for(var q=0,w=this.s_e_by_tile[a][r].length;q<w;q++){var u=this.s_e_by_tile[a][r][q];if(f>u[0]&&d<u[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][r]===undefined){this.s_e_by_tile[a][r]=[]}this.s_e_by_tile[a][r].push([d,f]);this.inc_slots[a][l]=r;h=Math.max(h,r);break}r++}}return h},rect_or_text:function(n,o,g,f,m,b,d,k,e,i){n.textAlign="center";var j=Math.round(o/2);if(d!==undefined&&o>g){n.fillStyle="#555";n.fillRect(k,i+1,e,9);n.fillStyle="#eee";for(var h=0,l=d.length;h<l;h++){if(b+h>=f&&b+h<=m){var a=Math.floor(Math.max(0,(b+h-f)*o));n.fillText(d[h],a+this.left_!
offset+j,i+9)}}}else{n.fillStyle="#555";n.fillRect(k,i+4,e,3)}},draw_t
ile:function(W,h,m,ak){var D=h*DENSITY*W,ac=(h+1)*DENSITY*W,C=DENSITY*W;var aj,r;var ad=D+"_"+ac;var w=this.data_cache.get(ad);if(w===undefined){this.data_queue[[D,ac]]=true;this.get_data(D,ac);return}if(w.dataset_type==="summary_tree"){r=30}else{var U=(w.extra_info==="no_detail");var al=(U?this.vertical_nodetail_px:this.vertical_detail_px);r=this.incremental_slots(this.view.zoom_res,w.data,U)*al+15;m.parent().css("height",Math.max(this.height_px,r)+"px");aj=this.inc_slots[this.view.zoom_res]}var a=Math.ceil(C*ak),K=$("<canvas class='tile'></canvas>"),Y=this.prefs.label_color,f=this.prefs.block_color,O=this.left_offset;K.css({position:"absolute",top:0,left:(D-this.view.low)*ak-O});K.get(0).width=a+O;K.get(0).height=r;var z=K.get(0).getContext("2d"),ah=z.measureText("A").width;z.fillStyle=this.prefs.block_color;z.font=this.default_font;z.textAlign="right";if(w.dataset_type=="summary_tree"){var J,G=55,ab=255-G,g=ab*2/3,Q=w.data,B=w.max,l=w.avg;if(Q.length>2){var b=Math.ceil((Q!
[1][0]-Q[0][0])*ak)}else{var b=50}for(var af=0,v=Q.length;af<v;af++){var S=Math.ceil((Q[af][0]-D)*ak);var R=Q[af][1];if(!R){continue}J=Math.floor(ab-(R/B)*ab);z.fillStyle="rgb("+J+","+J+","+J+")";z.fillRect(S+O,0,b,20);if(this.prefs.show_counts){if(J>g){z.fillStyle="black"}else{z.fillStyle="#ddd"}z.textAlign="center";z.fillText(Q[af][1],S+O+(b/2),12)}}m.append(K);return K}var ai=w.data;var ae=0;for(var af=0,v=ai.length;af<v;af++){var L=ai[af],I=L[0],ag=L[1],T=L[2],E=L[3];if(ag<=ac&&T>=D){var V=Math.floor(Math.max(0,(ag-D)*ak)),A=Math.ceil(Math.min(a,Math.max(0,(T-D)*ak))),P=aj[I]*al;if(w.dataset_type==="bai"){z.fillStyle="#555";if(L[4] instanceof Array){var s=Math.floor(Math.max(0,(L[4][0]-D)*ak)),H=Math.ceil(Math.min(a,Math.max(0,(L[4][1]-D)*ak))),q=Math.floor(Math.max(0,(L[5][0]-D)*ak)),o=Math.ceil(Math.min(a,Math.max(0,(L[5][1]-D)*ak)));if(L[4][1]>=D&&L[4][0]<=ac){this.rect_or_text(z,ak,ah,D,ac,L[4][0],L[4][2],s+O,H-s,P)}if(L[5][1]>=D&&L[5][0]<=ac){this.rect_or_text(z,ak!
,ah,D,ac,L[5][0],L[5][2],q+O,o-q,P)}if(q>H){z.fillStyle="#999";z.fillR
ect(H+O,P+5,q-H,1)}}else{z.fillStyle="#555";this.rect_or_text(z,ak,ah,D,ac,ag,E,V+O,A-V,P)}if(!U&&ag>D){z.fillStyle=this.prefs.label_color;if(h===0&&V-z.measureText(E).width<0){z.textAlign="left";z.fillText(I,A+2+O,P+8)}else{z.textAlign="right";z.fillText(I,V-2+O,P+8)}z.fillStyle="#555"}}else{if(w.dataset_type==="interval_index"){if(U){z.fillRect(V+O,P+5,A-V,1)}else{var u=L[4],N=L[5],X=L[6],e=L[7];var t,Z,F=null,am=null;if(N&&X){F=Math.floor(Math.max(0,(N-D)*ak));am=Math.ceil(Math.min(a,Math.max(0,(X-D)*ak)))}if(E!==undefined&&ag>D){z.fillStyle=Y;if(h===0&&V-z.measureText(E).width<0){z.textAlign="left";z.fillText(E,A+2+O,P+8)}else{z.textAlign="right";z.fillText(E,V-2+O,P+8)}z.fillStyle=f}if(e){if(u){if(u=="+"){z.fillStyle=RIGHT_STRAND}else{if(u=="-"){z.fillStyle=LEFT_STRAND}}z.fillRect(V+O,P,A-V,10);z.fillStyle=f}for(var ad=0,d=e.length;ad<d;ad++){var n=e[ad],c=Math.floor(Math.max(0,(n[0]-D)*ak)),M=Math.ceil(Math.min(a,Math.max((n[1]-D)*ak)));if(c>M){continue}t=5;Z=3;z.fillR!
ect(c+O,P+Z,M-c,t);if(F!==undefined&&!(c>am||M<F)){t=9;Z=1;var aa=Math.max(c,F),p=Math.min(M,am);z.fillRect(aa+O,P+Z,p-aa,t)}}}else{t=9;Z=1;z.fillRect(V+O,P+Z,A-V,t);if(L.strand){if(L.strand=="+"){z.fillStyle=RIGHT_STRAND_INV}else{if(L.strand=="-"){z.fillStyle=LEFT_STRAND_INV}}z.fillRect(V+O,P,A-V,10);z.fillStyle=prefs.block_color}}}}}ae++}}m.append(K);return K},gen_options:function(i){var a=$("<div></div>").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label></label>").attr("for",e).text("Block color:"),l=$("<input></input>").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label></label>").attr("for",j).text("Text color:"),h=$("<input></input>").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label></label>").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$(!
"<div></div>").append(b).append(c);return a.append(k).append(l).append
(g).append(h).append(d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!=this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(c,a,b){FeatureTrack.call(this,c,a,b);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
\ No newline at end of file
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/2cf1ad5edaa6
changeset: 3732:2cf1ad5edaa6
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Mon May 03 14:02:08 2010 -0400
description:
Enable viewing of shared/accessible/published visualizations.
diffstat:
lib/galaxy/web/base/controller.py | 38 ++++
lib/galaxy/web/controllers/visualization.py | 8 +-
static/scripts/trackster.js | 8 +-
templates/visualization/display.mako | 219 +++++++++++++++++++++++++++-
4 files changed, 263 insertions(+), 10 deletions(-)
diffs (349 lines):
diff -r 5f93447d642d -r 2cf1ad5edaa6 lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py Mon May 03 09:42:51 2010 -0400
+++ b/lib/galaxy/web/base/controller.py Mon May 03 14:02:08 2010 -0400
@@ -7,6 +7,7 @@
from galaxy.web import error, form, url_for
from galaxy.model.orm import *
from galaxy.workflow.modules import *
+from galaxy.web.framework import simplejson
from Cheetah.Template import Template
@@ -164,6 +165,7 @@
class UsesVisualization( SharableItemSecurity ):
""" Mixin for controllers that use Visualization objects. """
+
def get_visualization( self, trans, id, check_ownership=True, check_accessible=False ):
""" Get a Visualization from the database by id, verifying ownership. """
# Load workflow from database
@@ -173,6 +175,42 @@
error( "Visualization not found" )
else:
return self.security_check( trans.get_user(), visualization, check_ownership, check_accessible )
+
+ def get_visualization_config( self, trans, visualization ):
+ """ Returns a visualization's configuration. Only works for trackster visualizations right now. """
+
+ config = None
+ if visualization.type == 'trackster':
+ # Trackster config; taken from tracks/browser
+ latest_revision = visualization.latest_revision
+ tracks = []
+
+ try:
+ dbkey = latest_revision.config['dbkey']
+ except KeyError:
+ dbkey = None
+
+ hda_query = trans.sa_session.query( trans.model.HistoryDatasetAssociation )
+ for t in visualization.latest_revision.config['tracks']:
+ dataset_id = t['dataset_id']
+ try:
+ prefs = t['prefs']
+ except KeyError:
+ prefs = {}
+ dataset = hda_query.get( dataset_id )
+ track_type, _ = dataset.datatype.get_track_type()
+ tracks.append( {
+ "track_type": track_type,
+ "name": dataset.name,
+ "dataset_id": dataset.id,
+ "prefs": simplejson.dumps(prefs),
+ } )
+ if dbkey is None: dbkey = dataset.dbkey # Hack for backward compat
+
+ ## TODO: chrom needs to be able to be set; right now it's empty.
+ config = { "title": visualization.title, "vis_id": id, "tracks": tracks, "chrom": "", "dbkey": dbkey }
+
+ return config
class UsesStoredWorkflow( SharableItemSecurity ):
""" Mixin for controllers that use StoredWorkflow objects. """
diff -r 5f93447d642d -r 2cf1ad5edaa6 lib/galaxy/web/controllers/visualization.py
--- a/lib/galaxy/web/controllers/visualization.py Mon May 03 09:42:51 2010 -0400
+++ b/lib/galaxy/web/controllers/visualization.py Mon May 03 14:02:08 2010 -0400
@@ -256,9 +256,13 @@
visualization = trans.sa_session.query( model.Visualization ).filter_by( user=user, slug=slug, deleted=False ).first()
if visualization is None:
raise web.httpexceptions.HTTPNotFound()
+
# Security check raises error if user cannot access visualization.
- self.security_check( trans.get_user(), visualization, False, True)
- return trans.fill_template_mako( "visualization/display.mako", item=visualization, item_data=None, content_only=True )
+ self.security_check( trans.get_user(), visualization, False, True)
+
+ # Display.
+ visualization_config = self.get_visualization_config( trans, visualization )
+ return trans.fill_template_mako( "visualization/display.mako", item=visualization, item_data=visualization_config, content_only=True )
@web.expose
@web.json
diff -r 5f93447d642d -r 2cf1ad5edaa6 static/scripts/trackster.js
--- a/static/scripts/trackster.js Mon May 03 09:42:51 2010 -0400
+++ b/static/scripts/trackster.js Mon May 03 14:02:08 2010 -0400
@@ -17,22 +17,22 @@
RIGHT_STRAND, LEFT_STRAND;
var right_img = new Image();
-right_img.src = "../images/visualization/strand_right.png";
+right_img.src = "/static/images/visualization/strand_right.png";
right_img.onload = function() {
RIGHT_STRAND = CONTEXT.createPattern(right_img, "repeat");
};
var left_img = new Image();
-left_img.src = "../images/visualization/strand_left.png";
+left_img.src = "/static/images/visualization/strand_left.png";
left_img.onload = function() {
LEFT_STRAND = CONTEXT.createPattern(left_img, "repeat");
};
var right_img_inv = new Image();
-right_img_inv.src = "../images/visualization/strand_right_inv.png";
+right_img_inv.src = "/static/images/visualization/strand_right_inv.png";
right_img_inv.onload = function() {
RIGHT_STRAND_INV = CONTEXT.createPattern(right_img_inv, "repeat");
};
var left_img_inv = new Image();
-left_img_inv.src = "../images/visualization/strand_left_inv.png";
+left_img_inv.src = "/static/images/visualization/strand_left_inv.png";
left_img_inv.onload = function() {
LEFT_STRAND_INV = CONTEXT.createPattern(left_img_inv, "repeat");
};
diff -r 5f93447d642d -r 2cf1ad5edaa6 templates/visualization/display.mako
--- a/templates/visualization/display.mako Mon May 03 09:42:51 2010 -0400
+++ b/templates/visualization/display.mako Mon May 03 14:02:08 2010 -0400
@@ -2,18 +2,229 @@
<%def name="javascripts()">
${parent.javascripts()}
- ## Need visualization JS.
+ ${h.js( "jquery.event.drag", "jquery.autocomplete", "jquery.mousewheel", "trackster", "ui.core", "ui.sortable" )}
+
+ ## HACK: set config as item_data.
+ <% config = item_data %>
+
+ ## TODO: Copied from browser.mako -- probably should create JS file for visualization code and include visualization JS above.
+ <script type="text/javascript">
+
+ ## JG: add controller name.
+ var data_url = "${h.url_for( controller='/tracks', action='data' )}";
+ var view;
+
+ $(function() {
+
+ %if config:
+ view = new View( "${config.get('chrom')}", "${config.get('title') | h}", "${config.get('vis_id')}", "${config.get('dbkey')}" );
+ %for track in config.get('tracks'):
+ view.add_track(
+ new ${track["track_type"]}( "${track['name'] | h}", ${track['dataset_id']}, ${track['prefs']} )
+ );
+ %endfor
+ init();
+ %else:
+ continue_fn = function() {
+ view = new View( undefined, $("#new-title").val(), undefined, $("#new-dbkey").val() );
+ init();
+ hide_modal();
+ };
+ $.ajax({
+ url: "${h.url_for( action='new_browser' )}",
+ data: {},
+ error: function() { alert( "Couldn't create new browser" ) },
+ success: function(form_html) {
+ show_modal("New Track Browser", form_html, {
+ "Cancel": function() { window.location = "/"; },
+ "Continue": function() { $(document).trigger("convert_dbkeys"); continue_fn(); }
+ });
+ $("#new-title").focus();
+ replace_big_select_inputs();
+ }
+ });
+ %endif
+
+ // Execute this when everything is ready
+ function init() {
+ $("ul#sortable-ul").sortable({
+ update: function(event, ui) {
+ for (var track_id in view.tracks) {
+ var track = view.tracks[track_id];
+ }
+ }
+ });
+
+ $(document).bind( "redraw", function( e ) {
+ view.redraw();
+ });
+
+ $("#content").bind("mousewheel", function( e, delta ) {
+ if (delta > 0) {
+ view.zoom_in(e.pageX, $("#viewport-container"));
+ } else {
+ view.zoom_out();
+ }
+ e.preventDefault();
+ });
+
+ $("#content").bind("dblclick", function( e ) {
+ view.zoom_in(e.pageX, $("#viewport-container"));
+ });
+
+ // To let the overview box be draggable
+ $("#overview-box").bind("dragstart", function( e ) {
+ this.current_x = e.offsetX;
+ }).bind("drag", function( e ) {
+ var delta = e.offsetX - this.current_x;
+ this.current_x = e.offsetX;
+
+ var delta_chrom = Math.round(delta / $(document).width() * view.span);
+ view.center += delta_chrom;
+ view.redraw();
+ });
+
+ // To adjust the size of the viewport to fit the fixed-height footer
+ var refresh = function( e ) {
+ $("#viewport-container").height( $(window).height() - 120 );
+ $("#nav-container").width( $("#center").width() );
+ view.redraw();
+ };
+ $(window).bind( "resize", function(e) { refresh(e); } );
+ $("#right-border").bind( "click", function(e) { refresh(e); } );
+ $("#right-border").bind( "dragend", function(e) { refresh(e); } );
+ $(window).trigger( "resize" );
+
+ $("#viewport-container").bind( "dragstart", function( e ) {
+ this.original_low = view.low;
+ this.current_height = e.clientY;
+ this.current_x = e.offsetX;
+ }).bind( "drag", function( e ) {
+ var container = $(this);
+ var delta = e.offsetX - this.current_x;
+ var new_scroll = container.scrollTop() - (e.clientY - this.current_height);
+ if ( new_scroll < container.get(0).scrollHeight - container.height() ) {
+ container.scrollTop(new_scroll);
+ }
+ this.current_height = e.clientY;
+ this.current_x = e.offsetX;
+
+ var delta_chrom = Math.round(delta / $(document).width() * (view.high - view.low));
+ view.center -= delta_chrom;
+ view.redraw();
+ });
+
+ ## JG: Removed 'add-track' init code.
+
+ ## JG: Removed 'save-button' init code
+
+ view.add_label_track( new LabelTrack( $("#top-labeltrack" ) ) );
+ view.add_label_track( new LabelTrack( $("#nav-labeltrack" ) ) );
+
+ $.ajax({
+ ## JG: added controller name
+ url: "${h.url_for( controller='/tracks', action='chroms' )}",
+ data: { dbkey: view.dbkey },
+ dataType: "json",
+ success: function ( data ) {
+ view.chrom_data = data;
+ var chrom_options = '<option value="">Select Chrom/Contig</option>';
+ for (i in data) {
+ var chrom = data[i]['chrom']
+ chrom_options += '<option value="' + chrom + '">' + chrom + '</option>';
+ }
+ $("#chrom").html(chrom_options);
+ $("#chrom").bind( "change", function () {
+ view.chrom = $("#chrom").val();
+ var found = $.grep(view.chrom_data, function(v, i) {
+ return v.chrom === view.chrom;
+ })[0];
+ view.max_high = found.len;
+ view.reset();
+ view.redraw(true);
+
+ for (var track_id in view.tracks) {
+ var track = view.tracks[track_id];
+ if (track.init) {
+ track.init();
+ }
+ }
+ view.redraw();
+ });
+ },
+ error: function() {
+ alert( "Could not load chroms for this dbkey:", view.dbkey );
+ }
+ });
+
+ ## JG: Removed function sidebar_box() and sidebar init code.
+
+ $(window).trigger("resize");
+ };
+
+ });
+
+ </script>
+
</%def>
<%def name="stylesheets()">
${parent.stylesheets()}
- ## Need visualization CSS.
+
+ ${h.css( "history" )}
+ <link rel="stylesheet" type="text/css" href="${h.url_for('/static/trackster.css')}" />
+
+ <style type="text/css">
+ ul#sortable-ul {
+ list-style: none;
+ padding: 0;
+ margin: 5px;
+ }
+ ul#sortable-ul li {
+ display: block;
+ margin: 5px 0;
+ background: #eee;
+ }
+ </style>
</%def>
<%def name="render_item_links( visualization )">
## TODO
</%def>
-<%def name="render_item( visualization, visualization_data )">
- ## TODO
+<%def name="render_item( visualization, config )">
+ <br><br>
+ ## Copied from center_panel() in browser.mako -- probably need to create visualization_common.mako to render view.
+ <div id="content">
+ <div id="top-labeltrack"></div>
+ <div id="viewport-container" style="overflow-x: hidden; overflow-y: auto;">
+ <div id="viewport"></div>
+ </div>
+ </div>
+ <div id="nav-container" style="width:100%;">
+ <div id="nav-labeltrack"></div>
+ <div id="nav">
+ <div id="overview">
+ <div id="overview-viewport">
+ <div id="overview-box"></div>
+ </div>
+ </div>
+ <div id="nav-controls">
+ <form action="#">
+ <select id="chrom" name="chrom" style="width: 15em;">
+ <option value="">Loading</option>
+ </select>
+ <input id="low" size="12" />:<input id="high" size="12" />
+ <input type="hidden" name="id" value="${config.get('vis_id', '')}" />
+ <a href="#" onclick="javascript:view.zoom_in();view.redraw();">
+ <img src="${h.url_for('/static/images/fugue/magnifier-zoom.png')}" />
+ </a>
+ <a href="#" onclick="javascript:view.zoom_out();view.redraw();">
+ <img src="${h.url_for('/static/images/fugue/magnifier-zoom-out.png')}" />
+ </a>
+ </form>
+ <div id="debug" style="float: right"></div>
+ </div>
+ </div>
+ </div>
</%def>
\ No newline at end of file
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/5f93447d642d
changeset: 3731:5f93447d642d
user: rc
date: Mon May 03 09:42:51 2010 -0400
description:
forms default value bug fixed
diffstat:
lib/galaxy/web/controllers/forms.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diffs (12 lines):
diff -r e12587356892 -r 5f93447d642d lib/galaxy/web/controllers/forms.py
--- a/lib/galaxy/web/controllers/forms.py Mon May 03 09:34:13 2010 -0400
+++ b/lib/galaxy/web/controllers/forms.py Mon May 03 09:42:51 2010 -0400
@@ -565,7 +565,7 @@
# helptext
self.helptext.value = field['helptext']
# default value
- self.default.value = field.get('default', TextField('field_default_'+str(index), 40, ''))
+ self.default.value = field.get('default', '')
# type
self.fieldtype = SelectField('field_type_'+str(self.index),
refresh_on_change=True,
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/e12587356892
changeset: 3730:e12587356892
user: rc
date: Mon May 03 09:34:13 2010 -0400
description:
lims:
request page now shows a msg if the request has library selected
forms default value bug fixed
diffstat:
lib/galaxy/model/__init__.py | 6 ++++++
lib/galaxy/web/controllers/forms.py | 4 ++--
templates/admin/requests/show_request.mako | 6 ++++++
3 files changed, 14 insertions(+), 2 deletions(-)
diffs (53 lines):
diff -r a367a7cd53bb -r e12587356892 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Sun May 02 15:34:41 2010 -0400
+++ b/lib/galaxy/model/__init__.py Mon May 03 09:34:13 2010 -0400
@@ -1448,6 +1448,12 @@
return self.state() == self.states.NEW
def complete(self):
return self.state() == self.states.COMPLETE
+ def sequence_run_ready(self):
+ samples = []
+ for s in self.samples:
+ if not s.library:
+ samples.append(s.name)
+ return samples
class RequestEvent( object ):
diff -r a367a7cd53bb -r e12587356892 lib/galaxy/web/controllers/forms.py
--- a/lib/galaxy/web/controllers/forms.py Sun May 02 15:34:41 2010 -0400
+++ b/lib/galaxy/web/controllers/forms.py Mon May 03 09:34:13 2010 -0400
@@ -469,7 +469,7 @@
'required': row[3],
'type': row[4],
'selectlist': options,
- 'default': row[7]})
+ 'default': row[6]})
except:
return trans.response.send_redirect( web.url_for( controller='forms',
action='new',
@@ -565,7 +565,7 @@
# helptext
self.helptext.value = field['helptext']
# default value
- self.default.value = field['default']
+ self.default.value = field.get('default', TextField('field_default_'+str(index), 40, ''))
# type
self.fieldtype = SelectField('field_type_'+str(self.index),
refresh_on_change=True,
diff -r a367a7cd53bb -r e12587356892 templates/admin/requests/show_request.mako
--- a/templates/admin/requests/show_request.mako Sun May 02 15:34:41 2010 -0400
+++ b/templates/admin/requests/show_request.mako Mon May 03 09:34:13 2010 -0400
@@ -108,6 +108,12 @@
document.onkeypress = stopRKey
</script>
+%if request.submitted():
+ <% samples_not_ready = request.sequence_run_ready() %>
+ %if samples_not_ready:
+ ${render_msg( "Select a target library and folder for all the samples before starting the sequence run", "warning" )}
+ %endif
+%endif
%if request.rejected():
${render_msg( "Reason for rejection: "+request.last_comment(), "warning" )}
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/a367a7cd53bb
changeset: 3729:a367a7cd53bb
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Sun May 02 15:34:41 2010 -0400
description:
Bug fixes for history import: (a) import can now be used by users who have access via sharing and (b) import changes the current history to the imported history. Functional tests updated to reflect new functionality and error messages.
diffstat:
lib/galaxy/web/controllers/history.py | 9 +++------
test/functional/test_history_functions.py | 9 +++++++--
2 files changed, 10 insertions(+), 8 deletions(-)
diffs (56 lines):
diff -r 3ac0dfcdf44e -r a367a7cd53bb lib/galaxy/web/controllers/history.py
--- a/lib/galaxy/web/controllers/history.py Fri Apr 30 18:26:49 2010 -0400
+++ b/lib/galaxy/web/controllers/history.py Sun May 02 15:34:41 2010 -0400
@@ -513,7 +513,6 @@
"""Import another user's history via a shared URL"""
msg = ""
user = trans.get_user()
- user_history = trans.get_history()
# Set referer message
if 'referer' in kwd:
referer = kwd['referer']
@@ -527,11 +526,9 @@
# Do import.
if not id:
return trans.show_error_message( "You must specify a history you want to import.<br>You can %s." % referer_message, use_panels=True )
- import_history = self.get_history( trans, id, check_ownership=False )
+ import_history = self.get_history( trans, id, check_ownership=False, check_accessible=True )
if not import_history:
return trans.show_error_message( "The specified history does not exist.<br>You can %s." % referer_message, use_panels=True )
- if not import_history.importable:
- return trans.show_error_message( "The owner of this history has disabled imports via this link.<br>You can %s." % referer_message, use_panels=True )
if user:
if import_history.user_id == user.id:
return trans.show_error_message( "You cannot import your own history.<br>You can %s." % referer_message, use_panels=True )
@@ -548,8 +545,8 @@
new_history.add_galaxy_session( galaxy_session, association=association )
trans.sa_session.add( new_history )
trans.sa_session.flush()
- if not user_history.datasets:
- trans.set_history( new_history )
+ # Set imported history to be user's current history.
+ trans.set_history( new_history )
return trans.show_ok_message(
message="""History "%s" has been imported. <br>You can <a href="%s">start using this history</a> or %s."""
% ( new_history.name, web.url_for( '/' ), referer_message ), use_panels=True )
diff -r 3ac0dfcdf44e -r a367a7cd53bb test/functional/test_history_functions.py
--- a/test/functional/test_history_functions.py Fri Apr 30 18:26:49 2010 -0400
+++ b/test/functional/test_history_functions.py Sun May 02 15:34:41 2010 -0400
@@ -196,10 +196,15 @@
self.disable_access_via_link( self.security.encode_id( history3.id ),
check_str='Anyone can view and import this history',
check_str_after_submit='Make History Accessible via Link' )
- # Try importing history3 after disabling access via link.
+ # Try importing history3 after disabling access via link. To do this, need to login as regular user 2, who cannot access
+ # history via sharing or via link.
+ self.logout()
+ self.login( email=regular_user2.email )
self.import_history_via_url( self.security.encode_id( history3.id ),
admin_user.email,
- check_str_after_submit='The owner of this history has disabled imports via this link.' )
+ check_str_after_submit='History is not accessible to current user' )
+ self.logout()
+ self.login( email=admin_user.email )
# Test sharing history3 with an invalid user
self.share_current_history( 'jack(a)jill.com',
check_str_after_submit='jack(a)jill.com is not a valid Galaxy user.' )
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/3ac0dfcdf44e
changeset: 3728:3ac0dfcdf44e
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Fri Apr 30 18:26:49 2010 -0400
description:
merge
diffstat:
lib/galaxy/web/framework/__init__.py | 4 +-
lib/galaxy/webapps/community/controllers/admin.py | 385 +++++++--
lib/galaxy/webapps/community/controllers/common.py | 105 ++-
lib/galaxy/webapps/community/controllers/tool.py | 293 +++++--
lib/galaxy/webapps/community/controllers/upload.py | 68 +-
lib/galaxy/webapps/community/model/__init__.py | 7 +-
lib/galaxy/webapps/community/model/mapping.py | 14 +-
lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py | 3 +-
lib/galaxy/webapps/community/security/__init__.py | 6 +
templates/webapps/community/admin/index.mako | 43 +-
templates/webapps/community/category/edit_category.mako | 44 +
templates/webapps/community/category/rename_category.mako | 44 -
templates/webapps/community/index.mako | 64 +-
templates/webapps/community/tool/browse_tool.mako | 37 -
templates/webapps/community/tool/edit_tool.mako | 3 +-
templates/webapps/community/tool/view_tool.mako | 58 +-
templates/webapps/community/upload/upload.mako | 3 +
tools/samtools/sam_to_bam.py | 2 +-
tools/sr_mapping/bowtie_wrapper.py | 4 +-
tools/sr_mapping/bwa_wrapper_code.py | 9 +-
20 files changed, 883 insertions(+), 313 deletions(-)
diffs (1884 lines):
diff -r 8e00344d941a -r 3ac0dfcdf44e lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py Fri Apr 30 18:26:06 2010 -0400
+++ b/lib/galaxy/web/framework/__init__.py Fri Apr 30 18:26:49 2010 -0400
@@ -66,7 +66,7 @@
decorator.exposed = True
return decorator
-def require_login( verb="perform this action", use_panels=False ):
+def require_login( verb="perform this action", use_panels=False, webapp='galaxy' ):
def argcatcher( func ):
def decorator( self, trans, *args, **kwargs ):
if trans.get_user():
@@ -74,7 +74,7 @@
else:
return trans.show_error_message(
'You must be <a target="_top" href="%s">logged in</a> to %s</div>.'
- % ( url_for( controller='user', action='login' ), verb ), use_panels=use_panels )
+ % ( url_for( controller='user', action='login', webapp=webapp ), verb ), use_panels=use_panels )
return decorator
return argcatcher
diff -r 8e00344d941a -r 3ac0dfcdf44e lib/galaxy/webapps/community/controllers/admin.py
--- a/lib/galaxy/webapps/community/controllers/admin.py Fri Apr 30 18:26:06 2010 -0400
+++ b/lib/galaxy/webapps/community/controllers/admin.py Fri Apr 30 18:26:49 2010 -0400
@@ -2,7 +2,7 @@
from galaxy.webapps.community import model
from galaxy.model.orm import *
from galaxy.web.framework.helpers import time_ago, iff, grids
-from common import get_categories, get_category
+from common import get_categories, get_category, get_tools, get_event, get_tool, get_versions
import logging
log = logging.getLogger( __name__ )
@@ -18,13 +18,6 @@
if user.username:
return user.username
return 'not set'
- class StatusColumn( grids.GridColumn ):
- def get_value( self, trans, grid, user ):
- if user.purged:
- return "purged"
- elif user.deleted:
- return "deleted"
- return ""
class GroupsColumn( grids.GridColumn ):
def get_value( self, trans, grid, user ):
if user.groups:
@@ -45,6 +38,16 @@
if user.galaxy_sessions:
return self.format( user.galaxy_sessions[ 0 ].update_time )
return 'never'
+ class StatusColumn( grids.GridColumn ):
+ def get_value( self, trans, grid, user ):
+ if user.purged:
+ return "purged"
+ elif user.deleted:
+ return "deleted"
+ return ""
+ class ToolsColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, user ):
+ return '<a href="browse_tools_by_user?operation=browse&id=%s">%s</a>' % ( trans.security.encode_id( user.id ), str( len( user.tools ) ) )
# Grid definition
webapp = "community"
@@ -69,6 +72,10 @@
ExternalColumn( "External", attach_popup=False ),
LastLoginColumn( "Last Login", format=time_ago ),
StatusColumn( "Status", attach_popup=False ),
+ ToolsColumn( "Uploaded Tools",
+ model_class=model.User,
+ attach_popup=False,
+ filterable="advanced" ),
# Columns that are valid for filtering but are not visible.
grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
]
@@ -200,7 +207,7 @@
return None
def build_initial_query( self, session ):
return session.query( self.model_class )
- def apply_default_filter( self, trans, query, **kwargs ):
+ def apply_default_filter( self, trans, query, **kwd ):
return query.filter( model.Role.type != model.Role.types.PRIVATE )
class GroupListGrid( grids.Grid ):
@@ -280,34 +287,31 @@
def build_initial_query( self, session ):
return session.query( self.model_class )
-class CategoryListGrid( grids.Grid ):
+class ManageCategoryListGrid( grids.Grid ):
class NameColumn( grids.TextColumn ):
def get_value( self, trans, grid, category ):
return category.name
class DescriptionColumn( grids.TextColumn ):
def get_value( self, trans, grid, category ):
return category.description
- class StatusColumn( grids.GridColumn ):
- def get_value( self, trans, grid, category ):
- if category.deleted:
- return "deleted"
- return ""
# Grid definition
webapp = "community"
- title = "Categories"
+ title = "Manage Categories"
model_class = model.Category
template='/webapps/community/category/grid.mako'
default_sort_key = "name"
columns = [
NameColumn( "Name",
key="name",
- link=( lambda item: dict( operation="Edit category", id=item.id, webapp="community" ) ),
+ link=( lambda item: dict( operation="Edit", id=item.id, webapp="community" ) ),
model_class=model.Category,
- attach_popup=True,
+ attach_popup=False,
filterable="advanced" ),
- DescriptionColumn( "Description", attach_popup=False ),
- StatusColumn( "Status", attach_popup=False ),
+ DescriptionColumn( "Description",
+ model_class=model.Category,
+ attach_popup=False,
+ filterable="advanced" ),
# Columns that are valid for filtering but are not visible.
grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
]
@@ -318,13 +322,9 @@
filterable="standard" ) )
global_actions = [
grids.GridAction( "Add new category",
- dict( controller='admin', action='categories', operation='create', webapp="community" ) )
+ dict( controller='admin', action='manage_categories', operation='create', webapp="community" ) )
]
- operations = [ grids.GridOperation( "Rename",
- condition=( lambda item: not item.deleted ),
- allow_multiple=False,
- url_args=dict( webapp="community", action="rename_category" ) ),
- grids.GridOperation( "Delete",
+ operations = [ grids.GridOperation( "Delete",
condition=( lambda item: not item.deleted ),
allow_multiple=True,
url_args=dict( webapp="community", action="mark_category_deleted" ) ),
@@ -349,16 +349,77 @@
def build_initial_query( self, session ):
return session.query( self.model_class )
+class ToolsByCategoryListGrid( grids.Grid ):
+ class NameColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, category ):
+ return category.name
+ class DescriptionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, category ):
+ return category.description
+ class ToolsColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, category ):
+ if category.tools:
+ return len( category.tools )
+ return 0
+
+ # Grid definition
+ webapp = "community"
+ title = "Tools by Category"
+ model_class = model.Category
+ template='/webapps/community/category/grid.mako'
+ default_sort_key = "name"
+ columns = [
+ NameColumn( "Name",
+ key="name",
+ link=( lambda item: dict( operation="Browse Category", id=item.id, webapp="community" ) ),
+ model_class=model.Category,
+ attach_popup=True,
+ filterable="advanced" ),
+ DescriptionColumn( "Description",
+ model_class=model.Category,
+ attach_popup=False,
+ filterable="advanced" ),
+ ToolsColumn( "Tools",
+ model_class=model.Category,
+ attach_popup=False,
+ filterable="advanced" ),
+ # Columns that are valid for filtering but are not visible.
+ grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
+ ]
+ columns.append( grids.MulticolFilterColumn( "Search",
+ cols_to_filter=[ columns[0], columns[1], columns[2] ],
+ key="free-text-search",
+ visible=False,
+ filterable="standard" ) )
+ standard_filters = [
+ grids.GridColumnFilter( "Active", args=dict( deleted=False ) ),
+ grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ),
+ grids.GridColumnFilter( "All", args=dict( deleted='All' ) )
+ ]
+ num_rows_per_page = 50
+ preserve_state = False
+ use_paging = True
+ def get_current_item( self, trans ):
+ return None
+ def build_initial_query( self, session ):
+ return session.query( self.model_class )
+
class ToolListGrid( grids.Grid ):
class NameColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
return tool.name
+ class VersionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.version
+ class DescriptionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.description
class CategoryColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
if tool.categories:
rval = ''
for tca in tool.categories:
- rval = '%s%s<br/>' % ( rval, tca.category.name )
+ rval += '<a href="browse_category?id=%s">%s</a><br/>\n' % ( trans.security.encode_id( tca.category.id ), tca.category.name )
return rval
return 'not set'
class StateColumn( grids.GridColumn ):
@@ -389,7 +450,7 @@
return accepted_filters
class UserColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
- return tool.user.email
+ return '<a href="browse_tools_by_user?operation=browse&id=%s">%s</a>' % ( trans.security.encode_id( tool.user.id ), tool.user.username )
# Grid definition
title = "Tools"
model_class = model.Tool
@@ -402,36 +463,39 @@
link=( lambda item: dict( operation="View Tool", id=item.id, cntrller='admin', webapp="community" ) ),
attach_popup=True,
filterable="advanced" ),
+ VersionColumn( "Version",
+ model_class=model.Tool,
+ attach_popup=False,
+ filterable="advanced" ),
+ DescriptionColumn( "Description",
+ model_class=model.Tool,
+ attach_popup=False,
+ filterable="advanced" ),
CategoryColumn( "Category",
- key="category",
model_class=model.Category,
attach_popup=False,
filterable="advanced" ),
- StateColumn( "State",
- key="state",
+ StateColumn( "Status",
model_class=model.Event,
- attach_popup=False,
- filterable="advanced" ),
+ attach_popup=False ),
+ UserColumn( "Uploaded By",
+ key="username",
+ model_class=model.User,
+ attach_popup=False,
+ filterable="advanced" ),
# Columns that are valid for filtering but are not visible.
- grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
+ grids.DeletedColumn( "Deleted", model_class=model.Tool, key="deleted", visible=False, filterable="advanced" )
]
columns.append( grids.MulticolFilterColumn( "Search",
cols_to_filter=[ columns[0], columns[1] ],
key="free-text-search",
visible=False,
filterable="standard" ) )
- global_actions = [
- grids.GridAction( "Upload tool", dict( controller='upload', action='upload', type='tool' ) )
- ]
operations = [
grids.GridOperation( "Edit information",
condition=( lambda item: not item.deleted ),
allow_multiple=False,
- url_args=dict( controller="common", action="edit_tool", cntrller="admin", webapp="community" ) ),
- grids.GridOperation( "Manage categories",
- condition=( lambda item: not item.deleted ),
- allow_multiple=False,
- url_args=dict( controller="common", action="manage_categories", cntrller="admin", webapp="community" ) )
+ url_args=dict( controller="common", action="edit_tool", cntrller="admin", webapp="community" ) )
]
standard_filters = [
grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ),
@@ -443,57 +507,128 @@
use_paging = True
def build_initial_query( self, session ):
return session.query( self.model_class )
- def apply_default_filter( self, trans, query, **kwargs ):
+ def apply_default_filter( self, trans, query, **kwd ):
+ tool_id = kwd.get( 'tool_id', False )
+ if tool_id:
+ if str( tool_id ).lower() in [ '', 'none' ]:
+ # Return an empty query since the current user cannot view any
+ # tools (possibly due to state not being approved, etc).
+ return query.filter( model.Tool.id == None )
+ tool_id = util.listify( tool_id )
+ query = query.filter( or_( *map( lambda id: self.model_class.id == id, tool_id ) ) )
return query.filter( self.model_class.deleted==False )
-class AdminCommunity( BaseController, Admin ):
+class AdminController( BaseController, Admin ):
user_list_grid = UserListGrid()
role_list_grid = RoleListGrid()
group_list_grid = GroupListGrid()
- category_list_grid = CategoryListGrid()
+ manage_category_list_grid = ManageCategoryListGrid()
+ tools_by_category_list_grid = ToolsByCategoryListGrid()
tool_list_grid = ToolListGrid()
@web.expose
@web.require_admin
- def browse_tools( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
+ def browse_tools( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "edit tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='edit_tool',
+ cntrller='admin',
+ **kwd ) )
+ elif operation == "view tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='view_tool',
+ cntrller='admin',
+ **kwd ) )
+ # Render the list view
+ return self.tool_list_grid( trans, **kwd )
+ @web.expose
+ @web.require_admin
+ def browse_tools_by_category( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "browse category":
+ return self.browse_category( trans, id=kwd['id'] )
+ # Render the list view
+ return self.tools_by_category_list_grid( trans, **kwd )
+ @web.expose
+ @web.require_admin
+ def browse_tools_by_user( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
if operation == "browse":
- return trans.response.send_redirect( web.url_for( controller='tool',
- action='browse_tools',
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_tools_by_user',
cntrller='admin',
- **kwargs ) )
+ **kwd ) )
+ elif operation == "browse category":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_category',
+ cntrller='admin',
+ **kwd ) )
+ elif operation == "view tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='view_tool',
+ cntrller='admin',
+ **kwd ) )
elif operation == "edit tool":
return trans.response.send_redirect( web.url_for( controller='common',
action='edit_tool',
cntrller='admin',
- **kwargs ) )
- elif operation == "view tool":
- return trans.response.send_redirect( web.url_for( controller='common',
- action='view_tool',
- cntrller='admin',
- **kwargs ) )
+ **kwd ) )
+ elif operation == "download tool":
+ return trans.response.send_redirect( web.url_for( controller='tool',
+ action='download_tool',
+ **kwd ) )
# Render the list view
- return self.tool_list_grid( trans, **kwargs )
+ return self.tool_list_grid( trans, **kwd )
@web.expose
@web.require_admin
- def categories( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
+ def manage_categories( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
if operation == "create":
- return self.create_category( trans, **kwargs )
+ return self.create_category( trans, **kwd )
if operation == "delete":
- return self.mark_category_deleted( trans, **kwargs )
+ return self.mark_category_deleted( trans, **kwd )
if operation == "undelete":
- return self.undelete_category( trans, **kwargs )
+ return self.undelete_category( trans, **kwd )
if operation == "purge":
- return self.purge_category( trans, **kwargs )
- if operation == "rename":
- return self.rename_category( trans, **kwargs )
+ return self.purge_category( trans, **kwd )
+ if operation == "edit":
+ return self.edit_category( trans, **kwd )
# Render the list view
- return self.category_list_grid( trans, **kwargs )
-
+ return self.manage_category_list_grid( trans, **kwd )
+ @web.expose
+ @web.require_admin
+ def browse_tools_by_state( self, trans, state=None, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ if state is None:
+ id = params.get( 'id', None )
+ if not id:
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ message='Select a status',
+ status='error' ) )
+ event = get_event( trans, id )
+ state = event.state
+ tool_id = get_tools_by_state( trans, state )
+ if not tool_id:
+ tool_id = 'None'
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ tool_id=tool_id ) )
+ @web.expose
+ @web.require_admin
+ def browse_category( self, trans, **kwd ):
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_category',
+ cntrller='admin',
+ **kwd ) )
@web.expose
@web.require_admin
def create_category( self, trans, **kwd ):
@@ -515,7 +650,7 @@
message = "Category '%s' has been created" % category.name
trans.sa_session.flush()
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
@@ -530,42 +665,76 @@
status=status )
@web.expose
@web.require_admin
- def rename_category( self, trans, **kwd ):
+ def set_tool_state( self, trans, state, **kwd ):
+ params = util.Params( kwd )
+ webapp = params.get( 'webapp', 'galaxy' )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ redirect = params.get( 'no_redirect', True )
+ id = params.get( 'id', None )
+ if not id:
+ message = "No tool id received for setting status"
+ status = 'error'
+ else:
+ tool = get_tool( trans, id )
+ if state == trans.app.model.Tool.states.APPROVED:
+ # If we're approving a tool, all previous versions must be set to archived
+ for version in get_versions( trans, tool ):
+ if version != tool and version.is_approved():
+ self.set_tool_state( trans, trans.app.model.Tool.states.ARCHIVED, id=trans.app.security.encode_id( version.id ), redirect='False' )
+ event = trans.model.Event( state )
+ # Flush so we an get an id
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ tea = trans.model.ToolEventAssociation( tool, event )
+ trans.sa_session.add( tea )
+ trans.sa_session.flush()
+ message = "State of tool '%s' is now %s" % ( tool.name, state )
+ if redirect:
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ webapp=webapp,
+ message=message,
+ status=status ) )
+ @web.expose
+ @web.require_admin
+ def edit_category( self, trans, **kwd ):
params = util.Params( kwd )
webapp = params.get( 'webapp', 'galaxy' )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
id = params.get( 'id', None )
if not id:
- message = "No category ids received for renaming"
+ message = "No category ids received for editing"
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=message,
status='error' ) )
category = get_category( trans, id )
- if params.get( 'rename_category_button', False ):
+ if params.get( 'edit_category_button', False ):
old_name = category.name
new_name = util.restore_text( params.name )
new_description = util.restore_text( params.description )
- if not new_name:
- message = 'Enter a valid name'
- status = 'error'
- elif trans.sa_session.query( trans.app.model.Category ).filter( trans.app.model.Category.table.c.name==new_name ).first():
- message = 'A category with that name already exists'
- status = 'error'
+ if old_name != new_name:
+ if not new_name:
+ message = 'Enter a valid name'
+ status = 'error'
+ elif trans.sa_session.query( trans.app.model.Category ).filter( trans.app.model.Category.table.c.name==new_name ).first():
+ message = 'A category with that name already exists'
+ status = 'error'
else:
category.name = new_name
category.description = new_description
trans.sa_session.add( category )
trans.sa_session.flush()
- message = "Category '%s' has been renamed to '%s'" % ( old_name, new_name )
+ message = "The information has been saved for category '%s'" % ( new_name )
return trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
- return trans.fill_template( '/webapps/community/category/rename_category.mako',
+ return trans.fill_template( '/webapps/community/category/edit_category.mako',
category=category,
webapp=webapp,
message=message,
@@ -579,7 +748,7 @@
if not id:
message = "No category ids received for deleting"
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=message,
status='error' ) )
@@ -592,7 +761,7 @@
trans.sa_session.flush()
message += " %s " % category.name
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
@@ -605,7 +774,7 @@
if not id:
message = "No category ids received for undeleting"
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=message,
status='error' ) )
@@ -617,7 +786,7 @@
if not category.deleted:
message = "Category '%s' has not been deleted, so it cannot be undeleted." % category.name
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='error' ) )
@@ -628,7 +797,7 @@
undeleted_categories += " %s" % category.name
message = "Undeleted %d categories: %s" % ( count, undeleted_categories )
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
@@ -644,7 +813,7 @@
if not id:
message = "No category ids received for purging"
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='error' ) )
@@ -655,7 +824,7 @@
if not category.deleted:
message = "Category '%s' has not been deleted, so it cannot be purged." % category.name
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='error' ) )
@@ -665,7 +834,41 @@
trans.sa_session.flush()
message += " %s " % category.name
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
+
+## ---- Utility methods -------------------------------------------------------
+
+def get_tools_by_state( trans, state ):
+ tool_id = []
+ if state == trans.model.Tool.states.NEW:
+ for tool in get_tools( trans ):
+ if tool.is_new():
+ tool_id.append( tool.id )
+ elif state == trans.model.Tool.states.ERROR:
+ for tool in get_tools( trans ):
+ if tool.is_error():
+ tool_id.append( tool.id )
+ elif state == trans.model.Tool.states.DELETED:
+ for tool in get_tools( trans ):
+ if tool.is_deleted():
+ tool_id.append( tool.id )
+ elif state == trans.model.Tool.states.WAITING:
+ for tool in get_tools( trans ):
+ if tool.is_waiting():
+ tool_id.append( tool.id )
+ elif state == trans.model.Tool.states.APPROVED:
+ for tool in get_tools( trans ):
+ if tool.is_approved():
+ tool_id.append( tool.id )
+ elif state == trans.model.Tool.states.REJECTED:
+ for tool in get_tools( trans ):
+ if tool.is_rejected():
+ tool_id.append( tool.id )
+ elif state == trans.model.Tool.states.ARCHIVED:
+ for tool in get_tools( trans ):
+ if tool.is_archived():
+ tool_id.append( tool.id )
+ return tool_id
diff -r 8e00344d941a -r 3ac0dfcdf44e lib/galaxy/webapps/community/controllers/common.py
--- a/lib/galaxy/webapps/community/controllers/common.py Fri Apr 30 18:26:06 2010 -0400
+++ b/lib/galaxy/webapps/community/controllers/common.py Fri Apr 30 18:26:49 2010 -0400
@@ -8,7 +8,7 @@
import logging
log = logging.getLogger( __name__ )
-class CommunityCommon( BaseController ):
+class CommonController( BaseController ):
@web.expose
def edit_tool( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
@@ -81,9 +81,11 @@
tool = get_tool( trans, id )
categories = [ tca.category for tca in tool.categories ]
tool_file_contents = tarfile.open( tool.file_name, 'r' ).getnames()
+ versions = get_versions( trans, tool )
return trans.fill_template( '/webapps/community/tool/view_tool.mako',
tool=tool,
tool_file_contents=tool_file_contents,
+ versions=versions,
categories=categories,
cntrller=cntrller,
message=message,
@@ -100,22 +102,78 @@
message='Select a tool to to upload a new version',
status='error' ) )
tool = get_tool( trans, id )
- if params.save_button and ( params.file_data != '' or params.url != '' ):
- # TODO: call the upload method in the upload controller.
- message = 'Uploading new version not implemented'
- status = 'error'
+ return trans.response.send_redirect( web.url_for( controller='upload',
+ action='upload',
+ message=message,
+ status=status,
+ replace_id=id ) )
+ @web.expose
+ def browse_category( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_categories',
+ message='Select a category',
+ status='error' ) )
+ category = get_category( trans, id )
+ # If request came from the tool controller, then we need to filter by the state of the
+ # tool in addition to the category.
+ if cntrller == 'tool':
+ tool_id = get_approved_tools( trans, category=category )
+ else:
+ # If request came from the admin controller, we don't filter on tool state.
+ tool_id = [ tca.tool.id for tca in category.tools ]
+ if not tool_id:
+ tool_id = 'None'
return trans.response.send_redirect( web.url_for( controller=cntrller,
action='browse_tools',
- message='Not yet implemented, sorry...',
- status='error' ) )
+ tool_id=tool_id ) )
+ @web.expose
+ def browse_tools_by_user( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ message='Select a user',
+ status='error' ) )
+ user = get_user( trans, id )
+ # If request came from the tool controller, then we need to filter by the state of the
+ # tool if the user is not viewing his own tools
+ if cntrller == 'tool':
+ tool_id = get_approved_tools( trans, user=user )
+ else:
+ # If request came from the admin controller, we don't filter on tool state.
+ tool_id = [ tool.id for tool in user.tools ]
+ if not tool_id:
+ tool_id = 'None'
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools_by_user',
+ tool_id=tool_id ) )
## ---- Utility methods -------------------------------------------------------
+def get_versions( trans, tool ):
+ versions = [tool]
+ this_tool = tool
+ while tool.newer_version:
+ versions.insert( 0, tool.newer_version )
+ tool = tool.newer_version
+ tool = this_tool
+ while tool.older_version:
+ versions.append( tool.older_version[0] )
+ tool = tool.older_version[0]
+ return versions
def get_categories( trans ):
"""Get all categories from the database"""
return trans.sa_session.query( trans.model.Category ) \
.filter( trans.model.Category.table.c.deleted==False ) \
- .order_by( trans.model.Category.table.c.name )
+ .order_by( trans.model.Category.table.c.name ).all()
def get_unassociated_categories( trans, obj ):
"""Get all categories from the database that are not associated with obj"""
# TODO: we currently assume we are setting a tool category, so this method may need
@@ -142,4 +200,33 @@
obj.categories.append( trans.model.ToolCategoryAssociation( obj, category ) )
def get_tool( trans, id ):
return trans.sa_session.query( trans.model.Tool ).get( trans.app.security.decode_id( id ) )
-
+def get_tools( trans ):
+ return trans.sa_session.query( trans.model.Tool ).order_by( trans.model.Tool.name )
+def get_approved_tools( trans, category=None, user=None ):
+ tool_id = []
+ if category:
+ # Return only the approved tools in the category
+ for tca in category.tools:
+ tool = tca.tool
+ if tool.is_approved():
+ tool_id.append( tool.id )
+ elif user:
+ if trans.user == user:
+ # If the current user is browsing his own tools, then don't filter on state
+ tool_id = [ tool.id for tool in user.tools ]
+ else:
+ # The current user is viewing all tools uploaded by another user, so show only
+ # approved tools
+ for tool in user.active_tools:
+ if tool.is_approved():
+ tool_id.append( tool.id )
+ else:
+ # Return all approved tools
+ for tool in get_tools( trans ):
+ if tool.is_approved():
+ tool_id.append( tool.id )
+ return tool_id
+def get_event( trans, id ):
+ return trans.sa_session.query( trans.model.Event ).get( trans.security.decode_id( id ) )
+def get_user( trans, id ):
+ return trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( id ) )
diff -r 8e00344d941a -r 3ac0dfcdf44e lib/galaxy/webapps/community/controllers/tool.py
--- a/lib/galaxy/webapps/community/controllers/tool.py Fri Apr 30 18:26:06 2010 -0400
+++ b/lib/galaxy/webapps/community/controllers/tool.py Fri Apr 30 18:26:49 2010 -0400
@@ -15,38 +15,111 @@
class NameColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
return tool.name
+ class VersionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.version
+ class DescriptionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.description
class CategoryColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
if tool.categories:
rval = ''
for tca in tool.categories:
- rval += '%s<br/>\n' % tca.category.name
+ rval += '<a href="browse_category?id=%s">%s</a><br/>\n' % ( trans.security.encode_id( tca.category.id ), tca.category.name )
return rval
return 'not set'
- def filter( self, trans, user, query, column_filter ):
- # Category.name conflicts with Tool.name, so we have to make our own filter
- def get_single_filter( filter ):
- return func.lower( model.Category.name ).like( "%" + filter.lower() + "%" )
- if column_filter == 'All':
- pass
- elif isinstance( column_filter, list ):
- clause_list = []
- for filter in column_filter:
- clause_list.append( get_single_filter( filter ) )
- query = query.filter( or_( *clause_list ) )
- else:
- query = query.filter( get_single_filter( column_filter ) )
- return query
- def get_link( self, trans, grid, tool, filter_params ):
+ class UserColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return '<a href="browse_tools_by_user?operation=browse&id=%s">%s</a>' % ( trans.security.encode_id( tool.user.id ), tool.user.username )
+ # Grid definition
+ title = "Tools"
+ model_class = model.Tool
+ template='/webapps/community/tool/grid.mako'
+ default_sort_key = "name"
+ columns = [
+ NameColumn( "Name",
+ key="name",
+ model_class=model.Tool,
+ link=( lambda item: dict( operation="View Tool", id=item.id, cntrller='tool', webapp="community" ) ),
+ attach_popup=True,
+ filterable="advanced" ),
+ VersionColumn( "Version",
+ model_class=model.Tool,
+ attach_popup=False,
+ filterable="advanced" ),
+ DescriptionColumn( "Description",
+ model_class=model.Tool,
+ attach_popup=False,
+ filterable="advanced" ),
+ CategoryColumn( "Categories",
+ model_class=model.Category,
+ attach_popup=False,
+ filterable="advanced" ),
+ UserColumn( "Uploaded By",
+ key="username",
+ model_class=model.User,
+ attach_popup=False,
+ filterable="advanced" ),
+ # Columns that are valid for filtering but are not visible.
+ grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
+ ]
+ columns.append( grids.MulticolFilterColumn( "Search",
+ cols_to_filter=[ columns[0], columns[1] ],
+ key="free-text-search",
+ visible=False,
+ filterable="standard" ) )
+ operations = [
+ grids.GridOperation( "Download tool",
+ condition=( lambda item: not item.deleted ),
+ allow_multiple=False,
+ url_args=dict( controller="tool", action="download_tool", cntrller="tool", webapp="community" ) )
+ ]
+ standard_filters = [
+ grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ),
+ grids.GridColumnFilter( "All", args=dict( deleted='All' ) )
+ ]
+ default_filter = dict( name="All", deleted="False", username="All" )
+ num_rows_per_page = 50
+ preserve_state = False
+ use_paging = True
+ def build_initial_query( self, session ):
+ return session.query( self.model_class )
+ def apply_default_filter( self, trans, query, **kwd ):
+ def filter_query( query, tool_id ):
+ if str( tool_id ).lower() in [ '', 'none' ]:
+ # Return an empty query since the current user cannot view any
+ # tools (possibly due to state not being approved, etc).
+ return query.filter( model.Tool.id == None )
+ tool_id = util.listify( tool_id )
+ query = query.filter( or_( *map( lambda id: self.model_class.id == id, tool_id ) ) )
+ return query.filter( self.model_class.deleted==False )
+ tool_id = kwd.get( 'tool_id', False )
+ if not tool_id:
+ # Display only approved tools
+ tool_id = get_approved_tools( trans )
+ if not tool_id:
+ tool_id = 'None'
+ return filter_query( query, tool_id )
+
+class ToolsByUserListGrid( grids.Grid ):
+ class NameColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.name
+ class VersionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.version
+ class DescriptionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.description
+ class CategoryColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
if tool.categories:
- filter_params['f-category'] = []
+ rval = ''
for tca in tool.categories:
- filter_params['f-category'].append( tca.category.name )
- if len( filter_params['f-category'] ) == 1:
- filter_params['f-category'] = filter_params['f-category'][0]
- filter_params['advanced-search'] = 'True'
- return filter_params
- return None
+ rval += '<a href="browse_category?id=%s">%s</a><br/>\n' % ( trans.security.encode_id( tca.category.id ), tca.category.name )
+ return rval
+ return 'not set'
class StateColumn( grids.GridColumn ):
def get_value( self, trans, grid, tool ):
state = tool.state()
@@ -75,13 +148,9 @@
return accepted_filters
class UserColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
- return tool.user.username
- def get_link( self, trans, grid, tool, filter_params ):
- filter_params['f-username'] = tool.user.username
- filter_params['advanced-search'] = 'True'
- return filter_params
+ return '<a href="browse_tools_by_user?operation=browse&id=%s">%s</a>' % ( trans.security.encode_id( tool.user.id ), tool.user.username )
# Grid definition
- title = "Tools"
+ title = "Tools By User"
model_class = model.Tool
template='/webapps/community/tool/grid.mako'
default_sort_key = "name"
@@ -92,21 +161,26 @@
link=( lambda item: dict( operation="View Tool", id=item.id, cntrller='tool', webapp="community" ) ),
attach_popup=True,
filterable="advanced" ),
- CategoryColumn( "Categories",
- key="category",
+ VersionColumn( "Version",
model_class=model.Tool,
attach_popup=False,
filterable="advanced" ),
+ DescriptionColumn( "Description",
+ model_class=model.Tool,
+ attach_popup=False,
+ filterable="advanced" ),
+ CategoryColumn( "Categories",
+ model_class=model.Category,
+ attach_popup=False,
+ filterable="advanced" ),
+ StateColumn( "Status",
+ model_class=model.Event,
+ attach_popup=False ),
UserColumn( "Uploaded By",
key="username",
model_class=model.User,
attach_popup=False,
filterable="advanced" ),
- StateColumn( "State",
- key="state",
- model_class=model.Event,
- attach_popup=False,
- filterable="advanced" ),
# Columns that are valid for filtering but are not visible.
grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
]
@@ -130,18 +204,44 @@
preserve_state = False
use_paging = True
def build_initial_query( self, session ):
- return session.query( self.model_class ).outerjoin( model.ToolCategoryAssociation ).outerjoin( model.Category )
- def apply_default_filter( self, trans, query, **kwargs ):
- return query.filter( self.model_class.deleted==False )
+ return session.query( self.model_class )
+ def apply_default_filter( self, trans, query, **kwd ):
+ def filter_query( query, tool_id ):
+ if str( tool_id ).lower() in [ '', 'none' ]:
+ # Return an empty query since the current user cannot view any
+ # tools (possibly due to state not being approved, etc).
+ return query.filter( model.Tool.id == None )
+ tool_id = util.listify( tool_id )
+ query = query.filter( or_( *map( lambda id: self.model_class.id == id, tool_id ) ) )
+ return query.filter( self.model_class.deleted==False )
+ tool_id = kwd.get( 'tool_id', False )
+ if not tool_id:
+ # Display only approved tools
+ tool_id = get_approved_tools( trans )
+ if not tool_id:
+ tool_id = 'None'
+ return filter_query( query, tool_id )
-class ToolCategoryListGrid( grids.Grid ):
+class CategoryListGrid( grids.Grid ):
class NameColumn( grids.TextColumn ):
def get_value( self, trans, grid, category ):
return category.name
class DescriptionColumn( grids.TextColumn ):
def get_value( self, trans, grid, category ):
return category.description
+ class ToolsColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, category ):
+ if category.tools:
+ viewable_tools = 0
+ for tca in category.tools:
+ tool = tca.tool
+ if tool.is_approved():
+ viewable_tools += 1
+ return viewable_tools
+ return 0
+
# Grid definition
+ webapp = "community"
title = "Tool Categories"
model_class = model.Category
template='/webapps/community/category/grid.mako'
@@ -157,7 +257,13 @@
key="description",
model_class=model.Category,
attach_popup=False,
- filterable="advanced" )
+ filterable="advanced" ),
+ ToolsColumn( "Tools",
+ model_class=model.Tool,
+ attach_popup=False,
+ filterable="advanced" ),
+ # Columns that are valid for filtering but are not visible.
+ grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
]
columns.append( grids.MulticolFilterColumn( "Search",
cols_to_filter=[ columns[0], columns[1] ],
@@ -165,21 +271,23 @@
visible=False,
filterable="standard" ) )
standard_filters = [
+ grids.GridColumnFilter( "Active", args=dict( deleted=False ) ),
+ grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ),
grids.GridColumnFilter( "All", args=dict( deleted='All' ) )
]
- default_filter = dict( name="All", deleted="False" )
num_rows_per_page = 50
preserve_state = False
use_paging = True
def build_initial_query( self, session ):
return session.query( self.model_class )
- def apply_default_filter( self, trans, query, **kwargs ):
+ def apply_default_filter( self, trans, query, **kwd ):
return query.filter( self.model_class.deleted==False )
-class ToolBrowserController( BaseController ):
-
- tool_category_list_grid = ToolCategoryListGrid()
+class ToolController( BaseController ):
+
tool_list_grid = ToolListGrid()
+ tools_by_user_list_grid = ToolsByUserListGrid()
+ category_list_grid = CategoryListGrid()
@web.expose
def index( self, trans, **kwd ):
@@ -188,53 +296,84 @@
status = params.get( 'status', 'done' )
return trans.fill_template( '/webapps/community/index.mako', message=message, status=status )
@web.expose
- def browse_tool_categories( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
+ def browse_categories( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
if operation == "browse category":
- category_id = int( trans.app.security.decode_id( kwargs['id'] ) )
- category = trans.sa_session.query( model.Category ).get( category_id )
- del kwargs['id']
- del kwargs['operation']
- kwargs['f-category'] = category.name
- return trans.response.send_redirect( web.url_for( controller='tool',
- action='browse_tools',
- **kwargs ) )
- return self.tool_category_list_grid( trans, **kwargs )
+ return self.browse_category( trans, id=kwd['id'] )
+ elif operation == "view tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='view_category',
+ cntrller='tool',
+ **kwd ) )
+ elif operation == "edit tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='edit_category',
+ cntrller='tool',
+ **kwd ) )
+ # Render the list view
+ return self.category_list_grid( trans, **kwd )
@web.expose
- def browse_tools( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
- if operation == "browse":
- return trans.response.send_redirect( web.url_for( controller='tool',
- action='browse_tool',
+ def browse_category( self, trans, **kwd ):
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_category',
+ cntrller='tool',
+ **kwd ) )
+ @web.expose
+ def browse_tools( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "browse category":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_category',
cntrller='tool',
- **kwargs ) )
+ **kwd ) )
elif operation == "view tool":
return trans.response.send_redirect( web.url_for( controller='common',
action='view_tool',
cntrller='tool',
- **kwargs ) )
+ **kwd ) )
elif operation == "edit tool":
return trans.response.send_redirect( web.url_for( controller='common',
action='edit_tool',
cntrller='tool',
- **kwargs ) )
+ **kwd ) )
elif operation == "download tool":
return trans.response.send_redirect( web.url_for( controller='tool',
action='download_tool',
- **kwargs ) )
+ **kwd ) )
# Render the list view
- return self.tool_list_grid( trans, **kwargs )
+ return self.tool_list_grid( trans, **kwd )
@web.expose
- def browse_tool( self, trans, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- return trans.fill_template( '/webapps/community/tool/browse_tool.mako',
- tools=tools,
- message=message,
- status=status )
+ def browse_tools_by_user( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "browse":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_tools_by_user',
+ cntrller='tool',
+ **kwd ) )
+ elif operation == "browse category":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_category',
+ cntrller='tool',
+ **kwd ) )
+ elif operation == "view tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='view_tool',
+ cntrller='tool',
+ **kwd ) )
+ elif operation == "edit tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='edit_tool',
+ cntrller='tool',
+ **kwd ) )
+ elif operation == "download tool":
+ return trans.response.send_redirect( web.url_for( controller='tool',
+ action='download_tool',
+ **kwd ) )
+ # Render the list view
+ return self.tools_by_user_list_grid( trans, **kwd )
@web.expose
def download_tool( self, trans, **kwd ):
params = util.Params( kwd )
diff -r 8e00344d941a -r 3ac0dfcdf44e lib/galaxy/webapps/community/controllers/upload.py
--- a/lib/galaxy/webapps/community/controllers/upload.py Fri Apr 30 18:26:06 2010 -0400
+++ b/lib/galaxy/webapps/community/controllers/upload.py Fri Apr 30 18:26:49 2010 -0400
@@ -3,23 +3,36 @@
from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy.model.orm import *
from galaxy.webapps.community import datatypes
-from common import get_categories, get_category
+from common import get_categories, get_category, get_versions
log = logging.getLogger( __name__ )
# States for passing messages
SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error"
+class UploadError( Exception ):
+ pass
+
class UploadController( BaseController ):
@web.expose
+ @web.require_login( 'upload', use_panels=True, webapp='community' )
def upload( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
category_ids = util.listify( params.get( 'category_id', '' ) )
+ replace_id = params.get( 'replace_id', None )
+ replace_version = None
uploaded_file = None
- if params.file_data == '' and params.url.strip() == '':
+ categories = get_categories( trans )
+ if not get_categories( trans ):
+ return trans.response.send_redirect( web.url_for( controller='tool',
+ action='browse_tools',
+ cntrller='tool',
+ message='No categories have been configured in this instance of the Galaxy Community. An administrator needs to create some via the Administrator control panel before anything can be uploaded',
+ status='error' ) )
+ elif params.file_data == '' and params.url.strip() == '':
message = 'No files were entered on the upload form.'
status = 'error'
elif params.file_data == '':
@@ -47,6 +60,29 @@
obj = datatype.create_model_object( meta )
trans.sa_session.add( obj )
if isinstance( obj, trans.app.model.Tool ):
+ existing = trans.sa_session.query( trans.app.model.Tool ).filter_by( tool_id = meta.id ).all()
+ if existing and replace_id is None:
+ raise UploadError( 'A tool with the same ID already exists. If you are trying to update this tool to a new version, please use the upload form on the "Edit Tool" page. Otherwise, please choose a new ID.' )
+ elif existing:
+ replace_version = trans.sa_session.query( trans.app.model.Tool ).get( int( trans.app.security.decode_id( replace_id ) ) )
+ if replace_version.newer_version:
+ # If the user has picked an old version, switch to the newest version
+ replace_version = get_versions( trans, replace_version )[0]
+ if trans.user != replace_version.user:
+ raise UploadError( 'You are not the owner of this tool and may not upload new versions of it.' )
+ if replace_version.tool_id != meta.id:
+ raise UploadError( 'The new tool id (%s) does not match the old tool id (%s). Please check the tool XML file' % ( meta.id, replace_version.tool_id ) )
+ for old_version in get_versions( trans, replace_version ):
+ if old_version.version == meta.version:
+ raise UploadError( 'The new version (%s) matches an old version. Please check your version in the tool XML file' % meta.version )
+ if old_version.is_new():
+ raise UploadError( 'There is an existing version of this tool which is unsubmitted. Please either <a href="%s">submit or delete it</a> before uploading a new version.' % url_for( controller='common',
+ action='view_tool',
+ cntrller='tool',
+ id=trans.app.security.encode_id( old_version.id ) ) )
+ if old_version.is_waiting():
+ raise UploadError( 'There is an existing version of this tool which is waiting for administrative approval. Please contact an administrator for help.' )
+ # Defer setting the id since the newer version id doesn't exist until the new Tool object is flushed
if category_ids:
for category_id in category_ids:
category = trans.app.model.Category.get( trans.security.decode_id( category_id ) )
@@ -58,6 +94,9 @@
tea = trans.app.model.ToolEventAssociation( obj, event )
trans.sa_session.add_all( ( event, tea ) )
trans.sa_session.flush()
+ if replace_version and replace_id:
+ replace_version.newer_version_id = obj.id
+ trans.sa_session.flush()
try:
os.link( uploaded_file.name, obj.file_name )
except OSError:
@@ -69,13 +108,29 @@
id=trans.app.security.encode_id( obj.id ),
message='Uploaded %s' % meta.message,
status='done' ) )
- except datatypes.DatatypeVerificationError, e:
+ except ( datatypes.DatatypeVerificationError, UploadError ), e:
message = str( e )
status = 'error'
- except sqlalchemy.exc.IntegrityError:
- message = 'A tool with the same ID already exists. If you are trying to update this tool to a new version, please use the upload form on the "Edit Tool" page. Otherwise, please choose a new ID.'
- status = 'error'
uploaded_file.close()
+ elif replace_id is not None:
+ replace_version = trans.sa_session.query( trans.app.model.Tool ).get( int( trans.app.security.decode_id( replace_id ) ) )
+ old_version = None
+ for old_version in get_versions( trans, replace_version ):
+ if old_version.is_new():
+ message = 'There is an existing version of this tool which is unsubmitted. Please either submit or delete it before uploading a new version.'
+ break
+ if old_version.is_waiting():
+ message = 'There is an existing version of this tool which is waiting for administrative approval. Please contact an administrator for help.'
+ break
+ else:
+ old_version = None
+ if old_version is not None:
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='view_tool',
+ cntrller='tool',
+ id=trans.app.security.encode_id( old_version.id ),
+ message=message,
+ status='error' ) )
selected_upload_type = params.get( 'type', 'tool' )
selected_categories = [ trans.security.decode_id( id ) for id in category_ids ]
return trans.fill_template( '/webapps/community/upload/upload.mako',
@@ -83,5 +138,6 @@
status=status,
selected_upload_type=selected_upload_type,
upload_types=trans.app.datatypes_registry.get_datatypes_for_select_list(),
+ replace_id=replace_id,
selected_categories=selected_categories,
categories=get_categories( trans ) )
diff -r 8e00344d941a -r 3ac0dfcdf44e lib/galaxy/webapps/community/model/__init__.py
--- a/lib/galaxy/webapps/community/model/__init__.py Fri Apr 30 18:26:06 2010 -0400
+++ b/lib/galaxy/webapps/community/model/__init__.py Fri Apr 30 18:26:49 2010 -0400
@@ -92,7 +92,8 @@
DELETED = 'deleted',
WAITING = 'waiting for approval',
APPROVED = 'approved',
- REJECTED = 'rejected' )
+ REJECTED = 'rejected',
+ ARCHIVED = 'archived' )
def __init__( self, guid=None, tool_id=None, name=None, description=None, user_description=None, category=None, version=None, user_id=None, external_filename=None ):
self.guid = guid
self.tool_id = tool_id
@@ -153,10 +154,14 @@
return self.state() == self.states.ERROR
def is_deleted( self ):
return self.state() == self.states.DELETED
+ def is_waiting( self ):
+ return self.state() == self.states.WAITING
def is_approved( self ):
return self.state() == self.states.APPROVED
def is_rejected( self ):
return self.state() == self.states.REJECTED
+ def is_archived( self ):
+ return self.state() == self.states.ARCHIVED
@property
def extension( self ):
# if instantiated via a query, this unmapped property won't exist
diff -r 8e00344d941a -r 3ac0dfcdf44e lib/galaxy/webapps/community/model/mapping.py
--- a/lib/galaxy/webapps/community/model/mapping.py Fri Apr 30 18:26:06 2010 -0400
+++ b/lib/galaxy/webapps/community/model/mapping.py Fri Apr 30 18:26:49 2010 -0400
@@ -103,9 +103,10 @@
Tool.table = Table( "tool", metadata,
Column( "id", Integer, primary_key=True ),
Column( "guid", TrimmedString( 255 ), index=True, unique=True ),
- Column( "tool_id", TrimmedString( 255 ), index=True, unique=True ),
+ Column( "tool_id", TrimmedString( 255 ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "newer_version_id", Integer, ForeignKey( "tool.id" ), nullable=True ),
Column( "name", TrimmedString( 255 ), index=True ),
Column( "description" , TEXT ),
Column( "user_description" , TEXT ),
@@ -164,8 +165,8 @@
# With the tables defined we can define the mappers and setup the
# relationships between the model objects.
assign_mapper( context, User, User.table,
- properties=dict( tools=relation( Tool, order_by=desc( Tool.table.c.update_time ) ),
- active_tools=relation( Tool, primaryjoin=( ( Tool.table.c.user_id == User.table.c.id ) & ( not_( Tool.table.c.deleted ) ) ), order_by=desc( Tool.table.c.update_time ) ),
+ properties=dict( tools=relation( Tool, primaryjoin=( Tool.table.c.user_id == User.table.c.id ), order_by=( Tool.table.c.name ) ),
+ active_tools=relation( Tool, primaryjoin=( ( Tool.table.c.user_id == User.table.c.id ) & ( not_( Tool.table.c.deleted ) ) ), order_by=( Tool.table.c.name ) ),
galaxy_sessions=relation( GalaxySession, order_by=desc( GalaxySession.table.c.update_time ) ) ) )
assign_mapper( context, Group, Group.table,
@@ -215,7 +216,11 @@
properties = dict(
categories=relation( ToolCategoryAssociation ),
events=relation( ToolEventAssociation ),
- user=relation( User.mapper )
+ user=relation( User.mapper ),
+ older_version=relation(
+ Tool,
+ primaryjoin=( Tool.table.c.newer_version_id == Tool.table.c.id ),
+ backref=backref( "newer_version", primaryjoin=( Tool.table.c.newer_version_id == Tool.table.c.id ), remote_side=[Tool.table.c.id] ) )
) )
assign_mapper( context, Event, Event.table,
@@ -238,7 +243,6 @@
)
)
-
def guess_dialect_for_url( url ):
return (url.split(':', 1))[0]
diff -r 8e00344d941a -r 3ac0dfcdf44e lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py
--- a/lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py Fri Apr 30 18:26:06 2010 -0400
+++ b/lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py Fri Apr 30 18:26:49 2010 -0400
@@ -80,9 +80,10 @@
Tool_table = Table( "tool", metadata,
Column( "id", Integer, primary_key=True ),
Column( "guid", TrimmedString( 255 ), index=True, unique=True ),
- Column( "tool_id", TrimmedString( 255 ), index=True, unique=True ),
+ Column( "tool_id", TrimmedString( 255 ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "newer_version_id", Integer, ForeignKey( "tool.id" ), nullable=True ),
Column( "name", TrimmedString( 255 ), index=True ),
Column( "description" , TEXT ),
Column( "user_description" , TEXT ),
diff -r 8e00344d941a -r 3ac0dfcdf44e lib/galaxy/webapps/community/security/__init__.py
--- a/lib/galaxy/webapps/community/security/__init__.py Fri Apr 30 18:26:06 2010 -0400
+++ b/lib/galaxy/webapps/community/security/__init__.py Fri Apr 30 18:26:49 2010 -0400
@@ -167,6 +167,12 @@
# We currently assume the current user can edit the item if they are the owner (i.e., they
# uploaded the item), and the item is in a NEW state.
return user and user==item.user and item.is_new()
+ def can_upload_new_version( self, user, item, versions ):
+ state_ok = True
+ for version in versions:
+ if version.is_new() or version.is_approved():
+ state_ok = False
+ return user and user==item.user and state_ok
def get_permitted_actions( filter=None ):
'''Utility method to return a subset of RBACAgent's permitted actions'''
diff -r 8e00344d941a -r 3ac0dfcdf44e templates/webapps/community/admin/index.mako
--- a/templates/webapps/community/admin/index.mako Fri Apr 30 18:26:06 2010 -0400
+++ b/templates/webapps/community/admin/index.mako Fri Apr 30 18:26:49 2010 -0400
@@ -54,7 +54,6 @@
</style>
</%def>
-
<%def name="init()">
<%
self.has_left_panel=True
@@ -71,32 +70,34 @@
<div class="toolMenu">
<div class="toolSectionList">
<div class="toolSectionTitle">
+ <span>Tools</span>
+ </div>
+ <div class="toolSectionBody">
+ <div class="toolSectionBg">
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='browse_tools_by_state', state=trans.model.Tool.states.WAITING, webapp='community' )}">Tools awaiting approval</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='browse_tools_by_category' )}">Browse by category</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='browse_tools', webapp='community' )}">Browse all tools</a></div>
+ </div>
+ </div>
+ <div class="toolSectionPad"></div>
+ <div class="toolSectionTitle">
+ <span>Categories</span>
+ </div>
+ <div class="toolSectionBody">
+ <div class="toolSectionBg">
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='manage_categories', webapp='community' )}">Manage categories</a></div>
+ </div>
+ </div>
+ <div class="toolSectionTitle">
<span>Security</span>
</div>
<div class="toolSectionBody">
<div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='users', webapp='community' )}" target="galaxy_main">Manage users</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='groups', webapp='community' )}" target="galaxy_main">Manage groups</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='roles', webapp='community' )}" target="galaxy_main">Manage roles</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='users', webapp='community' )}">Manage users</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='groups', webapp='community' )}">Manage groups</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='roles', webapp='community' )}">Manage roles</a></div>
</div>
</div>
- <div class="toolSectionTitle">
- <span>Tools</span>
- </div>
- <div class="toolSectionBody">
- <div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='browse_tools', webapp='community' )}" target="galaxy_main">Manage tools</a></div>
- </div>
- </div>
- <div class="toolSectionPad"></div>
- <div class="toolSectionTitle">
- <span>Community</span>
- </div>
- <div class="toolSectionBody">
- <div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='categories', webapp='community' )}" target="galaxy_main">Manage categories</a></div>
- </div>
- </div>
</div>
</div>
</div>
diff -r 8e00344d941a -r 3ac0dfcdf44e templates/webapps/community/category/edit_category.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/webapps/community/category/edit_category.mako Fri Apr 30 18:26:49 2010 -0400
@@ -0,0 +1,44 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolForm">
+ <div class="toolFormTitle">Change category name and description</div>
+ <div class="toolFormBody">
+ <form name="library" action="${h.url_for( controller='admin', action='edit_category' )}" method="post" >
+ <div class="form-row">
+ <input name="webapp" type="hidden" value="${webapp}" size=40"/>
+ <label>Name:</label>
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input type="text" name="name" value="${category.name}" size="40"/>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Description:</label>
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input name="description" type="textfield" value="${category.description}" size=40"/>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input type="hidden" name="rename" value="submitted"/>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input type="hidden" name="id" value="${trans.security.encode_id( category.id )}"/>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <input type="submit" name="edit_category_button" value="Save"/>
+ </div>
+ </form>
+ </div>
+</div>
diff -r 8e00344d941a -r 3ac0dfcdf44e templates/webapps/community/category/rename_category.mako
--- a/templates/webapps/community/category/rename_category.mako Fri Apr 30 18:26:06 2010 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-<div class="toolForm">
- <div class="toolFormTitle">Change category name and description</div>
- <div class="toolFormBody">
- <form name="library" action="${h.url_for( controller='admin', action='rename_category' )}" method="post" >
- <div class="form-row">
- <input name="webapp" type="hidden" value="${webapp}" size=40"/>
- <label>Name:</label>
- <div style="float: left; width: 250px; margin-right: 10px;">
- <input type="text" name="name" value="${category.name}" size="40"/>
- </div>
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <label>Description:</label>
- <div style="float: left; width: 250px; margin-right: 10px;">
- <input name="description" type="textfield" value="${category.description}" size=40"/>
- </div>
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <div style="float: left; width: 250px; margin-right: 10px;">
- <input type="hidden" name="rename" value="submitted"/>
- </div>
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <div style="float: left; width: 250px; margin-right: 10px;">
- <input type="hidden" name="id" value="${trans.security.encode_id( category.id )}"/>
- </div>
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <input type="submit" name="rename_category_button" value="Save"/>
- </div>
- </form>
- </div>
-</div>
diff -r 8e00344d941a -r 3ac0dfcdf44e templates/webapps/community/index.mako
--- a/templates/webapps/community/index.mako Fri Apr 30 18:26:06 2010 -0400
+++ b/templates/webapps/community/index.mako Fri Apr 30 18:26:49 2010 -0400
@@ -1,6 +1,57 @@
<%inherit file="/webapps/community/base_panels.mako"/>
<%namespace file="/message.mako" import="render_msg" />
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ## TODO: Clean up these styles and move into panel_layout.css (they are
+ ## used here and in the editor).
+ <style type="text/css">
+ #left {
+ background: #C1C9E5 url(${h.url_for('/static/style/menu_bg.png')}) top repeat-x;
+ }
+ div.toolMenu {
+ margin: 5px;
+ margin-left: 10px;
+ margin-right: 10px;
+ }
+ div.toolSectionPad {
+ margin: 0;
+ padding: 0;
+ height: 5px;
+ font-size: 0px;
+ }
+ div.toolSectionDetailsInner {
+ margin-left: 5px;
+ margin-right: 5px;
+ }
+ div.toolSectionTitle {
+ padding-bottom: 0px;
+ font-weight: bold;
+ }
+ div.toolMenuGroupHeader {
+ font-weight: bold;
+ padding-top: 0.5em;
+ padding-bottom: 0.5em;
+ color: #333;
+ font-style: italic;
+ border-bottom: dotted #333 1px;
+ margin-bottom: 0.5em;
+ }
+ div.toolTitle {
+ padding-top: 5px;
+ padding-bottom: 5px;
+ margin-left: 16px;
+ margin-right: 10px;
+ display: list-item;
+ list-style: square outside;
+ }
+ a:link, a:visited, a:active
+ {
+ color: #303030;
+ }
+ </style>
+</%def>
+
<%def name="init()">
<%
self.has_left_panel=True
@@ -29,17 +80,20 @@
</div>
<div class="toolSectionBody">
<div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='tool', action='browse_tool_categories' )}" target="galaxy_main">Browse tools by category</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='tool', action='browse_tools' )}" target="galaxy_main">Browse all tools</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_categories' )}">Browse by category</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_tools' )}">Browse all tools</a></div>
+ %if trans.user:
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_tools_by_user', operation='browse', id=trans.security.encode_id( trans.user.id ) )}">Browse your tools</a></div>
+ %endif
</div>
</div>
<div class="toolSectionBody">
<div class="toolSectionBg">
<div class="toolTitle">
%if trans.user:
- <a href="${h.url_for( controller='upload', action='upload', type='tool' )}" target="galaxy_main">Upload a tool</a>
+ <a target="galaxy_main" href="${h.url_for( controller='upload', action='upload', type='tool' )}">Upload a tool</a>
%else:
- Login to upload
+ <a target="galaxy_main" href="${h.url_for( controller='/user', action='login', webapp='community' )}">Login to upload</a>
%endif
</div>
</div>
@@ -54,7 +108,7 @@
if trans.app.config.require_login and not trans.user:
center_url = h.url_for( controller='user', action='login', message=message, status=status )
else:
- center_url = h.url_for( controller='tool', action='browse_tool_categories', message=message, status=status )
+ center_url = h.url_for( controller='tool', action='browse_categories', message=message, status=status )
%>
<iframe name="galaxy_main" id="galaxy_main" frameborder="0" style="position: absolute; width: 100%; height: 100%;" src="${center_url}"> </iframe>
</%def>
diff -r 8e00344d941a -r 3ac0dfcdf44e templates/webapps/community/tool/browse_tool.mako
--- a/templates/webapps/community/tool/browse_tool.mako Fri Apr 30 18:26:06 2010 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-<%namespace file="/message.mako" import="render_msg" />
-
-<%!
- def inherit(context):
- if context.get('use_panels'):
- return '/webapps/community/base_panels.mako'
- else:
- return '/base.mako'
-%>
-<%inherit file="${inherit(context)}"/>
-
-<%def name="title()">Browse Tool</%def>
-
-<h2>Galaxy Tool</h2>
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-%if not tools:
- There are no tools
-%else:
- <table class="grid">
- <thead>
- <tr>
- <th>Name</th>
- <th>Description</th>
- </tr>
- </thead>
- <tbody>
- <tr class="formRow id="toolRow">
- <td><a href="${h.url_for( controller='tool', action='browse', id=trans.security.encode_id( tool.id ) )}">${tool.name}</a></td>
- <td>${tool.description}</td>
- </tr>
- </tbody>
- </table>
-%endif
diff -r 8e00344d941a -r 3ac0dfcdf44e templates/webapps/community/tool/edit_tool.mako
--- a/templates/webapps/community/tool/edit_tool.mako Fri Apr 30 18:26:06 2010 -0400
+++ b/templates/webapps/community/tool/edit_tool.mako Fri Apr 30 18:26:49 2010 -0400
@@ -58,7 +58,6 @@
<a id="tool-${tool.id}-popup" class="popup-arrow" style="display: none;">▼</a>
<div popupmenu="tool-${tool.id}-popup">
<a class="action-button" href="${h.url_for( controller='common', action='view_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">View information</a>
- <a class="action-button" href="${h.url_for( controller='common', action='manage_categories', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Manage categories</a>
<a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a>
<a class="action-button" href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">Download tool</a>
</div>
@@ -123,7 +122,7 @@
<div class="toolParamHelp" style="clear: both;">
Tools must be approved before they are made available to others in the community. After you have submitted
your tool to be published, you will no longer be able to modify it, so make sure the information above is
- correct and has been saved before submitting for approval.
+ correct and and save any changes before submitting for approval.
</div>
</div>
</form>
diff -r 8e00344d941a -r 3ac0dfcdf44e templates/webapps/community/tool/view_tool.mako
--- a/templates/webapps/community/tool/view_tool.mako Fri Apr 30 18:26:06 2010 -0400
+++ b/templates/webapps/community/tool/view_tool.mako Fri Apr 30 18:26:49 2010 -0400
@@ -6,6 +6,12 @@
if cntrller in [ 'tool' ]:
can_edit = trans.app.security_agent.can_edit_item( trans.user, tool )
+ can_upload_new_version = trans.app.security_agent.can_upload_new_version( trans.user, tool, versions )
+
+ visible_versions = []
+ for version in versions:
+ if version.is_approved() or version.is_archived() or version.user == trans.user:
+ visible_versions.append( version )
%>
<%!
@@ -49,6 +55,30 @@
<h2>View Tool: ${tool.name} <em>${tool.description}</em></h2>
+%if tool.is_approved():
+ <b><i>This is the latest approved version of this tool</i></b>
+%elif tool.is_deleted():
+ <font color="red"><b><i>This is a deleted version of this tool</i></b></font>
+%elif tool.is_archived():
+ <font color="red"><b><i>This is an archived version of this tool</i></b></font>
+%elif tool.is_new():
+ <font color="red"><b><i>This is an unsubmitted version of this tool</i></b></font>
+%elif tool.is_waiting():
+ <font color="red"><b><i>This version of this tool is awaiting administrative approval</i></b></font>
+%elif tool.is_rejected():
+ <font color="red"><b><i>This version of this tool has been rejected by an administrator</i></b></font>
+%endif
+<p/>
+
+%if cntrller=='admin' and tool.is_waiting():
+ <p>
+ <ul class="manage-table-actions">
+ <li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.APPROVED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}"><span>Approve</span></a></li>
+ <li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.REJECTED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}"><span>Reject</span></a></li>
+ </ul>
+ </p>
+%endif
+
%if message:
${render_msg( message, status )}
%endif
@@ -59,7 +89,8 @@
<div popupmenu="tool-${tool.id}-popup">
%if cntrller=='admin' or can_edit:
<a class="action-button" href="${h.url_for( controller='common', action='edit_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Edit information</a>
- <a class="action-button" href="${h.url_for( controller='common', action='manage_categories', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Manage categories</a>
+ %endif
+ %if cntrller=='admin' or can_upload_new_version:
<a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a>
%endif
<a class="action-button" href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">Download tool</a>
@@ -93,11 +124,30 @@
</div>
<div class="form-row">
<label>Categories:</label>
- %for category in categories:
- ${category.name}
- %endfor
+ %if categories:
+ %for category in categories:
+ ${category.name}
+ %endfor
+ %else:
+ none set
+ %endif
<div style="clear: both"></div>
</div>
+ %if len( visible_versions ) > 1:
+ <div class="form-row">
+ <label>All Versions:</label>
+ <ul>
+ %for version in visible_versions:
+ %if version == tool:
+ <li><strong>${version.version} (this version)</strong></li>
+ %else:
+ <li><a href="${h.url_for( controller='common', action='view_tool', id=trans.app.security.encode_id( version.id ), cntrller=cntrller )}">${version.version}</a></li>
+ %endif
+ %endfor
+ </ul>
+ <div style="clear: both"></div>
+ </div>
+ %endif
</div>
</div>
diff -r 8e00344d941a -r 3ac0dfcdf44e templates/webapps/community/upload/upload.mako
--- a/templates/webapps/community/upload/upload.mako Fri Apr 30 18:26:06 2010 -0400
+++ b/templates/webapps/community/upload/upload.mako Fri Apr 30 18:26:49 2010 -0400
@@ -22,6 +22,9 @@
<div class="toolFormBody">
## TODO: nginx
<form id="upload_form" name="upload_form" action="${h.url_for( controller='upload', action='upload' )}" enctype="multipart/form-data" method="post">
+ %if replace_id is not None:
+ <input type='hidden' name="replace_id" value="${replace_id}"/>
+ %endif
<div class="form-row">
<label>Upload Type</label>
<div class="form-row-input">
diff -r 8e00344d941a -r 3ac0dfcdf44e tools/samtools/sam_to_bam.py
--- a/tools/samtools/sam_to_bam.py Fri Apr 30 18:26:06 2010 -0400
+++ b/tools/samtools/sam_to_bam.py Fri Apr 30 18:26:49 2010 -0400
@@ -94,7 +94,7 @@
tmp_stderr.close()
if returncode != 0:
raise Exception, stderr
- if len( open( fai_index_file_path ).read().strip() ) == 0:
+ if os.path.getsize( fai_index_file_path ) == 0:
raise Exception, 'Index file empty, there may be an error with your reference file or settings.'
except Exception, e:
#clean up temp files
diff -r 8e00344d941a -r 3ac0dfcdf44e tools/sr_mapping/bowtie_wrapper.py
--- a/tools/sr_mapping/bowtie_wrapper.py Fri Apr 30 18:26:06 2010 -0400
+++ b/tools/sr_mapping/bowtie_wrapper.py Fri Apr 30 18:26:49 2010 -0400
@@ -387,8 +387,8 @@
if returncode != 0:
raise Exception, stderr
# check that there are results in the output file
- if len( open( options.output, 'rb' ).read().strip() ) == 0:
- raise Exception, 'The output file is empty, there may be an error with your input file or settings.'
+ if os.path.getsize( options.output ) == 0:
+ raise Exception, 'The output file is empty, there may be an error with your input file or settings.' + '\nextra: ' + str(extra)
except Exception, e:
stop_err( 'Error aligning sequence. ' + str( e ) )
finally:
diff -r 8e00344d941a -r 3ac0dfcdf44e tools/sr_mapping/bwa_wrapper_code.py
--- a/tools/sr_mapping/bwa_wrapper_code.py Fri Apr 30 18:26:06 2010 -0400
+++ b/tools/sr_mapping/bwa_wrapper_code.py Fri Apr 30 18:26:49 2010 -0400
@@ -2,11 +2,10 @@
def exec_before_job(app, inp_data, out_data, param_dict, tool):
try:
- refFile = param_dict['solidOrSolexa']['solidRefGenomeSource']['indices'].value
+ refFile = param_dict[ 'genomeSource' ][ 'indices' ].value
except:
try:
- refFile = param_dict['solidOrSolexa']['solidRefGenomeSource']['ownFile'].dbkey
+ refFile = param_dict[ 'genomeSource' ][ 'ownFile' ].dbkey
except:
- out_data['output'].set_dbkey('?')
- return
- out_data['output'].set_dbkey(os.path.split(refFile)[1].split('.')[0])
+ refFile = '?'
+ out_data[ 'output' ].set_dbkey( os.path.split( refFile )[1].split( '.' )[0] )
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/8e00344d941a
changeset: 3727:8e00344d941a
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Fri Apr 30 18:26:06 2010 -0400
description:
Cuffcompare wrapper handles all outputs gracefully. Tests needed.
diffstat:
tools/ngs_rna/cuffcompare_wrapper.py | 30 +++++++++++++++++++--------
tools/ngs_rna/cuffcompare_wrapper.xml | 38 +++++++++++++++++++++++++++++++++-
2 files changed, 57 insertions(+), 11 deletions(-)
diffs (130 lines):
diff -r c642fbb301fc -r 8e00344d941a tools/ngs_rna/cuffcompare_wrapper.py
--- a/tools/ngs_rna/cuffcompare_wrapper.py Fri Apr 30 13:09:29 2010 -0400
+++ b/tools/ngs_rna/cuffcompare_wrapper.py Fri Apr 30 18:26:06 2010 -0400
@@ -18,6 +18,11 @@
parser.add_option( '-A', '--transcripts-accuracy-output', dest='transcripts_accuracy_output_file', help='' )
parser.add_option( '-B', '--transcripts-combined-output', dest='transcripts_combined_output_file', help='' )
parser.add_option( '-C', '--transcripts-tracking-output', dest='transcripts_tracking_output_file', help='' )
+ parser.add_option( '', '--input1-tmap-output', dest='input1_tmap_output_file', help='' )
+ parser.add_option( '', '--input1-refmap-output', dest='input1_refmap_output_file', help='' )
+ parser.add_option( '', '--input2-tmap-output', dest='input2_tmap_output_file', help='' )
+ parser.add_option( '', '--input2-refmap-output', dest='input2_refmap_output_file', help='' )
+
(options, args) = parser.parse_args()
@@ -35,17 +40,22 @@
if options.ignore_nonoverlap:
cmd += " -R "
- # Output/debugging.
- print cmd
+ # Add input files.
- # Add input files.
- cmd += " %s" % options.input1
+ # Need to symlink inputs so that output files are written to temp directory.
+ print options.input1
+ input1_file_name = tmp_output_dir + "/input1"
+ os.symlink( options.input1, input1_file_name )
+ cmd += " %s" % input1_file_name
two_inputs = ( options.input2 != None)
if two_inputs:
- cmd += " %s" % options.input2
+ input2_file_name = tmp_output_dir + "/input2"
+ os.symlink( options.input2, input2_file_name )
+ cmd += " %s" % input2_file_name
+ print cmd
# Run command.
- try:
+ try:
tmp_name = tempfile.NamedTemporaryFile( dir=tmp_output_dir ).name
tmp_stderr = open( tmp_name, 'wb' )
proc = subprocess.Popen( args=cmd, shell=True, cwd=tmp_output_dir, stderr=tmp_stderr.fileno() )
@@ -79,16 +89,18 @@
try:
try:
shutil.copyfile( tmp_output_dir + "/cc_output", options.transcripts_accuracy_output_file )
+ shutil.copyfile( tmp_output_dir + "/input1.tmap", options.input1_tmap_output_file )
+ shutil.copyfile( tmp_output_dir + "/input1.refmap", options.input1_refmap_output_file )
if two_inputs:
shutil.copyfile( tmp_output_dir + "/cc_output.combined.gtf", options.transcripts_combined_output_file )
shutil.copyfile( tmp_output_dir + "/cc_output.tracking", options.transcripts_tracking_output_file )
-
- # TODO: also copy *.tmap, *.refmap to outputs?
+ shutil.copyfile( tmp_output_dir + "/input2.tmap", options.input2_tmap_output_file )
+ shutil.copyfile( tmp_output_dir + "/input2.refmap", options.input2_refmap_output_file )
except Exception, e:
stop_err( 'Error in cuffcompare:\n' + str( e ) )
finally:
# Clean up temp dirs
- if os.path.exists( tmp_output_dir ):
+ if not os.path.exists( tmp_output_dir ):
shutil.rmtree( tmp_output_dir )
if __name__=="__main__": __main__()
\ No newline at end of file
diff -r c642fbb301fc -r 8e00344d941a tools/ngs_rna/cuffcompare_wrapper.xml
--- a/tools/ngs_rna/cuffcompare_wrapper.xml Fri Apr 30 13:09:29 2010 -0400
+++ b/tools/ngs_rna/cuffcompare_wrapper.xml Fri Apr 30 18:26:06 2010 -0400
@@ -3,9 +3,13 @@
<command interpreter="python">
cuffcompare_wrapper.py
--transcripts-accuracy-output=$transcripts_accuracy
+ --input1-tmap-output=$input1_tmap
+ --input1-refmap-output=$input1_refmap
#if $second_gtf.use_second_gtf == "Yes":
--transcripts-combined-output=$transcripts_combined
--transcripts-tracking-output=$transcripts_tracking
+ --input2-tmap-output=$input2_tmap
+ --input2-refmap-output=$input2_refmap
#end if
#if $annotation.use_ref_annotation == "Yes":
-r $annotation.reference_annotation
@@ -46,13 +50,43 @@
</inputs>
<outputs>
- <data format="gtf" name="transcripts_combined" label="${tool.name} on ${on_string}: combined transcripts"/>
- <data format="tracking" name="transcripts_tracking" label="${tool.name} on ${on_string}: transcript tracking"/>
+ <data format="tmap" name="input1_tmap" label="${tool.name} on ${on_string}: data ${input1.hid} tmap file"/>
+ <data format="refmap" name="input1_refmap" label="${tool.name} on ${on_string}: data ${input1.hid} refmap file"/>
+ <data format="tmap" name="input2_tmap" label="${tool.name} on ${on_string}: data ${second_gtf.input2.hid} tmap file">
+ <filter>second_gtf['use_second_gtf'] == "Yes"</filter>
+ </data>
+ <data format="refmap" name="input2_refmap" label="${tool.name} on ${on_string}: data ${second_gtf.input2.hid} refmap file">
+ <filter>second_gtf['use_second_gtf'] == "Yes"</filter>
+ </data>
+ <data format="gtf" name="transcripts_combined" label="${tool.name} on ${on_string}: combined transcripts">
+ <filter>second_gtf['use_second_gtf'] == "Yes"</filter>
+ </data>
+ <data format="tracking" name="transcripts_tracking" label="${tool.name} on ${on_string}: transcript tracking">
+ <filter>second_gtf['use_second_gtf'] == "Yes"</filter>
+ </data>
<data format="gtf" name="transcripts_accuracy" label="${tool.name} on ${on_string}: transcript accuracy"/>
</outputs>
<tests>
+ <!--
+ cuffcompare -r cuffcompare_in3.gtf -R cuffcompare_in1.gtf cuffcompare_in3.gtf
+ -->
<test>
+ <param name="input1" value="cuffcompare_in1.gtf" ftype="gtf"/>
+ <param name="use_second_gtf" value="Yes"/>
+ <param name="input2" value="cuffcompare_in2.gtf" ftype="gtf"/>
+ <param name="use_ref_annotation" value="Yes"/>
+ <param name="reference_annotation" value="cuffcompare_in3.gtf" ftype="gtf"/>
+ <param name="ignore_nonoverlapping_reference" value="Yes"/>
+ <output name="transcripts_combined" file="cuffcompare_out5.gtf"/>
+ <!--
+ <output name="transcripts_tracking" file="cuffcompare_out6.tracking"/>
+ <output name="input1_tmap" file="cuffcompare_out1.tmap"/>
+ <output name="input1_refmap" file="cuffcompare_out2.refmap"/>
+ <output name="input2_tmap" file="cuffcompare_out3.tmap"/>
+ <output name="input2_refmap" file="cuffcompare_out4.refmap"/>
+ <output name="transcripts_accuracy" file="cuffcompare_out7.txt"/>
+ -->
</test>
</tests>
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/42f3f5d78f9a
changeset: 3726:42f3f5d78f9a
user: Nate Coraor <nate(a)bx.psu.edu>
date: Fri Apr 30 16:11:17 2010 -0400
description:
Community: Add tool versioning and tighten up the requirements in the upload/approval process
diffstat:
lib/galaxy/web/framework/__init__.py | 4 +-
lib/galaxy/webapps/community/controllers/admin.py | 19 +-
lib/galaxy/webapps/community/controllers/common.py | 28 ++-
lib/galaxy/webapps/community/controllers/upload.py | 68 +++++++++-
lib/galaxy/webapps/community/model/mapping.py | 9 +-
lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py | 3 +-
lib/galaxy/webapps/community/security/__init__.py | 6 +
templates/webapps/community/tool/view_tool.mako | 49 ++++++-
templates/webapps/community/upload/upload.mako | 3 +
9 files changed, 160 insertions(+), 29 deletions(-)
diffs (410 lines):
diff -r e71a3d03a529 -r 42f3f5d78f9a lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py Fri Apr 30 15:45:32 2010 -0400
+++ b/lib/galaxy/web/framework/__init__.py Fri Apr 30 16:11:17 2010 -0400
@@ -66,7 +66,7 @@
decorator.exposed = True
return decorator
-def require_login( verb="perform this action", use_panels=False ):
+def require_login( verb="perform this action", use_panels=False, webapp='galaxy' ):
def argcatcher( func ):
def decorator( self, trans, *args, **kwargs ):
if trans.get_user():
@@ -74,7 +74,7 @@
else:
return trans.show_error_message(
'You must be <a target="_top" href="%s">logged in</a> to %s</div>.'
- % ( url_for( controller='user', action='login' ), verb ), use_panels=use_panels )
+ % ( url_for( controller='user', action='login', webapp=webapp ), verb ), use_panels=use_panels )
return decorator
return argcatcher
diff -r e71a3d03a529 -r 42f3f5d78f9a lib/galaxy/webapps/community/controllers/admin.py
--- a/lib/galaxy/webapps/community/controllers/admin.py Fri Apr 30 15:45:32 2010 -0400
+++ b/lib/galaxy/webapps/community/controllers/admin.py Fri Apr 30 16:11:17 2010 -0400
@@ -2,7 +2,7 @@
from galaxy.webapps.community import model
from galaxy.model.orm import *
from galaxy.web.framework.helpers import time_ago, iff, grids
-from common import get_categories, get_category, get_tools, get_event, get_tool
+from common import get_categories, get_category, get_tools, get_event, get_tool, get_versions
import logging
log = logging.getLogger( __name__ )
@@ -670,12 +670,18 @@
webapp = params.get( 'webapp', 'galaxy' )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
+ redirect = params.get( 'no_redirect', True )
id = params.get( 'id', None )
if not id:
message = "No tool id received for setting status"
status = 'error'
else:
tool = get_tool( trans, id )
+ if state == trans.app.model.Tool.states.APPROVED:
+ # If we're approving a tool, all previous versions must be set to archived
+ for version in get_versions( trans, tool ):
+ if version != tool and version.is_approved():
+ self.set_tool_state( trans, trans.app.model.Tool.states.ARCHIVED, id=trans.app.security.encode_id( version.id ), redirect='False' )
event = trans.model.Event( state )
# Flush so we an get an id
trans.sa_session.add( event )
@@ -684,11 +690,12 @@
trans.sa_session.add( tea )
trans.sa_session.flush()
message = "State of tool '%s' is now %s" % ( tool.name, state )
- trans.response.send_redirect( web.url_for( controller='admin',
- action='browse_tools',
- webapp=webapp,
- message=message,
- status=status ) )
+ if redirect:
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ webapp=webapp,
+ message=message,
+ status=status ) )
@web.expose
@web.require_admin
def edit_category( self, trans, **kwd ):
diff -r e71a3d03a529 -r 42f3f5d78f9a lib/galaxy/webapps/community/controllers/common.py
--- a/lib/galaxy/webapps/community/controllers/common.py Fri Apr 30 15:45:32 2010 -0400
+++ b/lib/galaxy/webapps/community/controllers/common.py Fri Apr 30 16:11:17 2010 -0400
@@ -81,9 +81,11 @@
tool = get_tool( trans, id )
categories = [ tca.category for tca in tool.categories ]
tool_file_contents = tarfile.open( tool.file_name, 'r' ).getnames()
+ versions = get_versions( trans, tool )
return trans.fill_template( '/webapps/community/tool/view_tool.mako',
tool=tool,
tool_file_contents=tool_file_contents,
+ versions=versions,
categories=categories,
cntrller=cntrller,
message=message,
@@ -100,14 +102,11 @@
message='Select a tool to to upload a new version',
status='error' ) )
tool = get_tool( trans, id )
- if params.save_button and ( params.file_data != '' or params.url != '' ):
- # TODO: call the upload method in the upload controller.
- message = 'Uploading new version not implemented'
- status = 'error'
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='browse_tools',
- message='Not yet implemented, sorry...',
- status='error' ) )
+ return trans.response.send_redirect( web.url_for( controller='upload',
+ action='upload',
+ message=message,
+ status=status,
+ replace_id=id ) )
@web.expose
def browse_category( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
@@ -159,11 +158,22 @@
## ---- Utility methods -------------------------------------------------------
+def get_versions( trans, tool ):
+ versions = [tool]
+ this_tool = tool
+ while tool.newer_version:
+ versions.insert( 0, tool.newer_version )
+ tool = tool.newer_version
+ tool = this_tool
+ while tool.older_version:
+ versions.append( tool.older_version[0] )
+ tool = tool.older_version[0]
+ return versions
def get_categories( trans ):
"""Get all categories from the database"""
return trans.sa_session.query( trans.model.Category ) \
.filter( trans.model.Category.table.c.deleted==False ) \
- .order_by( trans.model.Category.table.c.name )
+ .order_by( trans.model.Category.table.c.name ).all()
def get_unassociated_categories( trans, obj ):
"""Get all categories from the database that are not associated with obj"""
# TODO: we currently assume we are setting a tool category, so this method may need
diff -r e71a3d03a529 -r 42f3f5d78f9a lib/galaxy/webapps/community/controllers/upload.py
--- a/lib/galaxy/webapps/community/controllers/upload.py Fri Apr 30 15:45:32 2010 -0400
+++ b/lib/galaxy/webapps/community/controllers/upload.py Fri Apr 30 16:11:17 2010 -0400
@@ -3,23 +3,36 @@
from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy.model.orm import *
from galaxy.webapps.community import datatypes
-from common import get_categories, get_category
+from common import get_categories, get_category, get_versions
log = logging.getLogger( __name__ )
# States for passing messages
SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error"
+class UploadError( Exception ):
+ pass
+
class UploadController( BaseController ):
@web.expose
+ @web.require_login( 'upload', use_panels=True, webapp='community' )
def upload( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
category_ids = util.listify( params.get( 'category_id', '' ) )
+ replace_id = params.get( 'replace_id', None )
+ replace_version = None
uploaded_file = None
- if params.file_data == '' and params.url.strip() == '':
+ categories = get_categories( trans )
+ if not get_categories( trans ):
+ return trans.response.send_redirect( web.url_for( controller='tool',
+ action='browse_tools',
+ cntrller='tool',
+ message='No categories have been configured in this instance of the Galaxy Community. An administrator needs to create some via the Administrator control panel before anything can be uploaded',
+ status='error' ) )
+ elif params.file_data == '' and params.url.strip() == '':
message = 'No files were entered on the upload form.'
status = 'error'
elif params.file_data == '':
@@ -47,6 +60,29 @@
obj = datatype.create_model_object( meta )
trans.sa_session.add( obj )
if isinstance( obj, trans.app.model.Tool ):
+ existing = trans.sa_session.query( trans.app.model.Tool ).filter_by( tool_id = meta.id ).all()
+ if existing and replace_id is None:
+ raise UploadError( 'A tool with the same ID already exists. If you are trying to update this tool to a new version, please use the upload form on the "Edit Tool" page. Otherwise, please choose a new ID.' )
+ elif existing:
+ replace_version = trans.sa_session.query( trans.app.model.Tool ).get( int( trans.app.security.decode_id( replace_id ) ) )
+ if replace_version.newer_version:
+ # If the user has picked an old version, switch to the newest version
+ replace_version = get_versions( trans, replace_version )[0]
+ if trans.user != replace_version.user:
+ raise UploadError( 'You are not the owner of this tool and may not upload new versions of it.' )
+ if replace_version.tool_id != meta.id:
+ raise UploadError( 'The new tool id (%s) does not match the old tool id (%s). Please check the tool XML file' % ( meta.id, replace_version.tool_id ) )
+ for old_version in get_versions( trans, replace_version ):
+ if old_version.version == meta.version:
+ raise UploadError( 'The new version (%s) matches an old version. Please check your version in the tool XML file' % meta.version )
+ if old_version.is_new():
+ raise UploadError( 'There is an existing version of this tool which is unsubmitted. Please either <a href="%s">submit or delete it</a> before uploading a new version.' % url_for( controller='common',
+ action='view_tool',
+ cntrller='tool',
+ id=trans.app.security.encode_id( old_version.id ) ) )
+ if old_version.is_waiting():
+ raise UploadError( 'There is an existing version of this tool which is waiting for administrative approval. Please contact an administrator for help.' )
+ # Defer setting the id since the newer version id doesn't exist until the new Tool object is flushed
if category_ids:
for category_id in category_ids:
category = trans.app.model.Category.get( trans.security.decode_id( category_id ) )
@@ -58,6 +94,9 @@
tea = trans.app.model.ToolEventAssociation( obj, event )
trans.sa_session.add_all( ( event, tea ) )
trans.sa_session.flush()
+ if replace_version and replace_id:
+ replace_version.newer_version_id = obj.id
+ trans.sa_session.flush()
try:
os.link( uploaded_file.name, obj.file_name )
except OSError:
@@ -69,13 +108,29 @@
id=trans.app.security.encode_id( obj.id ),
message='Uploaded %s' % meta.message,
status='done' ) )
- except datatypes.DatatypeVerificationError, e:
+ except ( datatypes.DatatypeVerificationError, UploadError ), e:
message = str( e )
status = 'error'
- except sqlalchemy.exc.IntegrityError:
- message = 'A tool with the same ID already exists. If you are trying to update this tool to a new version, please use the upload form on the "Edit Tool" page. Otherwise, please choose a new ID.'
- status = 'error'
uploaded_file.close()
+ elif replace_id is not None:
+ replace_version = trans.sa_session.query( trans.app.model.Tool ).get( int( trans.app.security.decode_id( replace_id ) ) )
+ old_version = None
+ for old_version in get_versions( trans, replace_version ):
+ if old_version.is_new():
+ message = 'There is an existing version of this tool which is unsubmitted. Please either submit or delete it before uploading a new version.'
+ break
+ if old_version.is_waiting():
+ message = 'There is an existing version of this tool which is waiting for administrative approval. Please contact an administrator for help.'
+ break
+ else:
+ old_version = None
+ if old_version is not None:
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='view_tool',
+ cntrller='tool',
+ id=trans.app.security.encode_id( old_version.id ),
+ message=message,
+ status='error' ) )
selected_upload_type = params.get( 'type', 'tool' )
selected_categories = [ trans.security.decode_id( id ) for id in category_ids ]
return trans.fill_template( '/webapps/community/upload/upload.mako',
@@ -83,5 +138,6 @@
status=status,
selected_upload_type=selected_upload_type,
upload_types=trans.app.datatypes_registry.get_datatypes_for_select_list(),
+ replace_id=replace_id,
selected_categories=selected_categories,
categories=get_categories( trans ) )
diff -r e71a3d03a529 -r 42f3f5d78f9a lib/galaxy/webapps/community/model/mapping.py
--- a/lib/galaxy/webapps/community/model/mapping.py Fri Apr 30 15:45:32 2010 -0400
+++ b/lib/galaxy/webapps/community/model/mapping.py Fri Apr 30 16:11:17 2010 -0400
@@ -103,9 +103,10 @@
Tool.table = Table( "tool", metadata,
Column( "id", Integer, primary_key=True ),
Column( "guid", TrimmedString( 255 ), index=True, unique=True ),
- Column( "tool_id", TrimmedString( 255 ), index=True, unique=True ),
+ Column( "tool_id", TrimmedString( 255 ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "newer_version_id", Integer, ForeignKey( "tool.id" ), nullable=True ),
Column( "name", TrimmedString( 255 ), index=True ),
Column( "description" , TEXT ),
Column( "user_description" , TEXT ),
@@ -215,7 +216,11 @@
properties = dict(
categories=relation( ToolCategoryAssociation ),
events=relation( ToolEventAssociation ),
- user=relation( User.mapper )
+ user=relation( User.mapper ),
+ older_version=relation(
+ Tool,
+ primaryjoin=( Tool.table.c.newer_version_id == Tool.table.c.id ),
+ backref=backref( "newer_version", primaryjoin=( Tool.table.c.newer_version_id == Tool.table.c.id ), remote_side=[Tool.table.c.id] ) )
) )
assign_mapper( context, Event, Event.table,
diff -r e71a3d03a529 -r 42f3f5d78f9a lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py
--- a/lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py Fri Apr 30 15:45:32 2010 -0400
+++ b/lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py Fri Apr 30 16:11:17 2010 -0400
@@ -80,9 +80,10 @@
Tool_table = Table( "tool", metadata,
Column( "id", Integer, primary_key=True ),
Column( "guid", TrimmedString( 255 ), index=True, unique=True ),
- Column( "tool_id", TrimmedString( 255 ), index=True, unique=True ),
+ Column( "tool_id", TrimmedString( 255 ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "newer_version_id", Integer, ForeignKey( "tool.id" ), nullable=True ),
Column( "name", TrimmedString( 255 ), index=True ),
Column( "description" , TEXT ),
Column( "user_description" , TEXT ),
diff -r e71a3d03a529 -r 42f3f5d78f9a lib/galaxy/webapps/community/security/__init__.py
--- a/lib/galaxy/webapps/community/security/__init__.py Fri Apr 30 15:45:32 2010 -0400
+++ b/lib/galaxy/webapps/community/security/__init__.py Fri Apr 30 16:11:17 2010 -0400
@@ -167,6 +167,12 @@
# We currently assume the current user can edit the item if they are the owner (i.e., they
# uploaded the item), and the item is in a NEW state.
return user and user==item.user and item.is_new()
+ def can_upload_new_version( self, user, item, versions ):
+ state_ok = True
+ for version in versions:
+ if version.is_new() or version.is_approved():
+ state_ok = False
+ return user and user==item.user and state_ok
def get_permitted_actions( filter=None ):
'''Utility method to return a subset of RBACAgent's permitted actions'''
diff -r e71a3d03a529 -r 42f3f5d78f9a templates/webapps/community/tool/view_tool.mako
--- a/templates/webapps/community/tool/view_tool.mako Fri Apr 30 15:45:32 2010 -0400
+++ b/templates/webapps/community/tool/view_tool.mako Fri Apr 30 16:11:17 2010 -0400
@@ -6,6 +6,12 @@
if cntrller in [ 'tool' ]:
can_edit = trans.app.security_agent.can_edit_item( trans.user, tool )
+ can_upload_new_version = trans.app.security_agent.can_upload_new_version( trans.user, tool, versions )
+
+ visible_versions = []
+ for version in versions:
+ if version.is_approved() or version.is_archived() or version.user == trans.user:
+ visible_versions.append( version )
%>
<%!
@@ -49,10 +55,26 @@
<h2>View Tool: ${tool.name} <em>${tool.description}</em></h2>
+%if tool.is_approved():
+ <b><i>This is the latest approved version of this tool</i></b>
+%elif tool.is_deleted():
+ <font color="red"><b><i>This is a deleted version of this tool</i></b></font>
+%elif tool.is_archived():
+ <font color="red"><b><i>This is an archived version of this tool</i></b></font>
+%elif tool.is_new():
+ <font color="red"><b><i>This is an unsubmitted version of this tool</i></b></font>
+%elif tool.is_waiting():
+ <font color="red"><b><i>This version of this tool is awaiting administrative approval</i></b></font>
+%elif tool.is_rejected():
+ <font color="red"><b><i>This version of this tool has been rejected by an administrator</i></b></font>
+%endif
+<p/>
+
%if cntrller=='admin' and tool.is_waiting():
<p>
<ul class="manage-table-actions">
<li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.APPROVED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}"><span>Approve</span></a></li>
+ <li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.REJECTED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}"><span>Reject</span></a></li>
</ul>
</p>
%endif
@@ -67,6 +89,8 @@
<div popupmenu="tool-${tool.id}-popup">
%if cntrller=='admin' or can_edit:
<a class="action-button" href="${h.url_for( controller='common', action='edit_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Edit information</a>
+ %endif
+ %if cntrller=='admin' or can_upload_new_version:
<a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a>
%endif
<a class="action-button" href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">Download tool</a>
@@ -100,11 +124,30 @@
</div>
<div class="form-row">
<label>Categories:</label>
- %for category in categories:
- ${category.name}
- %endfor
+ %if categories:
+ %for category in categories:
+ ${category.name}
+ %endfor
+ %else:
+ none set
+ %endif
<div style="clear: both"></div>
</div>
+ %if len( visible_versions ) > 1:
+ <div class="form-row">
+ <label>All Versions:</label>
+ <ul>
+ %for version in visible_versions:
+ %if version == tool:
+ <li><strong>${version.version} (this version)</strong></li>
+ %else:
+ <li><a href="${h.url_for( controller='common', action='view_tool', id=trans.app.security.encode_id( version.id ), cntrller=cntrller )}">${version.version}</a></li>
+ %endif
+ %endfor
+ </ul>
+ <div style="clear: both"></div>
+ </div>
+ %endif
</div>
</div>
diff -r e71a3d03a529 -r 42f3f5d78f9a templates/webapps/community/upload/upload.mako
--- a/templates/webapps/community/upload/upload.mako Fri Apr 30 15:45:32 2010 -0400
+++ b/templates/webapps/community/upload/upload.mako Fri Apr 30 16:11:17 2010 -0400
@@ -22,6 +22,9 @@
<div class="toolFormBody">
## TODO: nginx
<form id="upload_form" name="upload_form" action="${h.url_for( controller='upload', action='upload' )}" enctype="multipart/form-data" method="post">
+ %if replace_id is not None:
+ <input type='hidden' name="replace_id" value="${replace_id}"/>
+ %endif
<div class="form-row">
<label>Upload Type</label>
<div class="form-row-input">
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/e71a3d03a529
changeset: 3725:e71a3d03a529
user: Kelly Vincent <kpvincent(a)bx.psu.edu>
date: Fri Apr 30 15:45:32 2010 -0400
description:
Fix dbkey setting for BWA.
diffstat:
tools/sr_mapping/bwa_wrapper_code.py | 9 ++++-----
1 files changed, 4 insertions(+), 5 deletions(-)
diffs (19 lines):
diff -r 007f175c8b88 -r e71a3d03a529 tools/sr_mapping/bwa_wrapper_code.py
--- a/tools/sr_mapping/bwa_wrapper_code.py Fri Apr 30 15:27:41 2010 -0400
+++ b/tools/sr_mapping/bwa_wrapper_code.py Fri Apr 30 15:45:32 2010 -0400
@@ -2,11 +2,10 @@
def exec_before_job(app, inp_data, out_data, param_dict, tool):
try:
- refFile = param_dict['solidOrSolexa']['solidRefGenomeSource']['indices'].value
+ refFile = param_dict[ 'genomeSource' ][ 'indices' ].value
except:
try:
- refFile = param_dict['solidOrSolexa']['solidRefGenomeSource']['ownFile'].dbkey
+ refFile = param_dict[ 'genomeSource' ][ 'ownFile' ].dbkey
except:
- out_data['output'].set_dbkey('?')
- return
- out_data['output'].set_dbkey(os.path.split(refFile)[1].split('.')[0])
+ refFile = '?'
+ out_data[ 'output' ].set_dbkey( os.path.split( refFile )[1].split( '.' )[0] )
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/007f175c8b88
changeset: 3724:007f175c8b88
user: Kelly Vincent <kpvincent(a)bx.psu.edu>
date: Fri Apr 30 15:27:41 2010 -0400
description:
Change the way size of output file determined in Bowtie and SAM-to_BAM to deal with memory limitation.
diffstat:
tools/samtools/sam_to_bam.py | 2 +-
tools/sr_mapping/bowtie_wrapper.py | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diffs (26 lines):
diff -r 85e05920bada -r 007f175c8b88 tools/samtools/sam_to_bam.py
--- a/tools/samtools/sam_to_bam.py Fri Apr 30 15:20:04 2010 -0400
+++ b/tools/samtools/sam_to_bam.py Fri Apr 30 15:27:41 2010 -0400
@@ -94,7 +94,7 @@
tmp_stderr.close()
if returncode != 0:
raise Exception, stderr
- if len( open( fai_index_file_path ).read().strip() ) == 0:
+ if os.path.getsize( fai_index_file_path ) == 0:
raise Exception, 'Index file empty, there may be an error with your reference file or settings.'
except Exception, e:
#clean up temp files
diff -r 85e05920bada -r 007f175c8b88 tools/sr_mapping/bowtie_wrapper.py
--- a/tools/sr_mapping/bowtie_wrapper.py Fri Apr 30 15:20:04 2010 -0400
+++ b/tools/sr_mapping/bowtie_wrapper.py Fri Apr 30 15:27:41 2010 -0400
@@ -387,8 +387,8 @@
if returncode != 0:
raise Exception, stderr
# check that there are results in the output file
- if len( open( options.output, 'rb' ).read().strip() ) == 0:
- raise Exception, 'The output file is empty, there may be an error with your input file or settings.'
+ if os.path.getsize( options.output ) == 0:
+ raise Exception, 'The output file is empty, there may be an error with your input file or settings.' + '\nextra: ' + str(extra)
except Exception, e:
stop_err( 'Error aligning sequence. ' + str( e ) )
finally:
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/85e05920bada
changeset: 3723:85e05920bada
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Fri Apr 30 15:20:04 2010 -0400
description:
Add a link to the user's uploaded tools from the Manage users grid, and fix some more bugs in the community app.
diffstat:
lib/galaxy/webapps/community/controllers/admin.py | 60 ++++++----------------
templates/webapps/community/index.mako | 4 +-
templates/webapps/community/tool/edit_tool.mako | 1 -
templates/webapps/community/tool/view_tool.mako | 1 -
4 files changed, 20 insertions(+), 46 deletions(-)
diffs (148 lines):
diff -r db4f8befcd89 -r 85e05920bada lib/galaxy/webapps/community/controllers/admin.py
--- a/lib/galaxy/webapps/community/controllers/admin.py Fri Apr 30 14:08:23 2010 -0400
+++ b/lib/galaxy/webapps/community/controllers/admin.py Fri Apr 30 15:20:04 2010 -0400
@@ -18,13 +18,6 @@
if user.username:
return user.username
return 'not set'
- class StatusColumn( grids.GridColumn ):
- def get_value( self, trans, grid, user ):
- if user.purged:
- return "purged"
- elif user.deleted:
- return "deleted"
- return ""
class GroupsColumn( grids.GridColumn ):
def get_value( self, trans, grid, user ):
if user.groups:
@@ -45,6 +38,16 @@
if user.galaxy_sessions:
return self.format( user.galaxy_sessions[ 0 ].update_time )
return 'never'
+ class StatusColumn( grids.GridColumn ):
+ def get_value( self, trans, grid, user ):
+ if user.purged:
+ return "purged"
+ elif user.deleted:
+ return "deleted"
+ return ""
+ class ToolsColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, user ):
+ return '<a href="browse_tools_by_user?operation=browse&id=%s">%s</a>' % ( trans.security.encode_id( user.id ), str( len( user.tools ) ) )
# Grid definition
webapp = "community"
@@ -69,6 +72,10 @@
ExternalColumn( "External", attach_popup=False ),
LastLoginColumn( "Last Login", format=time_ago ),
StatusColumn( "Status", attach_popup=False ),
+ ToolsColumn( "Uploaded Tools",
+ model_class=model.User,
+ attach_popup=False,
+ filterable="advanced" ),
# Columns that are valid for filtering but are not visible.
grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
]
@@ -488,11 +495,7 @@
grids.GridOperation( "Edit information",
condition=( lambda item: not item.deleted ),
allow_multiple=False,
- url_args=dict( controller="common", action="edit_tool", cntrller="admin", webapp="community" ) ),
- grids.GridOperation( "Manage categories",
- condition=( lambda item: not item.deleted ),
- allow_multiple=False,
- url_args=dict( controller="common", action="manage_categories", cntrller="admin", webapp="community" ) )
+ url_args=dict( controller="common", action="edit_tool", cntrller="admin", webapp="community" ) )
]
standard_filters = [
grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ),
@@ -553,7 +556,6 @@
@web.expose
@web.require_admin
def browse_tools_by_user( self, trans, **kwd ):
- # TODO: move this to the common controller as it is in the tool controller as well...
if 'operation' in kwd:
operation = kwd['operation'].lower()
if operation == "browse":
@@ -580,35 +582,8 @@
return trans.response.send_redirect( web.url_for( controller='tool',
action='download_tool',
**kwd ) )
- @web.expose
- @web.require_admin
- def browse_tools_by_user( self, trans, **kwd ):
- # TODO: move this to the common controller as it is in the tool controller as well...
- if 'operation' in kwd:
- operation = kwd['operation'].lower()
- if operation == "browse":
- return trans.response.send_redirect( web.url_for( controller='admin',
- action='browse_tools_by_user',
- **kwd ) )
- elif operation == "browse category":
- return trans.response.send_redirect( web.url_for( controller='common',
- action='browse_category',
- cntrller='admin',
- **kwd ) )
- elif operation == "view tool":
- return trans.response.send_redirect( web.url_for( controller='common',
- action='view_tool',
- cntrller='admin',
- **kwd ) )
- elif operation == "edit tool":
- return trans.response.send_redirect( web.url_for( controller='common',
- action='edit_tool',
- cntrller='admin',
- **kwd ) )
- elif operation == "download tool":
- return trans.response.send_redirect( web.url_for( controller='tool',
- action='download_tool',
- **kwd ) )
+ # Render the list view
+ return self.tool_list_grid( trans, **kwd )
@web.expose
@web.require_admin
def manage_categories( self, trans, **kwd ):
@@ -860,7 +835,6 @@
## ---- Utility methods -------------------------------------------------------
def get_tools_by_state( trans, state ):
- # TODO: this can be written so that it is much cleaner and faster
tool_id = []
if state == trans.model.Tool.states.NEW:
for tool in get_tools( trans ):
diff -r db4f8befcd89 -r 85e05920bada templates/webapps/community/index.mako
--- a/templates/webapps/community/index.mako Fri Apr 30 14:08:23 2010 -0400
+++ b/templates/webapps/community/index.mako Fri Apr 30 15:20:04 2010 -0400
@@ -82,7 +82,9 @@
<div class="toolSectionBg">
<div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_categories' )}">Browse by category</a></div>
<div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_tools' )}">Browse all tools</a></div>
- <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_tools_by_user', operation='browse', id=trans.security.encode_id( trans.user.id ) )}">Browse your tools</a></div>
+ %if trans.user:
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_tools_by_user', operation='browse', id=trans.security.encode_id( trans.user.id ) )}">Browse your tools</a></div>
+ %endif
</div>
</div>
<div class="toolSectionBody">
diff -r db4f8befcd89 -r 85e05920bada templates/webapps/community/tool/edit_tool.mako
--- a/templates/webapps/community/tool/edit_tool.mako Fri Apr 30 14:08:23 2010 -0400
+++ b/templates/webapps/community/tool/edit_tool.mako Fri Apr 30 15:20:04 2010 -0400
@@ -58,7 +58,6 @@
<a id="tool-${tool.id}-popup" class="popup-arrow" style="display: none;">▼</a>
<div popupmenu="tool-${tool.id}-popup">
<a class="action-button" href="${h.url_for( controller='common', action='view_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">View information</a>
- <a class="action-button" href="${h.url_for( controller='common', action='manage_categories', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Manage categories</a>
<a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a>
<a class="action-button" href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">Download tool</a>
</div>
diff -r db4f8befcd89 -r 85e05920bada templates/webapps/community/tool/view_tool.mako
--- a/templates/webapps/community/tool/view_tool.mako Fri Apr 30 14:08:23 2010 -0400
+++ b/templates/webapps/community/tool/view_tool.mako Fri Apr 30 15:20:04 2010 -0400
@@ -67,7 +67,6 @@
<div popupmenu="tool-${tool.id}-popup">
%if cntrller=='admin' or can_edit:
<a class="action-button" href="${h.url_for( controller='common', action='edit_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Edit information</a>
- <a class="action-button" href="${h.url_for( controller='common', action='manage_categories', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Manage categories</a>
<a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a>
%endif
<a class="action-button" href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">Download tool</a>
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/db4f8befcd89
changeset: 3722:db4f8befcd89
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Fri Apr 30 14:08:23 2010 -0400
description:
Many fixes and additional features added to the community.
diffstat:
lib/galaxy/webapps/community/controllers/admin.py | 374 +++++++++++--
lib/galaxy/webapps/community/controllers/common.py | 81 ++-
lib/galaxy/webapps/community/controllers/tool.py | 293 ++++++++--
lib/galaxy/webapps/community/model/__init__.py | 7 +-
lib/galaxy/webapps/community/model/mapping.py | 5 +-
templates/webapps/community/admin/index.mako | 43 +-
templates/webapps/community/category/edit_category.mako | 44 +
templates/webapps/community/category/rename_category.mako | 44 -
templates/webapps/community/index.mako | 62 ++-
templates/webapps/community/tool/browse_tool.mako | 37 -
templates/webapps/community/tool/edit_tool.mako | 2 +-
templates/webapps/community/tool/view_tool.mako | 8 +
12 files changed, 733 insertions(+), 267 deletions(-)
diffs (1467 lines):
diff -r c642fbb301fc -r db4f8befcd89 lib/galaxy/webapps/community/controllers/admin.py
--- a/lib/galaxy/webapps/community/controllers/admin.py Fri Apr 30 13:09:29 2010 -0400
+++ b/lib/galaxy/webapps/community/controllers/admin.py Fri Apr 30 14:08:23 2010 -0400
@@ -2,7 +2,7 @@
from galaxy.webapps.community import model
from galaxy.model.orm import *
from galaxy.web.framework.helpers import time_ago, iff, grids
-from common import get_categories, get_category
+from common import get_categories, get_category, get_tools, get_event, get_tool
import logging
log = logging.getLogger( __name__ )
@@ -200,7 +200,7 @@
return None
def build_initial_query( self, session ):
return session.query( self.model_class )
- def apply_default_filter( self, trans, query, **kwargs ):
+ def apply_default_filter( self, trans, query, **kwd ):
return query.filter( model.Role.type != model.Role.types.PRIVATE )
class GroupListGrid( grids.Grid ):
@@ -280,34 +280,31 @@
def build_initial_query( self, session ):
return session.query( self.model_class )
-class CategoryListGrid( grids.Grid ):
+class ManageCategoryListGrid( grids.Grid ):
class NameColumn( grids.TextColumn ):
def get_value( self, trans, grid, category ):
return category.name
class DescriptionColumn( grids.TextColumn ):
def get_value( self, trans, grid, category ):
return category.description
- class StatusColumn( grids.GridColumn ):
- def get_value( self, trans, grid, category ):
- if category.deleted:
- return "deleted"
- return ""
# Grid definition
webapp = "community"
- title = "Categories"
+ title = "Manage Categories"
model_class = model.Category
template='/webapps/community/category/grid.mako'
default_sort_key = "name"
columns = [
NameColumn( "Name",
key="name",
- link=( lambda item: dict( operation="Edit category", id=item.id, webapp="community" ) ),
+ link=( lambda item: dict( operation="Edit", id=item.id, webapp="community" ) ),
model_class=model.Category,
- attach_popup=True,
+ attach_popup=False,
filterable="advanced" ),
- DescriptionColumn( "Description", attach_popup=False ),
- StatusColumn( "Status", attach_popup=False ),
+ DescriptionColumn( "Description",
+ model_class=model.Category,
+ attach_popup=False,
+ filterable="advanced" ),
# Columns that are valid for filtering but are not visible.
grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
]
@@ -318,13 +315,9 @@
filterable="standard" ) )
global_actions = [
grids.GridAction( "Add new category",
- dict( controller='admin', action='categories', operation='create', webapp="community" ) )
+ dict( controller='admin', action='manage_categories', operation='create', webapp="community" ) )
]
- operations = [ grids.GridOperation( "Rename",
- condition=( lambda item: not item.deleted ),
- allow_multiple=False,
- url_args=dict( webapp="community", action="rename_category" ) ),
- grids.GridOperation( "Delete",
+ operations = [ grids.GridOperation( "Delete",
condition=( lambda item: not item.deleted ),
allow_multiple=True,
url_args=dict( webapp="community", action="mark_category_deleted" ) ),
@@ -349,16 +342,77 @@
def build_initial_query( self, session ):
return session.query( self.model_class )
+class ToolsByCategoryListGrid( grids.Grid ):
+ class NameColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, category ):
+ return category.name
+ class DescriptionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, category ):
+ return category.description
+ class ToolsColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, category ):
+ if category.tools:
+ return len( category.tools )
+ return 0
+
+ # Grid definition
+ webapp = "community"
+ title = "Tools by Category"
+ model_class = model.Category
+ template='/webapps/community/category/grid.mako'
+ default_sort_key = "name"
+ columns = [
+ NameColumn( "Name",
+ key="name",
+ link=( lambda item: dict( operation="Browse Category", id=item.id, webapp="community" ) ),
+ model_class=model.Category,
+ attach_popup=True,
+ filterable="advanced" ),
+ DescriptionColumn( "Description",
+ model_class=model.Category,
+ attach_popup=False,
+ filterable="advanced" ),
+ ToolsColumn( "Tools",
+ model_class=model.Category,
+ attach_popup=False,
+ filterable="advanced" ),
+ # Columns that are valid for filtering but are not visible.
+ grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
+ ]
+ columns.append( grids.MulticolFilterColumn( "Search",
+ cols_to_filter=[ columns[0], columns[1], columns[2] ],
+ key="free-text-search",
+ visible=False,
+ filterable="standard" ) )
+ standard_filters = [
+ grids.GridColumnFilter( "Active", args=dict( deleted=False ) ),
+ grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ),
+ grids.GridColumnFilter( "All", args=dict( deleted='All' ) )
+ ]
+ num_rows_per_page = 50
+ preserve_state = False
+ use_paging = True
+ def get_current_item( self, trans ):
+ return None
+ def build_initial_query( self, session ):
+ return session.query( self.model_class )
+
class ToolListGrid( grids.Grid ):
class NameColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
return tool.name
+ class VersionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.version
+ class DescriptionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.description
class CategoryColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
if tool.categories:
rval = ''
for tca in tool.categories:
- rval = '%s%s<br/>' % ( rval, tca.category.name )
+ rval += '<a href="browse_category?id=%s">%s</a><br/>\n' % ( trans.security.encode_id( tca.category.id ), tca.category.name )
return rval
return 'not set'
class StateColumn( grids.GridColumn ):
@@ -389,7 +443,7 @@
return accepted_filters
class UserColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
- return tool.user.email
+ return '<a href="browse_tools_by_user?operation=browse&id=%s">%s</a>' % ( trans.security.encode_id( tool.user.id ), tool.user.username )
# Grid definition
title = "Tools"
model_class = model.Tool
@@ -402,27 +456,34 @@
link=( lambda item: dict( operation="View Tool", id=item.id, cntrller='admin', webapp="community" ) ),
attach_popup=True,
filterable="advanced" ),
+ VersionColumn( "Version",
+ model_class=model.Tool,
+ attach_popup=False,
+ filterable="advanced" ),
+ DescriptionColumn( "Description",
+ model_class=model.Tool,
+ attach_popup=False,
+ filterable="advanced" ),
CategoryColumn( "Category",
- key="category",
model_class=model.Category,
attach_popup=False,
filterable="advanced" ),
- StateColumn( "State",
- key="state",
+ StateColumn( "Status",
model_class=model.Event,
- attach_popup=False,
- filterable="advanced" ),
+ attach_popup=False ),
+ UserColumn( "Uploaded By",
+ key="username",
+ model_class=model.User,
+ attach_popup=False,
+ filterable="advanced" ),
# Columns that are valid for filtering but are not visible.
- grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
+ grids.DeletedColumn( "Deleted", model_class=model.Tool, key="deleted", visible=False, filterable="advanced" )
]
columns.append( grids.MulticolFilterColumn( "Search",
cols_to_filter=[ columns[0], columns[1] ],
key="free-text-search",
visible=False,
filterable="standard" ) )
- global_actions = [
- grids.GridAction( "Upload tool", dict( controller='upload', action='upload', type='tool' ) )
- ]
operations = [
grids.GridOperation( "Edit information",
condition=( lambda item: not item.deleted ),
@@ -443,57 +504,156 @@
use_paging = True
def build_initial_query( self, session ):
return session.query( self.model_class )
- def apply_default_filter( self, trans, query, **kwargs ):
+ def apply_default_filter( self, trans, query, **kwd ):
+ tool_id = kwd.get( 'tool_id', False )
+ if tool_id:
+ if str( tool_id ).lower() in [ '', 'none' ]:
+ # Return an empty query since the current user cannot view any
+ # tools (possibly due to state not being approved, etc).
+ return query.filter( model.Tool.id == None )
+ tool_id = util.listify( tool_id )
+ query = query.filter( or_( *map( lambda id: self.model_class.id == id, tool_id ) ) )
return query.filter( self.model_class.deleted==False )
-class AdminCommunity( BaseController, Admin ):
+class AdminController( BaseController, Admin ):
user_list_grid = UserListGrid()
role_list_grid = RoleListGrid()
group_list_grid = GroupListGrid()
- category_list_grid = CategoryListGrid()
+ manage_category_list_grid = ManageCategoryListGrid()
+ tools_by_category_list_grid = ToolsByCategoryListGrid()
tool_list_grid = ToolListGrid()
@web.expose
@web.require_admin
- def browse_tools( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
+ def browse_tools( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "edit tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='edit_tool',
+ cntrller='admin',
+ **kwd ) )
+ elif operation == "view tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='view_tool',
+ cntrller='admin',
+ **kwd ) )
+ # Render the list view
+ return self.tool_list_grid( trans, **kwd )
+ @web.expose
+ @web.require_admin
+ def browse_tools_by_category( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "browse category":
+ return self.browse_category( trans, id=kwd['id'] )
+ # Render the list view
+ return self.tools_by_category_list_grid( trans, **kwd )
+ @web.expose
+ @web.require_admin
+ def browse_tools_by_user( self, trans, **kwd ):
+ # TODO: move this to the common controller as it is in the tool controller as well...
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
if operation == "browse":
- return trans.response.send_redirect( web.url_for( controller='tool',
- action='browse_tools',
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_tools_by_user',
cntrller='admin',
- **kwargs ) )
+ **kwd ) )
+ elif operation == "browse category":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_category',
+ cntrller='admin',
+ **kwd ) )
+ elif operation == "view tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='view_tool',
+ cntrller='admin',
+ **kwd ) )
elif operation == "edit tool":
return trans.response.send_redirect( web.url_for( controller='common',
action='edit_tool',
cntrller='admin',
- **kwargs ) )
+ **kwd ) )
+ elif operation == "download tool":
+ return trans.response.send_redirect( web.url_for( controller='tool',
+ action='download_tool',
+ **kwd ) )
+ @web.expose
+ @web.require_admin
+ def browse_tools_by_user( self, trans, **kwd ):
+ # TODO: move this to the common controller as it is in the tool controller as well...
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "browse":
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools_by_user',
+ **kwd ) )
+ elif operation == "browse category":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_category',
+ cntrller='admin',
+ **kwd ) )
elif operation == "view tool":
return trans.response.send_redirect( web.url_for( controller='common',
action='view_tool',
cntrller='admin',
- **kwargs ) )
- # Render the list view
- return self.tool_list_grid( trans, **kwargs )
+ **kwd ) )
+ elif operation == "edit tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='edit_tool',
+ cntrller='admin',
+ **kwd ) )
+ elif operation == "download tool":
+ return trans.response.send_redirect( web.url_for( controller='tool',
+ action='download_tool',
+ **kwd ) )
@web.expose
@web.require_admin
- def categories( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
+ def manage_categories( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
if operation == "create":
- return self.create_category( trans, **kwargs )
+ return self.create_category( trans, **kwd )
if operation == "delete":
- return self.mark_category_deleted( trans, **kwargs )
+ return self.mark_category_deleted( trans, **kwd )
if operation == "undelete":
- return self.undelete_category( trans, **kwargs )
+ return self.undelete_category( trans, **kwd )
if operation == "purge":
- return self.purge_category( trans, **kwargs )
- if operation == "rename":
- return self.rename_category( trans, **kwargs )
+ return self.purge_category( trans, **kwd )
+ if operation == "edit":
+ return self.edit_category( trans, **kwd )
# Render the list view
- return self.category_list_grid( trans, **kwargs )
-
+ return self.manage_category_list_grid( trans, **kwd )
+ @web.expose
+ @web.require_admin
+ def browse_tools_by_state( self, trans, state=None, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ if state is None:
+ id = params.get( 'id', None )
+ if not id:
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ message='Select a status',
+ status='error' ) )
+ event = get_event( trans, id )
+ state = event.state
+ tool_id = get_tools_by_state( trans, state )
+ if not tool_id:
+ tool_id = 'None'
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ tool_id=tool_id ) )
+ @web.expose
+ @web.require_admin
+ def browse_category( self, trans, **kwd ):
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_category',
+ cntrller='admin',
+ **kwd ) )
@web.expose
@web.require_admin
def create_category( self, trans, **kwd ):
@@ -515,7 +675,7 @@
message = "Category '%s' has been created" % category.name
trans.sa_session.flush()
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
@@ -530,42 +690,69 @@
status=status )
@web.expose
@web.require_admin
- def rename_category( self, trans, **kwd ):
+ def set_tool_state( self, trans, state, **kwd ):
params = util.Params( kwd )
webapp = params.get( 'webapp', 'galaxy' )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
id = params.get( 'id', None )
if not id:
- message = "No category ids received for renaming"
+ message = "No tool id received for setting status"
+ status = 'error'
+ else:
+ tool = get_tool( trans, id )
+ event = trans.model.Event( state )
+ # Flush so we an get an id
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ tea = trans.model.ToolEventAssociation( tool, event )
+ trans.sa_session.add( tea )
+ trans.sa_session.flush()
+ message = "State of tool '%s' is now %s" % ( tool.name, state )
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ webapp=webapp,
+ message=message,
+ status=status ) )
+ @web.expose
+ @web.require_admin
+ def edit_category( self, trans, **kwd ):
+ params = util.Params( kwd )
+ webapp = params.get( 'webapp', 'galaxy' )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ message = "No category ids received for editing"
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=message,
status='error' ) )
category = get_category( trans, id )
- if params.get( 'rename_category_button', False ):
+ if params.get( 'edit_category_button', False ):
old_name = category.name
new_name = util.restore_text( params.name )
new_description = util.restore_text( params.description )
- if not new_name:
- message = 'Enter a valid name'
- status = 'error'
- elif trans.sa_session.query( trans.app.model.Category ).filter( trans.app.model.Category.table.c.name==new_name ).first():
- message = 'A category with that name already exists'
- status = 'error'
+ if old_name != new_name:
+ if not new_name:
+ message = 'Enter a valid name'
+ status = 'error'
+ elif trans.sa_session.query( trans.app.model.Category ).filter( trans.app.model.Category.table.c.name==new_name ).first():
+ message = 'A category with that name already exists'
+ status = 'error'
else:
category.name = new_name
category.description = new_description
trans.sa_session.add( category )
trans.sa_session.flush()
- message = "Category '%s' has been renamed to '%s'" % ( old_name, new_name )
+ message = "The information has been saved for category '%s'" % ( new_name )
return trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
- return trans.fill_template( '/webapps/community/category/rename_category.mako',
+ return trans.fill_template( '/webapps/community/category/edit_category.mako',
category=category,
webapp=webapp,
message=message,
@@ -579,7 +766,7 @@
if not id:
message = "No category ids received for deleting"
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=message,
status='error' ) )
@@ -592,7 +779,7 @@
trans.sa_session.flush()
message += " %s " % category.name
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
@@ -605,7 +792,7 @@
if not id:
message = "No category ids received for undeleting"
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=message,
status='error' ) )
@@ -617,7 +804,7 @@
if not category.deleted:
message = "Category '%s' has not been deleted, so it cannot be undeleted." % category.name
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='error' ) )
@@ -628,7 +815,7 @@
undeleted_categories += " %s" % category.name
message = "Undeleted %d categories: %s" % ( count, undeleted_categories )
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
@@ -644,7 +831,7 @@
if not id:
message = "No category ids received for purging"
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='error' ) )
@@ -655,7 +842,7 @@
if not category.deleted:
message = "Category '%s' has not been deleted, so it cannot be purged." % category.name
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='error' ) )
@@ -665,7 +852,42 @@
trans.sa_session.flush()
message += " %s " % category.name
trans.response.send_redirect( web.url_for( controller='admin',
- action='categories',
+ action='manage_categories',
webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
+
+## ---- Utility methods -------------------------------------------------------
+
+def get_tools_by_state( trans, state ):
+ # TODO: this can be written so that it is much cleaner and faster
+ tool_id = []
+ if state == trans.model.Tool.states.NEW:
+ for tool in get_tools( trans ):
+ if tool.is_new():
+ tool_id.append( tool.id )
+ elif state == trans.model.Tool.states.ERROR:
+ for tool in get_tools( trans ):
+ if tool.is_error():
+ tool_id.append( tool.id )
+ elif state == trans.model.Tool.states.DELETED:
+ for tool in get_tools( trans ):
+ if tool.is_deleted():
+ tool_id.append( tool.id )
+ elif state == trans.model.Tool.states.WAITING:
+ for tool in get_tools( trans ):
+ if tool.is_waiting():
+ tool_id.append( tool.id )
+ elif state == trans.model.Tool.states.APPROVED:
+ for tool in get_tools( trans ):
+ if tool.is_approved():
+ tool_id.append( tool.id )
+ elif state == trans.model.Tool.states.REJECTED:
+ for tool in get_tools( trans ):
+ if tool.is_rejected():
+ tool_id.append( tool.id )
+ elif state == trans.model.Tool.states.ARCHIVED:
+ for tool in get_tools( trans ):
+ if tool.is_archived():
+ tool_id.append( tool.id )
+ return tool_id
diff -r c642fbb301fc -r db4f8befcd89 lib/galaxy/webapps/community/controllers/common.py
--- a/lib/galaxy/webapps/community/controllers/common.py Fri Apr 30 13:09:29 2010 -0400
+++ b/lib/galaxy/webapps/community/controllers/common.py Fri Apr 30 14:08:23 2010 -0400
@@ -8,7 +8,7 @@
import logging
log = logging.getLogger( __name__ )
-class CommunityCommon( BaseController ):
+class CommonController( BaseController ):
@web.expose
def edit_tool( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
@@ -108,6 +108,54 @@
action='browse_tools',
message='Not yet implemented, sorry...',
status='error' ) )
+ @web.expose
+ def browse_category( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_categories',
+ message='Select a category',
+ status='error' ) )
+ category = get_category( trans, id )
+ # If request came from the tool controller, then we need to filter by the state of the
+ # tool in addition to the category.
+ if cntrller == 'tool':
+ tool_id = get_approved_tools( trans, category=category )
+ else:
+ # If request came from the admin controller, we don't filter on tool state.
+ tool_id = [ tca.tool.id for tca in category.tools ]
+ if not tool_id:
+ tool_id = 'None'
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ tool_id=tool_id ) )
+ @web.expose
+ def browse_tools_by_user( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ message='Select a user',
+ status='error' ) )
+ user = get_user( trans, id )
+ # If request came from the tool controller, then we need to filter by the state of the
+ # tool if the user is not viewing his own tools
+ if cntrller == 'tool':
+ tool_id = get_approved_tools( trans, user=user )
+ else:
+ # If request came from the admin controller, we don't filter on tool state.
+ tool_id = [ tool.id for tool in user.tools ]
+ if not tool_id:
+ tool_id = 'None'
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools_by_user',
+ tool_id=tool_id ) )
## ---- Utility methods -------------------------------------------------------
@@ -142,4 +190,33 @@
obj.categories.append( trans.model.ToolCategoryAssociation( obj, category ) )
def get_tool( trans, id ):
return trans.sa_session.query( trans.model.Tool ).get( trans.app.security.decode_id( id ) )
-
+def get_tools( trans ):
+ return trans.sa_session.query( trans.model.Tool ).order_by( trans.model.Tool.name )
+def get_approved_tools( trans, category=None, user=None ):
+ tool_id = []
+ if category:
+ # Return only the approved tools in the category
+ for tca in category.tools:
+ tool = tca.tool
+ if tool.is_approved():
+ tool_id.append( tool.id )
+ elif user:
+ if trans.user == user:
+ # If the current user is browsing his own tools, then don't filter on state
+ tool_id = [ tool.id for tool in user.tools ]
+ else:
+ # The current user is viewing all tools uploaded by another user, so show only
+ # approved tools
+ for tool in user.active_tools:
+ if tool.is_approved():
+ tool_id.append( tool.id )
+ else:
+ # Return all approved tools
+ for tool in get_tools( trans ):
+ if tool.is_approved():
+ tool_id.append( tool.id )
+ return tool_id
+def get_event( trans, id ):
+ return trans.sa_session.query( trans.model.Event ).get( trans.security.decode_id( id ) )
+def get_user( trans, id ):
+ return trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( id ) )
diff -r c642fbb301fc -r db4f8befcd89 lib/galaxy/webapps/community/controllers/tool.py
--- a/lib/galaxy/webapps/community/controllers/tool.py Fri Apr 30 13:09:29 2010 -0400
+++ b/lib/galaxy/webapps/community/controllers/tool.py Fri Apr 30 14:08:23 2010 -0400
@@ -15,38 +15,111 @@
class NameColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
return tool.name
+ class VersionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.version
+ class DescriptionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.description
class CategoryColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
if tool.categories:
rval = ''
for tca in tool.categories:
- rval += '%s<br/>\n' % tca.category.name
+ rval += '<a href="browse_category?id=%s">%s</a><br/>\n' % ( trans.security.encode_id( tca.category.id ), tca.category.name )
return rval
return 'not set'
- def filter( self, trans, user, query, column_filter ):
- # Category.name conflicts with Tool.name, so we have to make our own filter
- def get_single_filter( filter ):
- return func.lower( model.Category.name ).like( "%" + filter.lower() + "%" )
- if column_filter == 'All':
- pass
- elif isinstance( column_filter, list ):
- clause_list = []
- for filter in column_filter:
- clause_list.append( get_single_filter( filter ) )
- query = query.filter( or_( *clause_list ) )
- else:
- query = query.filter( get_single_filter( column_filter ) )
- return query
- def get_link( self, trans, grid, tool, filter_params ):
+ class UserColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return '<a href="browse_tools_by_user?operation=browse&id=%s">%s</a>' % ( trans.security.encode_id( tool.user.id ), tool.user.username )
+ # Grid definition
+ title = "Tools"
+ model_class = model.Tool
+ template='/webapps/community/tool/grid.mako'
+ default_sort_key = "name"
+ columns = [
+ NameColumn( "Name",
+ key="name",
+ model_class=model.Tool,
+ link=( lambda item: dict( operation="View Tool", id=item.id, cntrller='tool', webapp="community" ) ),
+ attach_popup=True,
+ filterable="advanced" ),
+ VersionColumn( "Version",
+ model_class=model.Tool,
+ attach_popup=False,
+ filterable="advanced" ),
+ DescriptionColumn( "Description",
+ model_class=model.Tool,
+ attach_popup=False,
+ filterable="advanced" ),
+ CategoryColumn( "Categories",
+ model_class=model.Category,
+ attach_popup=False,
+ filterable="advanced" ),
+ UserColumn( "Uploaded By",
+ key="username",
+ model_class=model.User,
+ attach_popup=False,
+ filterable="advanced" ),
+ # Columns that are valid for filtering but are not visible.
+ grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
+ ]
+ columns.append( grids.MulticolFilterColumn( "Search",
+ cols_to_filter=[ columns[0], columns[1] ],
+ key="free-text-search",
+ visible=False,
+ filterable="standard" ) )
+ operations = [
+ grids.GridOperation( "Download tool",
+ condition=( lambda item: not item.deleted ),
+ allow_multiple=False,
+ url_args=dict( controller="tool", action="download_tool", cntrller="tool", webapp="community" ) )
+ ]
+ standard_filters = [
+ grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ),
+ grids.GridColumnFilter( "All", args=dict( deleted='All' ) )
+ ]
+ default_filter = dict( name="All", deleted="False", username="All" )
+ num_rows_per_page = 50
+ preserve_state = False
+ use_paging = True
+ def build_initial_query( self, session ):
+ return session.query( self.model_class )
+ def apply_default_filter( self, trans, query, **kwd ):
+ def filter_query( query, tool_id ):
+ if str( tool_id ).lower() in [ '', 'none' ]:
+ # Return an empty query since the current user cannot view any
+ # tools (possibly due to state not being approved, etc).
+ return query.filter( model.Tool.id == None )
+ tool_id = util.listify( tool_id )
+ query = query.filter( or_( *map( lambda id: self.model_class.id == id, tool_id ) ) )
+ return query.filter( self.model_class.deleted==False )
+ tool_id = kwd.get( 'tool_id', False )
+ if not tool_id:
+ # Display only approved tools
+ tool_id = get_approved_tools( trans )
+ if not tool_id:
+ tool_id = 'None'
+ return filter_query( query, tool_id )
+
+class ToolsByUserListGrid( grids.Grid ):
+ class NameColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.name
+ class VersionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.version
+ class DescriptionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ return tool.description
+ class CategoryColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
if tool.categories:
- filter_params['f-category'] = []
+ rval = ''
for tca in tool.categories:
- filter_params['f-category'].append( tca.category.name )
- if len( filter_params['f-category'] ) == 1:
- filter_params['f-category'] = filter_params['f-category'][0]
- filter_params['advanced-search'] = 'True'
- return filter_params
- return None
+ rval += '<a href="browse_category?id=%s">%s</a><br/>\n' % ( trans.security.encode_id( tca.category.id ), tca.category.name )
+ return rval
+ return 'not set'
class StateColumn( grids.GridColumn ):
def get_value( self, trans, grid, tool ):
state = tool.state()
@@ -75,13 +148,9 @@
return accepted_filters
class UserColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
- return tool.user.username
- def get_link( self, trans, grid, tool, filter_params ):
- filter_params['f-username'] = tool.user.username
- filter_params['advanced-search'] = 'True'
- return filter_params
+ return '<a href="browse_tools_by_user?operation=browse&id=%s">%s</a>' % ( trans.security.encode_id( tool.user.id ), tool.user.username )
# Grid definition
- title = "Tools"
+ title = "Tools By User"
model_class = model.Tool
template='/webapps/community/tool/grid.mako'
default_sort_key = "name"
@@ -92,21 +161,26 @@
link=( lambda item: dict( operation="View Tool", id=item.id, cntrller='tool', webapp="community" ) ),
attach_popup=True,
filterable="advanced" ),
- CategoryColumn( "Categories",
- key="category",
+ VersionColumn( "Version",
model_class=model.Tool,
attach_popup=False,
filterable="advanced" ),
+ DescriptionColumn( "Description",
+ model_class=model.Tool,
+ attach_popup=False,
+ filterable="advanced" ),
+ CategoryColumn( "Categories",
+ model_class=model.Category,
+ attach_popup=False,
+ filterable="advanced" ),
+ StateColumn( "Status",
+ model_class=model.Event,
+ attach_popup=False ),
UserColumn( "Uploaded By",
key="username",
model_class=model.User,
attach_popup=False,
filterable="advanced" ),
- StateColumn( "State",
- key="state",
- model_class=model.Event,
- attach_popup=False,
- filterable="advanced" ),
# Columns that are valid for filtering but are not visible.
grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
]
@@ -130,18 +204,44 @@
preserve_state = False
use_paging = True
def build_initial_query( self, session ):
- return session.query( self.model_class ).outerjoin( model.ToolCategoryAssociation ).outerjoin( model.Category )
- def apply_default_filter( self, trans, query, **kwargs ):
- return query.filter( self.model_class.deleted==False )
+ return session.query( self.model_class )
+ def apply_default_filter( self, trans, query, **kwd ):
+ def filter_query( query, tool_id ):
+ if str( tool_id ).lower() in [ '', 'none' ]:
+ # Return an empty query since the current user cannot view any
+ # tools (possibly due to state not being approved, etc).
+ return query.filter( model.Tool.id == None )
+ tool_id = util.listify( tool_id )
+ query = query.filter( or_( *map( lambda id: self.model_class.id == id, tool_id ) ) )
+ return query.filter( self.model_class.deleted==False )
+ tool_id = kwd.get( 'tool_id', False )
+ if not tool_id:
+ # Display only approved tools
+ tool_id = get_approved_tools( trans )
+ if not tool_id:
+ tool_id = 'None'
+ return filter_query( query, tool_id )
-class ToolCategoryListGrid( grids.Grid ):
+class CategoryListGrid( grids.Grid ):
class NameColumn( grids.TextColumn ):
def get_value( self, trans, grid, category ):
return category.name
class DescriptionColumn( grids.TextColumn ):
def get_value( self, trans, grid, category ):
return category.description
+ class ToolsColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, category ):
+ if category.tools:
+ viewable_tools = 0
+ for tca in category.tools:
+ tool = tca.tool
+ if tool.is_approved():
+ viewable_tools += 1
+ return viewable_tools
+ return 0
+
# Grid definition
+ webapp = "community"
title = "Tool Categories"
model_class = model.Category
template='/webapps/community/category/grid.mako'
@@ -157,7 +257,13 @@
key="description",
model_class=model.Category,
attach_popup=False,
- filterable="advanced" )
+ filterable="advanced" ),
+ ToolsColumn( "Tools",
+ model_class=model.Tool,
+ attach_popup=False,
+ filterable="advanced" ),
+ # Columns that are valid for filtering but are not visible.
+ grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" )
]
columns.append( grids.MulticolFilterColumn( "Search",
cols_to_filter=[ columns[0], columns[1] ],
@@ -165,21 +271,23 @@
visible=False,
filterable="standard" ) )
standard_filters = [
+ grids.GridColumnFilter( "Active", args=dict( deleted=False ) ),
+ grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ),
grids.GridColumnFilter( "All", args=dict( deleted='All' ) )
]
- default_filter = dict( name="All", deleted="False" )
num_rows_per_page = 50
preserve_state = False
use_paging = True
def build_initial_query( self, session ):
return session.query( self.model_class )
- def apply_default_filter( self, trans, query, **kwargs ):
+ def apply_default_filter( self, trans, query, **kwd ):
return query.filter( self.model_class.deleted==False )
-class ToolBrowserController( BaseController ):
-
- tool_category_list_grid = ToolCategoryListGrid()
+class ToolController( BaseController ):
+
tool_list_grid = ToolListGrid()
+ tools_by_user_list_grid = ToolsByUserListGrid()
+ category_list_grid = CategoryListGrid()
@web.expose
def index( self, trans, **kwd ):
@@ -188,53 +296,84 @@
status = params.get( 'status', 'done' )
return trans.fill_template( '/webapps/community/index.mako', message=message, status=status )
@web.expose
- def browse_tool_categories( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
+ def browse_categories( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
if operation == "browse category":
- category_id = int( trans.app.security.decode_id( kwargs['id'] ) )
- category = trans.sa_session.query( model.Category ).get( category_id )
- del kwargs['id']
- del kwargs['operation']
- kwargs['f-category'] = category.name
- return trans.response.send_redirect( web.url_for( controller='tool',
- action='browse_tools',
- **kwargs ) )
- return self.tool_category_list_grid( trans, **kwargs )
+ return self.browse_category( trans, id=kwd['id'] )
+ elif operation == "view tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='view_category',
+ cntrller='tool',
+ **kwd ) )
+ elif operation == "edit tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='edit_category',
+ cntrller='tool',
+ **kwd ) )
+ # Render the list view
+ return self.category_list_grid( trans, **kwd )
@web.expose
- def browse_tools( self, trans, **kwargs ):
- if 'operation' in kwargs:
- operation = kwargs['operation'].lower()
- if operation == "browse":
- return trans.response.send_redirect( web.url_for( controller='tool',
- action='browse_tool',
+ def browse_category( self, trans, **kwd ):
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_category',
+ cntrller='tool',
+ **kwd ) )
+ @web.expose
+ def browse_tools( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "browse category":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_category',
cntrller='tool',
- **kwargs ) )
+ **kwd ) )
elif operation == "view tool":
return trans.response.send_redirect( web.url_for( controller='common',
action='view_tool',
cntrller='tool',
- **kwargs ) )
+ **kwd ) )
elif operation == "edit tool":
return trans.response.send_redirect( web.url_for( controller='common',
action='edit_tool',
cntrller='tool',
- **kwargs ) )
+ **kwd ) )
elif operation == "download tool":
return trans.response.send_redirect( web.url_for( controller='tool',
action='download_tool',
- **kwargs ) )
+ **kwd ) )
# Render the list view
- return self.tool_list_grid( trans, **kwargs )
+ return self.tool_list_grid( trans, **kwd )
@web.expose
- def browse_tool( self, trans, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- return trans.fill_template( '/webapps/community/tool/browse_tool.mako',
- tools=tools,
- message=message,
- status=status )
+ def browse_tools_by_user( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "browse":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_tools_by_user',
+ cntrller='tool',
+ **kwd ) )
+ elif operation == "browse category":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='browse_category',
+ cntrller='tool',
+ **kwd ) )
+ elif operation == "view tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='view_tool',
+ cntrller='tool',
+ **kwd ) )
+ elif operation == "edit tool":
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='edit_tool',
+ cntrller='tool',
+ **kwd ) )
+ elif operation == "download tool":
+ return trans.response.send_redirect( web.url_for( controller='tool',
+ action='download_tool',
+ **kwd ) )
+ # Render the list view
+ return self.tools_by_user_list_grid( trans, **kwd )
@web.expose
def download_tool( self, trans, **kwd ):
params = util.Params( kwd )
diff -r c642fbb301fc -r db4f8befcd89 lib/galaxy/webapps/community/model/__init__.py
--- a/lib/galaxy/webapps/community/model/__init__.py Fri Apr 30 13:09:29 2010 -0400
+++ b/lib/galaxy/webapps/community/model/__init__.py Fri Apr 30 14:08:23 2010 -0400
@@ -92,7 +92,8 @@
DELETED = 'deleted',
WAITING = 'waiting for approval',
APPROVED = 'approved',
- REJECTED = 'rejected' )
+ REJECTED = 'rejected',
+ ARCHIVED = 'archived' )
def __init__( self, guid=None, tool_id=None, name=None, description=None, user_description=None, category=None, version=None, user_id=None, external_filename=None ):
self.guid = guid
self.tool_id = tool_id
@@ -153,10 +154,14 @@
return self.state() == self.states.ERROR
def is_deleted( self ):
return self.state() == self.states.DELETED
+ def is_waiting( self ):
+ return self.state() == self.states.WAITING
def is_approved( self ):
return self.state() == self.states.APPROVED
def is_rejected( self ):
return self.state() == self.states.REJECTED
+ def is_archived( self ):
+ return self.state() == self.states.ARCHIVED
@property
def extension( self ):
# if instantiated via a query, this unmapped property won't exist
diff -r c642fbb301fc -r db4f8befcd89 lib/galaxy/webapps/community/model/mapping.py
--- a/lib/galaxy/webapps/community/model/mapping.py Fri Apr 30 13:09:29 2010 -0400
+++ b/lib/galaxy/webapps/community/model/mapping.py Fri Apr 30 14:08:23 2010 -0400
@@ -164,8 +164,8 @@
# With the tables defined we can define the mappers and setup the
# relationships between the model objects.
assign_mapper( context, User, User.table,
- properties=dict( tools=relation( Tool, order_by=desc( Tool.table.c.update_time ) ),
- active_tools=relation( Tool, primaryjoin=( ( Tool.table.c.user_id == User.table.c.id ) & ( not_( Tool.table.c.deleted ) ) ), order_by=desc( Tool.table.c.update_time ) ),
+ properties=dict( tools=relation( Tool, primaryjoin=( Tool.table.c.user_id == User.table.c.id ), order_by=( Tool.table.c.name ) ),
+ active_tools=relation( Tool, primaryjoin=( ( Tool.table.c.user_id == User.table.c.id ) & ( not_( Tool.table.c.deleted ) ) ), order_by=( Tool.table.c.name ) ),
galaxy_sessions=relation( GalaxySession, order_by=desc( GalaxySession.table.c.update_time ) ) ) )
assign_mapper( context, Group, Group.table,
@@ -238,7 +238,6 @@
)
)
-
def guess_dialect_for_url( url ):
return (url.split(':', 1))[0]
diff -r c642fbb301fc -r db4f8befcd89 templates/webapps/community/admin/index.mako
--- a/templates/webapps/community/admin/index.mako Fri Apr 30 13:09:29 2010 -0400
+++ b/templates/webapps/community/admin/index.mako Fri Apr 30 14:08:23 2010 -0400
@@ -54,7 +54,6 @@
</style>
</%def>
-
<%def name="init()">
<%
self.has_left_panel=True
@@ -71,32 +70,34 @@
<div class="toolMenu">
<div class="toolSectionList">
<div class="toolSectionTitle">
+ <span>Tools</span>
+ </div>
+ <div class="toolSectionBody">
+ <div class="toolSectionBg">
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='browse_tools_by_state', state=trans.model.Tool.states.WAITING, webapp='community' )}">Tools awaiting approval</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='browse_tools_by_category' )}">Browse by category</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='browse_tools', webapp='community' )}">Browse all tools</a></div>
+ </div>
+ </div>
+ <div class="toolSectionPad"></div>
+ <div class="toolSectionTitle">
+ <span>Categories</span>
+ </div>
+ <div class="toolSectionBody">
+ <div class="toolSectionBg">
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='manage_categories', webapp='community' )}">Manage categories</a></div>
+ </div>
+ </div>
+ <div class="toolSectionTitle">
<span>Security</span>
</div>
<div class="toolSectionBody">
<div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='users', webapp='community' )}" target="galaxy_main">Manage users</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='groups', webapp='community' )}" target="galaxy_main">Manage groups</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='roles', webapp='community' )}" target="galaxy_main">Manage roles</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='users', webapp='community' )}">Manage users</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='groups', webapp='community' )}">Manage groups</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='admin', action='roles', webapp='community' )}">Manage roles</a></div>
</div>
</div>
- <div class="toolSectionTitle">
- <span>Tools</span>
- </div>
- <div class="toolSectionBody">
- <div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='browse_tools', webapp='community' )}" target="galaxy_main">Manage tools</a></div>
- </div>
- </div>
- <div class="toolSectionPad"></div>
- <div class="toolSectionTitle">
- <span>Community</span>
- </div>
- <div class="toolSectionBody">
- <div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='admin', action='categories', webapp='community' )}" target="galaxy_main">Manage categories</a></div>
- </div>
- </div>
</div>
</div>
</div>
diff -r c642fbb301fc -r db4f8befcd89 templates/webapps/community/category/edit_category.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/webapps/community/category/edit_category.mako Fri Apr 30 14:08:23 2010 -0400
@@ -0,0 +1,44 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolForm">
+ <div class="toolFormTitle">Change category name and description</div>
+ <div class="toolFormBody">
+ <form name="library" action="${h.url_for( controller='admin', action='edit_category' )}" method="post" >
+ <div class="form-row">
+ <input name="webapp" type="hidden" value="${webapp}" size=40"/>
+ <label>Name:</label>
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input type="text" name="name" value="${category.name}" size="40"/>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Description:</label>
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input name="description" type="textfield" value="${category.description}" size=40"/>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input type="hidden" name="rename" value="submitted"/>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input type="hidden" name="id" value="${trans.security.encode_id( category.id )}"/>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <input type="submit" name="edit_category_button" value="Save"/>
+ </div>
+ </form>
+ </div>
+</div>
diff -r c642fbb301fc -r db4f8befcd89 templates/webapps/community/category/rename_category.mako
--- a/templates/webapps/community/category/rename_category.mako Fri Apr 30 13:09:29 2010 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-<div class="toolForm">
- <div class="toolFormTitle">Change category name and description</div>
- <div class="toolFormBody">
- <form name="library" action="${h.url_for( controller='admin', action='rename_category' )}" method="post" >
- <div class="form-row">
- <input name="webapp" type="hidden" value="${webapp}" size=40"/>
- <label>Name:</label>
- <div style="float: left; width: 250px; margin-right: 10px;">
- <input type="text" name="name" value="${category.name}" size="40"/>
- </div>
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <label>Description:</label>
- <div style="float: left; width: 250px; margin-right: 10px;">
- <input name="description" type="textfield" value="${category.description}" size=40"/>
- </div>
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <div style="float: left; width: 250px; margin-right: 10px;">
- <input type="hidden" name="rename" value="submitted"/>
- </div>
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <div style="float: left; width: 250px; margin-right: 10px;">
- <input type="hidden" name="id" value="${trans.security.encode_id( category.id )}"/>
- </div>
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <input type="submit" name="rename_category_button" value="Save"/>
- </div>
- </form>
- </div>
-</div>
diff -r c642fbb301fc -r db4f8befcd89 templates/webapps/community/index.mako
--- a/templates/webapps/community/index.mako Fri Apr 30 13:09:29 2010 -0400
+++ b/templates/webapps/community/index.mako Fri Apr 30 14:08:23 2010 -0400
@@ -1,6 +1,57 @@
<%inherit file="/webapps/community/base_panels.mako"/>
<%namespace file="/message.mako" import="render_msg" />
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ## TODO: Clean up these styles and move into panel_layout.css (they are
+ ## used here and in the editor).
+ <style type="text/css">
+ #left {
+ background: #C1C9E5 url(${h.url_for('/static/style/menu_bg.png')}) top repeat-x;
+ }
+ div.toolMenu {
+ margin: 5px;
+ margin-left: 10px;
+ margin-right: 10px;
+ }
+ div.toolSectionPad {
+ margin: 0;
+ padding: 0;
+ height: 5px;
+ font-size: 0px;
+ }
+ div.toolSectionDetailsInner {
+ margin-left: 5px;
+ margin-right: 5px;
+ }
+ div.toolSectionTitle {
+ padding-bottom: 0px;
+ font-weight: bold;
+ }
+ div.toolMenuGroupHeader {
+ font-weight: bold;
+ padding-top: 0.5em;
+ padding-bottom: 0.5em;
+ color: #333;
+ font-style: italic;
+ border-bottom: dotted #333 1px;
+ margin-bottom: 0.5em;
+ }
+ div.toolTitle {
+ padding-top: 5px;
+ padding-bottom: 5px;
+ margin-left: 16px;
+ margin-right: 10px;
+ display: list-item;
+ list-style: square outside;
+ }
+ a:link, a:visited, a:active
+ {
+ color: #303030;
+ }
+ </style>
+</%def>
+
<%def name="init()">
<%
self.has_left_panel=True
@@ -29,17 +80,18 @@
</div>
<div class="toolSectionBody">
<div class="toolSectionBg">
- <div class="toolTitle"><a href="${h.url_for( controller='tool', action='browse_tool_categories' )}" target="galaxy_main">Browse tools by category</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='tool', action='browse_tools' )}" target="galaxy_main">Browse all tools</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_categories' )}">Browse by category</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_tools' )}">Browse all tools</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_tools_by_user', operation='browse', id=trans.security.encode_id( trans.user.id ) )}">Browse your tools</a></div>
</div>
</div>
<div class="toolSectionBody">
<div class="toolSectionBg">
<div class="toolTitle">
%if trans.user:
- <a href="${h.url_for( controller='upload', action='upload', type='tool' )}" target="galaxy_main">Upload a tool</a>
+ <a target="galaxy_main" href="${h.url_for( controller='upload', action='upload', type='tool' )}">Upload a tool</a>
%else:
- Login to upload
+ <a target="galaxy_main" href="${h.url_for( controller='/user', action='login', webapp='community' )}">Login to upload</a>
%endif
</div>
</div>
@@ -54,7 +106,7 @@
if trans.app.config.require_login and not trans.user:
center_url = h.url_for( controller='user', action='login', message=message, status=status )
else:
- center_url = h.url_for( controller='tool', action='browse_tool_categories', message=message, status=status )
+ center_url = h.url_for( controller='tool', action='browse_categories', message=message, status=status )
%>
<iframe name="galaxy_main" id="galaxy_main" frameborder="0" style="position: absolute; width: 100%; height: 100%;" src="${center_url}"> </iframe>
</%def>
diff -r c642fbb301fc -r db4f8befcd89 templates/webapps/community/tool/browse_tool.mako
--- a/templates/webapps/community/tool/browse_tool.mako Fri Apr 30 13:09:29 2010 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-<%namespace file="/message.mako" import="render_msg" />
-
-<%!
- def inherit(context):
- if context.get('use_panels'):
- return '/webapps/community/base_panels.mako'
- else:
- return '/base.mako'
-%>
-<%inherit file="${inherit(context)}"/>
-
-<%def name="title()">Browse Tool</%def>
-
-<h2>Galaxy Tool</h2>
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-%if not tools:
- There are no tools
-%else:
- <table class="grid">
- <thead>
- <tr>
- <th>Name</th>
- <th>Description</th>
- </tr>
- </thead>
- <tbody>
- <tr class="formRow id="toolRow">
- <td><a href="${h.url_for( controller='tool', action='browse', id=trans.security.encode_id( tool.id ) )}">${tool.name}</a></td>
- <td>${tool.description}</td>
- </tr>
- </tbody>
- </table>
-%endif
diff -r c642fbb301fc -r db4f8befcd89 templates/webapps/community/tool/edit_tool.mako
--- a/templates/webapps/community/tool/edit_tool.mako Fri Apr 30 13:09:29 2010 -0400
+++ b/templates/webapps/community/tool/edit_tool.mako Fri Apr 30 14:08:23 2010 -0400
@@ -123,7 +123,7 @@
<div class="toolParamHelp" style="clear: both;">
Tools must be approved before they are made available to others in the community. After you have submitted
your tool to be published, you will no longer be able to modify it, so make sure the information above is
- correct and has been saved before submitting for approval.
+ correct and and save any changes before submitting for approval.
</div>
</div>
</form>
diff -r c642fbb301fc -r db4f8befcd89 templates/webapps/community/tool/view_tool.mako
--- a/templates/webapps/community/tool/view_tool.mako Fri Apr 30 13:09:29 2010 -0400
+++ b/templates/webapps/community/tool/view_tool.mako Fri Apr 30 14:08:23 2010 -0400
@@ -49,6 +49,14 @@
<h2>View Tool: ${tool.name} <em>${tool.description}</em></h2>
+%if cntrller=='admin' and tool.is_waiting():
+ <p>
+ <ul class="manage-table-actions">
+ <li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.APPROVED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}"><span>Approve</span></a></li>
+ </ul>
+ </p>
+%endif
+
%if message:
${render_msg( message, status )}
%endif
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/c642fbb301fc
changeset: 3721:c642fbb301fc
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Fri Apr 30 13:09:29 2010 -0400
description:
merge
diffstat:
lib/galaxy/datatypes/interval.py | 5 +-
lib/galaxy/web/base/controller.py | 12 +-
lib/galaxy/web/controllers/dataset.py | 12 +-
tools/ngs_rna/tophat_wrapper.py | 216 +++++++++++++++----
tools/ngs_rna/tophat_wrapper.xml | 364 +++++++++++++++++++++++++++++----
5 files changed, 503 insertions(+), 106 deletions(-)
diffs (733 lines):
diff -r 20ba52b9d504 -r c642fbb301fc lib/galaxy/datatypes/interval.py
--- a/lib/galaxy/datatypes/interval.py Fri Apr 30 13:08:33 2010 -0400
+++ b/lib/galaxy/datatypes/interval.py Fri Apr 30 13:09:29 2010 -0400
@@ -831,9 +831,12 @@
except:
return False
-class Gtf( Tabular ):
+class Gtf( Gff ):
"""Tab delimited data in Gtf format"""
file_ext = "gtf"
+
+ def sniff( self, filename ):
+ return False
class Wiggle( Tabular, _RemoteCallMixin ):
"""Tab delimited data in wiggle format"""
diff -r 20ba52b9d504 -r c642fbb301fc lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py Fri Apr 30 13:08:33 2010 -0400
+++ b/lib/galaxy/web/base/controller.py Fri Apr 30 13:09:29 2010 -0400
@@ -122,10 +122,14 @@
""" Get an HDA object by id. """
# DEPRECATION: We still support unencoded ids for backward compatibility
try:
- dataset_id = int( dataset_id )
- except ValueError:
- dataset_id = trans.security.decode_id( dataset_id )
- data = trans.sa_session.query( trans.model.HistoryDatasetAssociation ).get( dataset_id )
+ data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ if data is None:
+ raise ValueError( 'Invalid reference dataset id: %s.' % dataset_id )
+ except:
+ try:
+ data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( int( dataset_id ) )
+ except:
+ data = None
if not data:
raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid dataset id: %s." % str( dataset_id ) )
if check_ownership:
diff -r 20ba52b9d504 -r c642fbb301fc lib/galaxy/web/controllers/dataset.py
--- a/lib/galaxy/web/controllers/dataset.py Fri Apr 30 13:08:33 2010 -0400
+++ b/lib/galaxy/web/controllers/dataset.py Fri Apr 30 13:09:29 2010 -0400
@@ -307,10 +307,14 @@
composite_extensions.append('html') # for archiving composite datatypes
# DEPRECATION: We still support unencoded ids for backward compatibility
try:
- dataset_id = int( dataset_id )
- except ValueError:
- dataset_id = trans.security.decode_id( dataset_id )
- data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dataset_id )
+ data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ if data is None:
+ raise ValueError( 'Invalid reference dataset id: %s.' % dataset_id )
+ except:
+ try:
+ data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( int( dataset_id ) )
+ except:
+ data = None
if not data:
raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % str( dataset_id ) )
current_user_roles = trans.get_current_user_roles()
diff -r 20ba52b9d504 -r c642fbb301fc tools/ngs_rna/tophat_wrapper.py
--- a/tools/ngs_rna/tophat_wrapper.py Fri Apr 30 13:08:33 2010 -0400
+++ b/tools/ngs_rna/tophat_wrapper.py Fri Apr 30 13:09:29 2010 -0400
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-import optparse, os, shutil, sys, tempfile
+import optparse, os, shutil, subprocess, sys, tempfile, fileinput
def stop_err( msg ):
sys.stderr.write( "%s\n" % msg )
@@ -9,72 +9,184 @@
def __main__():
#Parse Command Line
parser = optparse.OptionParser()
- parser.add_option( '-1', '--input1', dest='input1', help='The (forward or single-end) reads file in Sanger FASTQ format' )
- parser.add_option( '-2', '--input2', dest='input2', help='The reverse reads file in Sanger FASTQ format' )
+ parser.add_option( '-p', '--num-threads', dest='num_threads', help='Use this many threads to align reads. The default is 1.' )
+ parser.add_option( '-C', '--coverage-output', dest='coverage_output_file', help='Coverage output file; formate is WIG.' )
+ parser.add_option( '-J', '--junctions-output', dest='junctions_output_file', help='Junctions output file; formate is BED.' )
+ parser.add_option( '-H', '--hits-output', dest='accepted_hits_output_file', help='Accepted hits output file; formate is SAM.' )
+ parser.add_option( '', '--own-file', dest='own_file', help='' )
+ parser.add_option( '-D', '--indexes-path', dest='index_path', help='Indexes directory; location of .ebwt and .fa files.' )
+ parser.add_option( '-r', '--mate-inner-dist', dest='mate_inner_dist', help='This is the expected (mean) inner distance between mate pairs. \
+ For, example, for paired end runs with fragments selected at 300bp, \
+ where each end is 50bp, you should set -r to be 200. There is no default, \
+ and this parameter is required for paired end runs.')
+ parser.add_option( '', '--mate-std-dev', dest='mate_std_dev', help='Standard deviation of distribution on inner distances between male pairs.' )
parser.add_option( '-a', '--min-anchor-length', dest='min_anchor_length',
help='The "anchor length". TopHat will report junctions spanned by reads with at least this many bases on each side of the junction.' )
+ parser.add_option( '-m', '--splice-mismatches', dest='splice_mismatches', help='The maximum number of mismatches that can appear in the anchor region of a spliced alignment.' )
parser.add_option( '-i', '--min-intron-length', dest='min_intron_length',
help='The minimum intron length. TopHat will ignore donor/acceptor pairs closer than this many bases apart.' )
parser.add_option( '-I', '--max-intron-length', dest='max_intron_length',
help='The maximum intron length. When searching for junctions ab initio, TopHat will ignore donor/acceptor pairs farther than this many bases apart, except when such a pair is supported by a split segment alignment of a long read.' )
- parser.add_option( '-s', '--solexa-quals', dest='solexa_quals', help='Use the Solexa scale for quality values in FASTQ files.' )
- parser.add_option( '-S', '--solexa.3-quals', dest='solexa_quals',
- help='As of the Illumina GA pipeline version 1.3, quality scores are encoded in Phred-scaled base-64. Use this option for FASTQ files from pipeline 1.3 or later.' )
- parser.add_option( '-p', '--num-threads', dest='num_threads', help='Use this many threads to align reads. The default is 1.' )
- parser.add_option( '-C', '--coverage-output', dest='coverage_output_file', help='Coverage output file; formate is WIG.' )
- parser.add_option( '-J', '--junctions-output', dest='junctions_output_file', help='Junctions output file; formate is BED.' )
- parser.add_option( '-H', '--hits-output', dest='accepted_hits_output_file', help='Accepted hits output file; formate is SAM.' )
- parser.add_option( '-D', '--indexes-dir', dest='indexes_directory', help='Indexes directory; location of .ebwt and .fa files.' )
- parser.add_option( '-r', '--mate-inner-dist', dest='mate_inner_dist', help='This is the expected (mean) inner distance between mate pairs. \
- For, example, for paired end runs with fragments selected at 300bp, \
- where each end is 50bp, you should set -r to be 200. There is no default, \
- and this parameter is required for paired end runs.')
+ parser.add_option( '-F', '--junction_filter', dest='junction_filter', help='Filter out junctions supported by too few alignments (number of reads divided by average depth of coverage)' )
+ parser.add_option( '-g', '--max_multihits', dest='max_multihits', help='Maximum number of alignments to be allowed' )
+ parser.add_option( '', '--microexon-search', action="store_true", dest='microexon_search', help='With this option, the pipeline will attempt to find alignments incident to microexons. Works only for reads 50bp or longer.')
+ parser.add_option( '', '--closure-search', action="store_true", dest='closure_search', help='Enables the mate pair closure-based search for junctions. Closure-based search should only be used when the expected inner distance between mates is small (<= 50bp)')
+ parser.add_option( '', '--no-closure-search', action="store_false", dest='closure_search' )
+ parser.add_option( '', '--coverage-search', action="store_true", dest='coverage_search', help='Enables the coverage based search for junctions. Use when coverage search is disabled by default (such as for reads 75bp or longer), for maximum sensitivity.')
+ parser.add_option( '', '--no-coverage-search', action="store_false", dest='coverage_search' )
+ parser.add_option( '', '--min-segment-intron', dest='min_segment_intron', help='Minimum intron length that may be found during split-segment search' )
+ parser.add_option( '', '--max-segment-intron', dest='max_segment_intron', help='Maximum intron length that may be found during split-segment search' )
+ parser.add_option( '', '--min-closure-exon', dest='min_closure_exon', help='Minimum length for exonic hops in potential splice graph' )
+ parser.add_option( '', '--min-closure-intron', dest='min_closure_intron', help='Minimum intron length that may be found during closure search' )
+ parser.add_option( '', '--max-closure-intron', dest='max_closure_intron', help='Maximum intron length that may be found during closure search' )
+ parser.add_option( '', '--min-coverage-intron', dest='min_coverage_intron', help='Minimum intron length that may be found during coverage search' )
+ parser.add_option( '', '--max-coverage-intron', dest='max_coverage_intron', help='Maximum intron length that may be found during coverage search' )
+ parser.add_option( '', '--seg-mismatches', dest='seg_mismatches', help='Number of mismatches allowed in each segment alignment for reads mapped independently' )
+ parser.add_option( '', '--seg-length', dest='seg_length', help='Minimum length of read segments' )
+
+ # Wrapper options.
+ parser.add_option( '-1', '--input1', dest='input1', help='The (forward or single-end) reads file in Sanger FASTQ format' )
+ parser.add_option( '-2', '--input2', dest='input2', help='The reverse reads file in Sanger FASTQ format' )
+ parser.add_option( '', '--single-paired', dest='single_paired', help='' )
+ parser.add_option( '', '--settings', dest='settings', help='' )
+
(options, args) = parser.parse_args()
- # Make temp directory for output.
+ #sys.stderr.write('*'*50+'\n'+str(options)+'\n'+'*'*50+'\n')
+
+ # Creat bowtie index if necessary.
+ tmp_index_dir = tempfile.mkdtemp()
+ if options.own_file != 'None':
+ index_path = os.path.join( tmp_index_dir, os.path.split( options.own_file )[1] )
+ cmd_index = 'bowtie-build -f %s %s' % ( options.own_file, index_path )
+ try:
+ tmp = tempfile.NamedTemporaryFile( dir=tmp_index_dir ).name
+ tmp_stderr = open( tmp, 'wb' )
+ proc = subprocess.Popen( args=cmd_index, shell=True, cwd=tmp_index_dir, stderr=tmp_stderr.fileno() )
+ returncode = proc.wait()
+ tmp_stderr.close()
+ # get stderr, allowing for case where it's very large
+ tmp_stderr = open( tmp, 'rb' )
+ stderr = ''
+ buffsize = 1048576
+ try:
+ while True:
+ stderr += tmp_stderr.read( buffsize )
+ if not stderr or len( stderr ) % buffsize != 0:
+ break
+ except OverflowError:
+ pass
+ tmp_stderr.close()
+ if returncode != 0:
+ raise Exception, stderr
+ except Exception, e:
+ if os.path.exists( tmp_index_dir ):
+ shutil.rmtree( tmp_index_dir )
+ stop_err( 'Error indexing reference sequence\n' + str( e ) )
+ else:
+ index_path = options.index_path
+
+ # Build tophat command.
tmp_output_dir = tempfile.mkdtemp()
-
- # Build command.
-
- # Base.
- cmd = "tophat -o %s " % ( tmp_output_dir )
-
- # Add options.
- if options.mate_inner_dist:
- cmd += ( " -r %i" % int ( options.mate_inner_dist ) )
-
- # Add index prefix.
- cmd += " " + options.indexes_directory
-
- # Add input files.
- cmd += " " + options.input1
- if options.mate_inner_dist:
- # Using paired-end reads.
- cmd += " " + options.input2
-
- # Route program output to file.
- cmd += " > %s" % tmp_output_dir + "/std_out.txt"
- # Route program error output to file.
- cmd += " 2> %s" % tmp_output_dir + "/std_err.txt"
-
- # Run.
+ cmd = 'tophat -o %s %s %s %s'
+ reads = options.input1
+ if options.input2 != 'None':
+ reads += ' ' + options.input2
+ opts = '-p %s' % options.num_threads
+ if options.single_paired == 'paired':
+ opts += ' -r %s' % options.mate_inner_dist
+ if options.settings == 'preSet':
+ cmd = cmd % ( tmp_output_dir, opts, index_path, reads )
+ else:
+ try:
+ if int( options.min_anchor_length ) >= 3:
+ opts += ' -a %s' % options.min_anchor_length
+ else:
+ raise Exception, 'Minimum anchor length must be 3 or greater'
+ opts += ' -m %s' % options.splice_mismatches
+ opts += ' -i %s' % options.min_intron_length
+ opts += ' -I %s' % options.max_intron_length
+ if float( options.junction_filter ) != 0.0:
+ opts += ' -F %s' % options.junction_filter
+ opts += ' -g %s' % options.max_multihits
+ if options.coverage_search:
+ opts += ' --coverage-search --min-coverage-intron %s --max-coverage-intron %s' % ( options.min_coverage_intron, options.max_coverage_intron )
+ else:
+ opts += ' --no-coverage-search'
+ if options.closure_search:
+ opts += ' --closure-search --min-closure-exon %s --min-closure-intron %s --max-closure-intron %s' % ( options.min_closure_exon, options.min_closure_intron, options.max_closure_intron )
+ else:
+ opts += ' --no-closure-search'
+ if options.microexon_search:
+ opts += ' --microexon-search'
+ if options.single_paired == 'paired':
+ opts += ' --mate-std-dev %s' % options.mate_std_dev
+ cmd = cmd % ( tmp_output_dir, opts, index_path, reads )
+ except Exception, e:
+ # Clean up temp dirs
+ if os.path.exists( tmp_index_dir ):
+ shutil.rmtree( tmp_index_dir )
+ if os.path.exists( tmp_output_dir ):
+ shutil.rmtree( tmp_output_dir )
+ stop_err( 'Something is wrong with the alignment parameters and the alignment could not be run\n' + str( e ) )
+ print cmd
+
+ # Run
try:
- os.system( cmd )
+ tmp_out = tempfile.NamedTemporaryFile( dir=tmp_output_dir ).name
+ tmp_stdout = open( tmp_out, 'wb' )
+ tmp_err = tempfile.NamedTemporaryFile( dir=tmp_output_dir ).name
+ tmp_stderr = open( tmp_err, 'wb' )
+ proc = subprocess.Popen( args=cmd, shell=True, cwd=tmp_output_dir, stdout=tmp_stdout, stderr=tmp_stderr )
+ returncode = proc.wait()
+ tmp_stderr.close()
+ # get stderr, allowing for case where it's very large
+ tmp_stderr = open( tmp_err, 'rb' )
+ stderr = ''
+ buffsize = 1048576
+ try:
+ while True:
+ stderr += tmp_stderr.read( buffsize )
+ if not stderr or len( stderr ) % buffsize != 0:
+ break
+ except OverflowError:
+ pass
+ tmp_stdout.close()
+ tmp_stderr.close()
+ if returncode != 0:
+ raise Exception, stderr
except Exception, e:
+ # Clean up temp dirs
+ if os.path.exists( tmp_output_dir ):
+ shutil.rmtree( tmp_output_dir )
stop_err( 'Error in tophat:\n' + str( e ) )
# TODO: look for errors in program output.
- # Copy output files from tmp directory to specified files.
+ # Postprocessing: copy output files from tmp directory to specified files. Also need to remove header lines from SAM file.
try:
- shutil.copyfile( tmp_output_dir + "/coverage.wig", options.coverage_output_file )
- shutil.copyfile( tmp_output_dir + "/junctions.bed", options.junctions_output_file )
- shutil.copyfile( tmp_output_dir + "/accepted_hits.sam", options.accepted_hits_output_file )
- except Exception, e:
- stop_err( 'Error in tophat:\n' + str( e ) )
-
- # Clean up temp dirs
- if os.path.exists( tmp_output_dir ):
- shutil.rmtree( tmp_output_dir )
+ try:
+ shutil.copyfile( tmp_output_dir + "/coverage.wig", options.coverage_output_file )
+ shutil.copyfile( tmp_output_dir + "/junctions.bed", options.junctions_output_file )
+
+ # Remove headers from SAM file in place.
+ in_header = True # Headers always at start of file.
+ for line in fileinput.input( tmp_output_dir + "/accepted_hits.sam", inplace=1 ):
+ if in_header and line.startswith("@"):
+ continue
+ else:
+ in_header = False
+ sys.stdout.write( line )
+
+ # Copy SAM File.
+ shutil.copyfile( tmp_output_dir + "/accepted_hits.sam", options.accepted_hits_output_file )
+ except Exception, e:
+ stop_err( 'Error in tophat:\n' + str( e ) )
+ finally:
+ # Clean up temp dirs
+ if os.path.exists( tmp_index_dir ):
+ shutil.rmtree( tmp_index_dir )
+ if os.path.exists( tmp_output_dir ):
+ shutil.rmtree( tmp_output_dir )
if __name__=="__main__": __main__()
diff -r 20ba52b9d504 -r c642fbb301fc tools/ngs_rna/tophat_wrapper.xml
--- a/tools/ngs_rna/tophat_wrapper.xml Fri Apr 30 13:08:33 2010 -0400
+++ b/tools/ngs_rna/tophat_wrapper.xml Fri Apr 30 13:09:29 2010 -0400
@@ -1,24 +1,104 @@
-<tool id="tophat" name="Tophat" version="1.0.13">
+<tool id="tophat" name="Tophat" version="1.0.0">
<description>Find splice junctions using RNA-seq data</description>
<command interpreter="python">
- tophat_wrapper.py
+ tophat_wrapper.py
+ ## Change this to accomodate the number of threads you have available.
--num-threads="4"
- --coverage-output=$coverage
- --junctions-output=$junctions
+
+ ## Provide outputs.
+ --coverage-output=$coverage
+ --junctions-output=$junctions
--hits-output=$accepted_hits
+
+ ## Handle reference file.
#if $refGenomeSource.genomeSource == "history":
- --indexes-dir=$refGenomeSource.ownFile
+ --own-file=$refGenomeSource.ownFile
+ --indexes-path="None"
#else:
- --indexes-dir=$refGenomeSource.index.value
+ --own-file="None"
+ --indexes-path=$refGenomeSource.index.value
#end if
+
+ ## Are reads single-end or paired?
+ --single-paired=$singlePaired.sPaired
+
+ ## First input file always required.
+ --input1=$singlePaired.input1
+
+ ## Set parms based on whether reads are single-end or paired.
#if $singlePaired.sPaired == "single":
- --input1=$singlePaired.input1
--input2="None"
- #else:
- -r $singlePaired.mean_inner_distance
- --input1=$singlePaired.input1
- --input2=$singlePaired.input2
- #end if
+ -r "None"
+ --settings=$singlePaired.sParams.sSettingsType
+ #if $singlePaired.sParams.sSettingsType == "full":
+ --mate-std-dev="None"
+ -a $singlePaired.sParams.anchor_length
+ -m $singlePaired.sParams.splice_mismatches
+ -i $singlePaired.sParams.min_intron_length
+ -I $singlePaired.sParams.max_intron_length
+ -F $singlePaired.sParams.junction_filter
+ -g $singlePaired.sParams.max_multihits
+ --min-segment-intron $singlePaired.sParams.min_segment_intron
+ --max-segment-intron $singlePaired.sParams.max_segment_intron
+ --seg-mismatches=$singlePaired.sParams.seg_mismatches
+ --seg-length=$singlePaired.sParams.seg_length
+ #if $singlePaired.sParams.closure_search.use_search == "Yes":
+ --closure-search
+ --min-closure-exon $singlePaired.sParams.closure_search.min_closure_exon
+ --min-closure-intron $singlePaired.sParams.closure_search.min_closure_intron
+ --max-closure-intron $singlePaired.sParams.closure_search.max_closure_intron
+ #else:
+ --no-closure-search
+ #end if
+ #if $singlePaired.sParams.coverage_search.use_search == "Yes":
+ --coverage-search
+ --min-coverage-intron $singlePaired.sParams.coverage_search.min_coverage_intron
+ --max-coverage-intron $singlePaired.sParams.coverage_search.max_coverage_intron
+ #else:
+ --no-coverage-search
+ #end if
+ ## No idea why the type conversion is necessary, but it seems to be.
+ #if str ($singlePaired.sParams.microexon_search) == "Yes":
+ --microexon-search
+ #end if
+ #end if
+ #else:
+ --input2=$singlePaired.input2
+ -r $singlePaired.mate_inner_distance
+ --settings=$singlePaired.pParams.pSettingsType
+ #if $singlePaired.pParams.pSettingsType == "full":
+ --mate-std-dev=$singlePaired.pParams.mate_std_dev
+ -a $singlePaired.pParams.anchor_length
+ -m $singlePaired.pParams.splice_mismatches
+ -i $singlePaired.pParams.min_intron_length
+ -I $singlePaired.pParams.max_intron_length
+ -F $singlePaired.pParams.junction_filter
+ -g $singlePaired.pParams.max_multihits
+ --min-segment-intron $singlePaired.pParams.min_segment_intron
+ --max-segment-intron $singlePaired.pParams.max_segment_intron
+ --seg-mismatches=$singlePaired.pParams.seg_mismatches
+ --seg-length=$singlePaired.pParams.seg_length
+ #if $singlePaired.pParams.closure_search.use_search == "Yes":
+ --closure-search
+ --min-closure-exon $singlePaired.pParams.closure_search.min_closure_exon
+ --min-closure-intron $singlePaired.pParams.closure_search.min_closure_intron
+ --max-closure-intron $singlePaired.pParams.closure_search.max_closure_intron
+ #else:
+ --no-closure-search
+ #end if
+ #if $singlePaired.pParams.coverage_search.use_search == "Yes":
+ --coverage-search
+ --min-coverage-intron $singlePaired.pParams.coverage_search.min_coverage_intron
+ --max-coverage-intron $singlePaired.pParams.coverage_search.max_coverage_intron
+ #else:
+ --no-coverage-search
+ #end if
+ ## No idea why the type conversion is necessary, but it seems to be.
+ #if str ($singlePaired.pParams.microexon_search) == "Yes":
+ --microexon-search
+ #end if
+ #end if
+ #end if
</command>
<inputs>
<conditional name="refGenomeSource">
@@ -27,56 +107,219 @@
<option value="history">Use one from the history</option>
</param>
<when value="indexed">
- <param name="index" type="select" label="Select a reference genome" help="If your genome of interest is not listed, contact the Galaxy team">
- <options from_file="bowtie_indices.loc">
- <column name="value" index="1" />
- <column name="name" index="0" />
- </options>
- </param>
- </when>
+ <param name="index" type="select" label="Select a reference genome" help="If your genome of interest is not listed, contact the Galaxy team">
+ <options from_file="bowtie_indices.loc">
+ <column name="value" index="1" />
+ <column name="name" index="0" />
+ </options>
+ </param>
+ </when>
<when value="history">
<param name="ownFile" type="data" format="fasta" metadata_name="dbkey" label="Select the reference genome" />
</when> <!-- history -->
- </conditional> <!-- refGenomeSource -->
+ </conditional> <!-- refGenomeSource -->
<conditional name="singlePaired">
<param name="sPaired" type="select" label="Is this library mate-paired?">
- <option value="single">Single-end</option>
- <option value="paired">Paired-end</option>
+ <option value="single">Single-end</option>
+ <option value="paired">Paired-end</option>
</param>
<when value="single">
- <param format="fastqsanger" name="input1" type="data" label="RNA-Seq FASTQ file" help="Must have Sanger-scaled quality values with ASCII offset 33"/>
+ <param format="fastqsanger" name="input1" type="data" label="RNA-Seq FASTQ file" help="Must have Sanger-scaled quality values with ASCII offset 33"/>
+ <conditional name="sParams">
+ <param name="sSettingsType" type="select" label="TopHat settings to use" help="You can use the default settings or set custom values for any of Tophat's parameters.">
+ <option value="preSet">Use Defaults</option>
+ <option value="full">Full parameter list</option>
+ </param>
+ <when value="preSet" />
+ <!-- Full/advanced parms. -->
+ <when value="full">
+ <param name="anchor_length" type="integer" value="8" label="Anchor length (at least 3)" help="Report junctions spanned by reads with at least this many bases on each side of the junction." />
+ <param name="splice_mismatches" type="integer" value="0" label="Maximum number of mismatches that can appear in the anchor region of spliced alignment" />
+ <param name="min_intron_length" type="integer" value="70" label="The minimum intron length" help="TopHat will ignore donor/acceptor pairs closer than this many bases apart." />
+ <param name="max_intron_length" type="integer" value="500000" label="The maximum intron length" help="When searching for junctions ab initio, TopHat will ignore donor/acceptor pairs farther than this many bases apart, except when such a pair is supported by a split segment alignment of a long read." />
+ <param name="junction_filter" type="float" value="0.15" label="Minimum isoform fraction: filter out junctions supported by too few alignments (number of reads divided by average depth of coverage)" help="0.0 to 1.0 (0 to turn off)" />
+ <param name="max_multihits" type="integer" value="40" label="Maximum number of alignments to be allowed" />
+ <param name="min_segment_intron" type="integer" value="50" label="Minimum intron length that may be found during split-segment (default) search" />
+ <param name="max_segment_intron" type="integer" value="500000" label="Maximum intron length that may be found during split-segment (default) search" />
+ <param name="seg_mismatches" type="integer" value="2" label="Number of mismatches allowed in each segment alignment for reads mapped independently" />
+ <param name="seg_length" type="integer" value="25" label="Minimum length of read segments" />
+ <!-- Closure search. -->
+ <conditional name="closure_search">
+ <param name="use_search" type="select" label="Use Closure Search">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ <when value="Yes">
+ <param name="min_closure_exon" type="integer" value="50" label="During closure search for paired end reads, exonic hops in the potential splice graph must be at least this long. The default is 50." />
+ <param name="min_closure_intron" type="integer" value="50" label="Minimum intron length that may be found during closure search" />
+ <param name="max_closure_intron" type="integer" value="5000" label="Maximum intron length that may be found during closure search" />
+ </when>
+ <when value="No" />
+ </conditional>
+ <!-- Coverage search. -->
+ <conditional name="coverage_search">
+ <param name="use_search" type="select" label="Use Coverage Search">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ <when value="Yes">
+ <param name="min_coverage_intron" type="integer" value="50" label="Minimum intron length that may be found during coverage search" />
+ <param name="max_coverage_intron" type="integer" value="20000" label="Maximum intron length that may be found during coverage search" />
+ </when>
+ <when value="No" />
+ </conditional>
+ <param name="microexon_search" type="select" label="Use Microexon Search" help="With this option, the pipeline will attempt to find alignments incident to microexons. Works only for reads 50bp or longer.">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ </when> <!-- full -->
+ </conditional> <!-- sParams -->
</when>
<when value="paired">
- <param format="fastqsanger" name="input1" type="data" label="RNA-Seq FASTQ file" help="Must have Sanger-scaled quality values with ASCII offset 33"/>
- <param format="fastqsanger" name="input2" type="data" label="RNA-Seq FASTQ file" help="Must have Sanger-scaled quality values with ASCII offset 33"/>
- <param format="fastqsanger" name="mean_inner_distance" type="integer" value="20" label="Mean Inner Distance between Mate Pairs"/>
+ <param format="fastqsanger" name="input1" type="data" label="RNA-Seq FASTQ file" help="Must have Sanger-scaled quality values with ASCII offset 33"/>
+ <param format="fastqsanger" name="input2" type="data" label="RNA-Seq FASTQ file" help="Must have Sanger-scaled quality values with ASCII offset 33"/>
+ <param name="mate_inner_distance" type="integer" value="20" label="Mean Inner Distance between Mate Pairs" />
+ <conditional name="pParams">
+ <param name="pSettingsType" type="select" label="TopHat settings to use" help="For most mapping needs use Commonly used settings. If you want full control use Full parameter list">
+ <option value="preSet">Commonly used</option>
+ <option value="full">Full parameter list</option>
+ </param>
+ <when value="preSet" />
+ <!-- Full/advanced parms. -->
+ <when value="full">
+ <param name="mate_std_dev" type="integer" value="20" label="Std. Dev for Distance between Mate Pairs" help="The standard deviation for the distribution on inner distances between mate pairs."/>
+ <param name="anchor_length" type="integer" value="8" label="Anchor length (at least 3)" help="Report junctions spanned by reads with at least this many bases on each side of the junction." />
+ <param name="splice_mismatches" type="integer" value="0" label="Maximum number of mismatches that can appear in the anchor region of spliced alignment" />
+ <param name="min_intron_length" type="integer" value="70" label="The minimum intron length" help="TopHat will ignore donor/acceptor pairs closer than this many bases apart." />
+ <param name="max_intron_length" type="integer" value="500000" label="The maximum intron length" help="When searching for junctions ab initio, TopHat will ignore donor/acceptor pairs farther than this many bases apart, except when such a pair is supported by a split segment alignment of a long read." />
+ <param name="junction_filter" type="float" value="0.15" label="Minimum isoform fraction: filter out junctions supported by too few alignments (number of reads divided by average depth of coverage)" help="0.0 to 1.0 (0 to turn off)" />
+ <param name="max_multihits" type="integer" value="40" label="Maximum number of alignments to be allowed" />
+ <param name="min_segment_intron" type="integer" value="50" label="Minimum intron length that may be found during split-segment (default) search" />
+ <param name="max_segment_intron" type="integer" value="500000" label="Maximum intron length that may be found during split-segment (default) search" />
+ <param name="seg_mismatches" type="integer" value="2" label="Number of mismatches allowed in each segment alignment for reads mapped independently" />
+ <param name="seg_length" type="integer" value="25" label="Minimum length of read segments" />
+ <!-- Closure search. -->
+ <conditional name="closure_search">
+ <param name="use_search" type="select" label="Use Closure Search">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ <when value="Yes">
+ <param name="min_closure_exon" type="integer" value="50" label="During closure search for paired end reads, exonic hops in the potential splice graph must be at least this long. The default is 50." />
+ <param name="min_closure_intron" type="integer" value="50" label="Minimum intron length that may be found during closure search" />
+ <param name="max_closure_intron" type="integer" value="5000" label="Maximum intron length that may be found during closure search" />
+ </when>
+ <when value="No" />
+ </conditional>
+ <!-- Coverage search. -->
+ <conditional name="coverage_search">
+ <param name="use_search" type="select" label="Use Coverage Search">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ <when value="Yes">
+ <param name="min_coverage_intron" type="integer" value="50" label="Minimum intron length that may be found during coverage search" />
+ <param name="max_coverage_intron" type="integer" value="20000" label="Maximum intron length that may be found during coverage search" />
+ </when>
+ <when value="No" />
+ </conditional>
+ <param name="microexon_search" type="select" label="Use Microexon Search" help="With this option, the pipeline will attempt to find alignments incident to microexons. Works only for reads 50bp or longer.">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ </when> <!-- full -->
+ </conditional> <!-- pParams -->
</when>
</conditional>
</inputs>
-
+
<outputs>
- <data format="sam" name="accepted_hits" label="${tool.name} on ${on_string}: accepted_hits"/>
- <data format="wig" name="coverage" label="${tool.name} on ${on_string}: coverage"/>
+ <data format="sam" name="accepted_hits" label="${tool.name} on ${on_string}: accepted_hits"/>
+ <data format="bedgraph" name="coverage" label="${tool.name} on ${on_string}: coverage"/>
<data format="bed" name="junctions" label="${tool.name} on ${on_string}: splice junctions"/>
</outputs>
-
+
<tests>
+<!-- <test>
+ <param name="genomeSource" value="indexed"/>
+ <param name="index" value="equCab2chrM"/>
+ <param name="sPaired" value="single"/>
+ <param name="input1" ftype="fastqsanger" value="tophat_in1.fq"/>
+ <param name="sSettingsType" value="preSet"/>
+--> <!--
+ Can't test this right now because first lines of file are run-specific.
+ <output name="accepted_hits" file="tophat_out1.sam"/>
+ -->
+<!-- <output name="coverage" file="tophat_out2.wig"/>
+ <output name="junctions" file="tophat_out3.bed"/>
+ </test>
+-->
+ <!-- Test using test data: paired-end reads, index from history. -->
<test>
- <param name="genomeSource" value="indexed"/>
- <param name="index" value="test_ref"/>
+ <param name="genomeSource" value="history"/>
+ <param name="ownFile" ftype="fasta" value="tophat_in3.fa"/>
<param name="sPaired" value="paired"/>
<param name="input1" ftype="fastqsanger" value="tophat_in1.fq"/>
<param name="input2" ftype="fastqsanger" value="tophat_in2.fq"/>
- <param name="mean_inner_distance" value="20"/>
- <!--
- Can't test this right now because first lines of file are run-specific.
+ <param name="mate_inner_distance" value="20"/>
+ <param name="pSettingsType" value="preSet"/>
<output name="accepted_hits" file="tophat_out1.sam"/>
- -->
<output name="coverage" file="tophat_out2.wig"/>
<output name="junctions" file="tophat_out3.bed"/>
</test>
- </tests>
-
+<!-- <test>
+ <param name="genomeSource" value="history"/>
+ <param name="ownFile" value="phiX.fasta"/>
+ <param name="sPaired" value="single"/>
+ <param name="input1" ftype="fastqsanger" value="tophat_in1.fq"/>
+ <param name="sSettingsType" value="full"/>
+ <param name="anchor_length" value="8"/>
+ <param name="splice_mismatches" value="0"/>
+ <param name="min_intron_length" value="70"/>
+ <param name="max_intron_length" value="500000"/>
+ <param name="quals_scale" value="default"/>
+ <param name="junction_filter" value="0.15"/>
+ <param name="max_multihits" value="40"/>
+ <param name="min_segment_intron" value="50" />
+ <param name="max_segment_intron" value="500000" />
+ <param name="seg_mismatches" value="2"/>
+ <param name="seg_length" value="25"/>
+--> <!--
+ Can't test this right now because first lines of file are run-specific.
+ <output name="accepted_hits" file="tophat_out1.sam"/>
+ -->
+<!-- <output name="coverage" file="tophat_out2.wig"/>
+ <output name="junctions" file="tophat_out3.bed"/>
+ </test>
+ <test>
+ <param name="genomeSource" value="indexed"/>
+ <param name="index" value="equCab2chrM"/>
+ <param name="sPaired" value="paired"/>
+ <param name="input1" ftype="fastqsanger" value="tophat_in1.fq"/>
+ <param name="input2" ftype="fastqsanger" value="tophat_in2.fq"/>
+ <param name="mate_inner_distance" value="20"/>
+ <param name="pSettingsType" value="full"/>
+ <param name="mate_std_dev" value="20"/>
+ <param name="anchor_length" value="8"/>
+ <param name="splice_mismatches" value="0"/>
+ <param name="min_intron_length" value="70"/>
+ <param name="max_intron_length" value="500000"/>
+ <param name="quals_scale" value="default"/>
+ <param name="junction_filter" value="0.15"/>
+ <param name="max_multihits" value="40"/>
+ <param name="min_coverage_intron" value="50" />
+ <param name="max_coverage_intron" value="20000" />
+ <param name="seg_mismatches" value="2"/>
+ <param name="seg_length" value="25"/>
+--> <!--
+ Can't test this right now because first lines of file are run-specific.
+ <output name="accepted_hits" file="tophat_out1.sam"/>
+ -->
+<!-- <output name="coverage" file="tophat_out2.wig"/>
+ <output name="junctions" file="tophat_out3.bed"/>
+ </test>
+--> </tests>
+
<help>
**Tophat Overview**
@@ -104,11 +347,15 @@
**Outputs**
-Tophat produces three output files::
+Tophat produces three output files:
- coverage.wig -- coverage of reads
- accepted_hits.sam -- reads that were mapped onto genome
- junctions.bed -- splice junctions identified by Tophat
+- coverage.wig -- A UCSC BedGraph_ wigglegram track, showing the depth of coverage at each position, including the spliced read alignments.
+- accepted_hits.sam -- A list of read alignments in SAM_ format.
+- junctions.bed -- A UCSC BED_ track of junctions reported by TopHat. Each junction consists of two connected BED blocks, where each block is as long as the maximal overhang of any read spanning the junction. The score is the number of alignments spanning the junction.
+
+.. _BedGraph: http://genome.ucsc.edu/goldenPath/help/bedgraph.html
+.. _SAM: http://samtools.sourceforge.net/
+.. _BED: http://genome.ucsc.edu/FAQ/FAQformat.html#format1
-------
@@ -122,8 +369,35 @@
This is a list of implemented Tophat options::
- -r This is the expected (mean) inner distance between mate pairs. For, example, for paired end runs with fragments
- selected at 300bp, where each end is 50bp, you should set -r to be 200. There is no default, and this parameter
- is required for paired end runs.
+ -r This is the expected (mean) inner distance between mate pairs. For, example, for paired end runs with fragments
+ selected at 300bp, where each end is 50bp, you should set -r to be 200. There is no default, and this parameter
+ is required for paired end runs.
+ --mate-std-dev INT The standard deviation for the distribution on inner distances between mate pairs. The default is 20bp.
+ -a/--min-anchor-length INT The "anchor length". TopHat will report junctions spanned by reads with at least this many bases on each side of the junction. Note that individual spliced
+ alignments may span a junction with fewer than this many bases on one side. However, every junction involved in spliced alignments is supported by at least one
+ read with this many bases on each side. This must be at least 3 and the default is 8.
+ -m/--splice-mismatches INT The maximum number of mismatches that may appear in the "anchor" region of a spliced alignment. The default is 0.
+ -i/--min-intron-length INT The minimum intron length. TopHat will ignore donor/acceptor pairs closer than this many bases apart. The default is 70.
+ -I/--max-intron-length INT The maximum intron length. When searching for junctions ab initio, TopHat will ignore donor/acceptor pairs farther than this many bases apart, except when such a pair is supported by a split segment alignment of a long read. The default is 500000.
+ -F/--min-isoform-fraction 0.0-1.0 TopHat filters out junctions supported by too few alignments. Suppose a junction spanning two exons, is supported by S reads. Let the average depth of coverage of
+ exon A be D, and assume that it is higher than B. If S / D is less than the minimum isoform fraction, the junction is not reported. A value of zero disables the
+ filter. The default is 0.15.
+ -g/--max-multihits INT Instructs TopHat to allow up to this many alignments to the reference for a given read, and suppresses all alignments for reads with more than this many
+ alignments. The default is 40.
+ --no-closure-search Disables the mate pair closure-based search for junctions. Currently, has no effect - closure search is off by default.
+ --closure-search Enables the mate pair closure-based search for junctions. Closure-based search should only be used when the expected inner distance between mates is small (about or less than 50bp)
+ --no-coverage-search Disables the coverage based search for junctions.
+ --coverage-search Enables the coverage based search for junctions. Use when coverage search is disabled by default (such as for reads 75bp or longer), for maximum sensitivity.
+ --microexon-search With this option, the pipeline will attempt to find alignments incident to microexons. Works only for reads 50bp or longer.
+ --butterfly-search TopHat will use a slower but potentially more sensitive algorithm to find junctions in addition to its standard search. Consider using this if you expect that your experiment produced a lot of reads from pre-mRNA, that fall within the introns of your transcripts.
+ --segment-mismatches Read segments are mapped independently, allowing up to this many mismatches in each segment alignment. The default is 2.
+ --segment-length Each read is cut up into segments, each at least this long. These segments are mapped independently. The default is 25.
+ --min-closure-exon During closure search for paired end reads, exonic hops in the potential splice graph must be at least this long. The default is 50.
+ --min-closure-intron The minimum intron length that may be found during closure search. The default is 50.
+ --max-closure-intron The maximum intron length that may be found during closure search. The default is 5000.
+ --min-coverage-intron The minimum intron length that may be found during coverage search. The default is 50.
+ --max-coverage-intron The maximum intron length that may be found during coverage search. The default is 20000.
+ --min-segment-intron The minimum intron length that may be found during split-segment search. The default is 50.
+ --max-segment-intron The maximum intron length that may be found during split-segment search. The default is 500000.
</help>
</tool>
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/20ba52b9d504
changeset: 3720:20ba52b9d504
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Fri Apr 30 13:08:33 2010 -0400
description:
Remove headers from SAM file so that output matches new Tophat wrapper.
diffstat:
test-data/tophat_out1.sam | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)
diffs (9 lines):
diff -r 436c9d1c5e64 -r 20ba52b9d504 test-data/tophat_out1.sam
--- a/test-data/tophat_out1.sam Wed Apr 28 17:35:02 2010 -0400
+++ b/test-data/tophat_out1.sam Fri Apr 30 13:08:33 2010 -0400
@@ -1,5 +1,3 @@
-@HD VN:1.0 SO:sorted
-@PG TopHat VN:1.0.13 CL:/Users/jeremy/projects/bx_tools/tophat-1.0.13/bin/tophat -r 20 test_ref reads_1.fq reads_2.fq
test_mRNA_3_187_51 99 test_chromosome 53 255 75M = 163 0 TACTATTTGACTAGACTGGAGGCGCTTGCGACTGAGCTAGGACGTGCCACTACGGGGATGACGACTCGGACTACG IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII NM:i:2
test_mRNA_4_191_5d 163 test_chromosome 54 255 75M = 167 0 ACTATCTGACGAGACTGGAGGCGCTTGCGACTGAGCTAGGACGTACCATTACGCGGATGACGACTAGGACTACGG IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII NM:i:4
test_mRNA_5_197_46 97 test_chromosome 55 255 75M = 173 0 CTATCTGACTAGACTCGAGGCGCTTGCGTCTGAGCTAGGACGTGCCACTACGGGGATGACGACTAGGACTACGGA IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII NM:i:2
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/34c3789c86ef
changeset: 3719:34c3789c86ef
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Fri Apr 30 12:34:48 2010 -0400
description:
Make GTF type subclass from GFF type.
diffstat:
lib/galaxy/datatypes/interval.py | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diffs (17 lines):
diff -r 853046b87a5c -r 34c3789c86ef lib/galaxy/datatypes/interval.py
--- a/lib/galaxy/datatypes/interval.py Fri Apr 30 10:15:42 2010 -0400
+++ b/lib/galaxy/datatypes/interval.py Fri Apr 30 12:34:48 2010 -0400
@@ -831,9 +831,12 @@
except:
return False
-class Gtf( Tabular ):
+class Gtf( Gff ):
"""Tab delimited data in Gtf format"""
file_ext = "gtf"
+
+ def sniff( self, filename ):
+ return False
class Wiggle( Tabular, _RemoteCallMixin ):
"""Tab delimited data in wiggle format"""
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/c09a8456b63e
changeset: 3717:c09a8456b63e
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Thu Apr 29 19:57:00 2010 -0400
description:
Full Tophat wrapper. All options are implemented, but additional tests are needed.
diffstat:
tools/ngs_rna/tophat_wrapper.py | 216 +++++++++++++++++-----
tools/ngs_rna/tophat_wrapper.xml | 364 ++++++++++++++++++++++++++++++++++----
2 files changed, 483 insertions(+), 97 deletions(-)
diffs (672 lines):
diff -r 436c9d1c5e64 -r c09a8456b63e tools/ngs_rna/tophat_wrapper.py
--- a/tools/ngs_rna/tophat_wrapper.py Wed Apr 28 17:35:02 2010 -0400
+++ b/tools/ngs_rna/tophat_wrapper.py Thu Apr 29 19:57:00 2010 -0400
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-import optparse, os, shutil, sys, tempfile
+import optparse, os, shutil, subprocess, sys, tempfile, fileinput
def stop_err( msg ):
sys.stderr.write( "%s\n" % msg )
@@ -9,72 +9,184 @@
def __main__():
#Parse Command Line
parser = optparse.OptionParser()
- parser.add_option( '-1', '--input1', dest='input1', help='The (forward or single-end) reads file in Sanger FASTQ format' )
- parser.add_option( '-2', '--input2', dest='input2', help='The reverse reads file in Sanger FASTQ format' )
+ parser.add_option( '-p', '--num-threads', dest='num_threads', help='Use this many threads to align reads. The default is 1.' )
+ parser.add_option( '-C', '--coverage-output', dest='coverage_output_file', help='Coverage output file; formate is WIG.' )
+ parser.add_option( '-J', '--junctions-output', dest='junctions_output_file', help='Junctions output file; formate is BED.' )
+ parser.add_option( '-H', '--hits-output', dest='accepted_hits_output_file', help='Accepted hits output file; formate is SAM.' )
+ parser.add_option( '', '--own-file', dest='own_file', help='' )
+ parser.add_option( '-D', '--indexes-path', dest='index_path', help='Indexes directory; location of .ebwt and .fa files.' )
+ parser.add_option( '-r', '--mate-inner-dist', dest='mate_inner_dist', help='This is the expected (mean) inner distance between mate pairs. \
+ For, example, for paired end runs with fragments selected at 300bp, \
+ where each end is 50bp, you should set -r to be 200. There is no default, \
+ and this parameter is required for paired end runs.')
+ parser.add_option( '', '--mate-std-dev', dest='mate_std_dev', help='Standard deviation of distribution on inner distances between male pairs.' )
parser.add_option( '-a', '--min-anchor-length', dest='min_anchor_length',
help='The "anchor length". TopHat will report junctions spanned by reads with at least this many bases on each side of the junction.' )
+ parser.add_option( '-m', '--splice-mismatches', dest='splice_mismatches', help='The maximum number of mismatches that can appear in the anchor region of a spliced alignment.' )
parser.add_option( '-i', '--min-intron-length', dest='min_intron_length',
help='The minimum intron length. TopHat will ignore donor/acceptor pairs closer than this many bases apart.' )
parser.add_option( '-I', '--max-intron-length', dest='max_intron_length',
help='The maximum intron length. When searching for junctions ab initio, TopHat will ignore donor/acceptor pairs farther than this many bases apart, except when such a pair is supported by a split segment alignment of a long read.' )
- parser.add_option( '-s', '--solexa-quals', dest='solexa_quals', help='Use the Solexa scale for quality values in FASTQ files.' )
- parser.add_option( '-S', '--solexa.3-quals', dest='solexa_quals',
- help='As of the Illumina GA pipeline version 1.3, quality scores are encoded in Phred-scaled base-64. Use this option for FASTQ files from pipeline 1.3 or later.' )
- parser.add_option( '-p', '--num-threads', dest='num_threads', help='Use this many threads to align reads. The default is 1.' )
- parser.add_option( '-C', '--coverage-output', dest='coverage_output_file', help='Coverage output file; formate is WIG.' )
- parser.add_option( '-J', '--junctions-output', dest='junctions_output_file', help='Junctions output file; formate is BED.' )
- parser.add_option( '-H', '--hits-output', dest='accepted_hits_output_file', help='Accepted hits output file; formate is SAM.' )
- parser.add_option( '-D', '--indexes-dir', dest='indexes_directory', help='Indexes directory; location of .ebwt and .fa files.' )
- parser.add_option( '-r', '--mate-inner-dist', dest='mate_inner_dist', help='This is the expected (mean) inner distance between mate pairs. \
- For, example, for paired end runs with fragments selected at 300bp, \
- where each end is 50bp, you should set -r to be 200. There is no default, \
- and this parameter is required for paired end runs.')
+ parser.add_option( '-F', '--junction_filter', dest='junction_filter', help='Filter out junctions supported by too few alignments (number of reads divided by average depth of coverage)' )
+ parser.add_option( '-g', '--max_multihits', dest='max_multihits', help='Maximum number of alignments to be allowed' )
+ parser.add_option( '', '--microexon-search', action="store_true", dest='microexon_search', help='With this option, the pipeline will attempt to find alignments incident to microexons. Works only for reads 50bp or longer.')
+ parser.add_option( '', '--closure-search', action="store_true", dest='closure_search', help='Enables the mate pair closure-based search for junctions. Closure-based search should only be used when the expected inner distance between mates is small (<= 50bp)')
+ parser.add_option( '', '--no-closure-search', action="store_false", dest='closure_search' )
+ parser.add_option( '', '--coverage-search', action="store_true", dest='coverage_search', help='Enables the coverage based search for junctions. Use when coverage search is disabled by default (such as for reads 75bp or longer), for maximum sensitivity.')
+ parser.add_option( '', '--no-coverage-search', action="store_false", dest='coverage_search' )
+ parser.add_option( '', '--min-segment-intron', dest='min_segment_intron', help='Minimum intron length that may be found during split-segment search' )
+ parser.add_option( '', '--max-segment-intron', dest='max_segment_intron', help='Maximum intron length that may be found during split-segment search' )
+ parser.add_option( '', '--min-closure-exon', dest='min_closure_exon', help='Minimum length for exonic hops in potential splice graph' )
+ parser.add_option( '', '--min-closure-intron', dest='min_closure_intron', help='Minimum intron length that may be found during closure search' )
+ parser.add_option( '', '--max-closure-intron', dest='max_closure_intron', help='Maximum intron length that may be found during closure search' )
+ parser.add_option( '', '--min-coverage-intron', dest='min_coverage_intron', help='Minimum intron length that may be found during coverage search' )
+ parser.add_option( '', '--max-coverage-intron', dest='max_coverage_intron', help='Maximum intron length that may be found during coverage search' )
+ parser.add_option( '', '--seg-mismatches', dest='seg_mismatches', help='Number of mismatches allowed in each segment alignment for reads mapped independently' )
+ parser.add_option( '', '--seg-length', dest='seg_length', help='Minimum length of read segments' )
+
+ # Wrapper options.
+ parser.add_option( '-1', '--input1', dest='input1', help='The (forward or single-end) reads file in Sanger FASTQ format' )
+ parser.add_option( '-2', '--input2', dest='input2', help='The reverse reads file in Sanger FASTQ format' )
+ parser.add_option( '', '--single-paired', dest='single_paired', help='' )
+ parser.add_option( '', '--settings', dest='settings', help='' )
+
(options, args) = parser.parse_args()
- # Make temp directory for output.
+ #sys.stderr.write('*'*50+'\n'+str(options)+'\n'+'*'*50+'\n')
+
+ # Creat bowtie index if necessary.
+ tmp_index_dir = tempfile.mkdtemp()
+ if options.own_file != 'None':
+ index_path = os.path.join( tmp_index_dir, os.path.split( options.own_file )[1] )
+ cmd_index = 'bowtie-build -f %s %s' % ( options.own_file, index_path )
+ try:
+ tmp = tempfile.NamedTemporaryFile( dir=tmp_index_dir ).name
+ tmp_stderr = open( tmp, 'wb' )
+ proc = subprocess.Popen( args=cmd_index, shell=True, cwd=tmp_index_dir, stderr=tmp_stderr.fileno() )
+ returncode = proc.wait()
+ tmp_stderr.close()
+ # get stderr, allowing for case where it's very large
+ tmp_stderr = open( tmp, 'rb' )
+ stderr = ''
+ buffsize = 1048576
+ try:
+ while True:
+ stderr += tmp_stderr.read( buffsize )
+ if not stderr or len( stderr ) % buffsize != 0:
+ break
+ except OverflowError:
+ pass
+ tmp_stderr.close()
+ if returncode != 0:
+ raise Exception, stderr
+ except Exception, e:
+ if os.path.exists( tmp_index_dir ):
+ shutil.rmtree( tmp_index_dir )
+ stop_err( 'Error indexing reference sequence\n' + str( e ) )
+ else:
+ index_path = options.index_path
+
+ # Build tophat command.
tmp_output_dir = tempfile.mkdtemp()
-
- # Build command.
-
- # Base.
- cmd = "tophat -o %s " % ( tmp_output_dir )
-
- # Add options.
- if options.mate_inner_dist:
- cmd += ( " -r %i" % int ( options.mate_inner_dist ) )
-
- # Add index prefix.
- cmd += " " + options.indexes_directory
-
- # Add input files.
- cmd += " " + options.input1
- if options.mate_inner_dist:
- # Using paired-end reads.
- cmd += " " + options.input2
-
- # Route program output to file.
- cmd += " > %s" % tmp_output_dir + "/std_out.txt"
- # Route program error output to file.
- cmd += " 2> %s" % tmp_output_dir + "/std_err.txt"
-
- # Run.
+ cmd = 'tophat -o %s %s %s %s'
+ reads = options.input1
+ if options.input2 != 'None':
+ reads += ' ' + options.input2
+ opts = '-p %s' % options.num_threads
+ if options.single_paired == 'paired':
+ opts += ' -r %s' % options.mate_inner_dist
+ if options.settings == 'preSet':
+ cmd = cmd % ( tmp_output_dir, opts, index_path, reads )
+ else:
+ try:
+ if int( options.min_anchor_length ) >= 3:
+ opts += ' -a %s' % options.min_anchor_length
+ else:
+ raise Exception, 'Minimum anchor length must be 3 or greater'
+ opts += ' -m %s' % options.splice_mismatches
+ opts += ' -i %s' % options.min_intron_length
+ opts += ' -I %s' % options.max_intron_length
+ if float( options.junction_filter ) != 0.0:
+ opts += ' -F %s' % options.junction_filter
+ opts += ' -g %s' % options.max_multihits
+ if options.coverage_search:
+ opts += ' --coverage-search --min-coverage-intron %s --max-coverage-intron %s' % ( options.min_coverage_intron, options.max_coverage_intron )
+ else:
+ opts += ' --no-coverage-search'
+ if options.closure_search:
+ opts += ' --closure-search --min-closure-exon %s --min-closure-intron %s --max-closure-intron %s' % ( options.min_closure_exon, options.min_closure_intron, options.max_closure_intron )
+ else:
+ opts += ' --no-closure-search'
+ if options.microexon_search:
+ opts += ' --microexon-search'
+ if options.single_paired == 'paired':
+ opts += ' --mate-std-dev %s' % options.mate_std_dev
+ cmd = cmd % ( tmp_output_dir, opts, index_path, reads )
+ except Exception, e:
+ # Clean up temp dirs
+ if os.path.exists( tmp_index_dir ):
+ shutil.rmtree( tmp_index_dir )
+ if os.path.exists( tmp_output_dir ):
+ shutil.rmtree( tmp_output_dir )
+ stop_err( 'Something is wrong with the alignment parameters and the alignment could not be run\n' + str( e ) )
+ print cmd
+
+ # Run
try:
- os.system( cmd )
+ tmp_out = tempfile.NamedTemporaryFile( dir=tmp_output_dir ).name
+ tmp_stdout = open( tmp_out, 'wb' )
+ tmp_err = tempfile.NamedTemporaryFile( dir=tmp_output_dir ).name
+ tmp_stderr = open( tmp_err, 'wb' )
+ proc = subprocess.Popen( args=cmd, shell=True, cwd=tmp_output_dir, stdout=tmp_stdout, stderr=tmp_stderr )
+ returncode = proc.wait()
+ tmp_stderr.close()
+ # get stderr, allowing for case where it's very large
+ tmp_stderr = open( tmp_err, 'rb' )
+ stderr = ''
+ buffsize = 1048576
+ try:
+ while True:
+ stderr += tmp_stderr.read( buffsize )
+ if not stderr or len( stderr ) % buffsize != 0:
+ break
+ except OverflowError:
+ pass
+ tmp_stdout.close()
+ tmp_stderr.close()
+ if returncode != 0:
+ raise Exception, stderr
except Exception, e:
+ # Clean up temp dirs
+ if os.path.exists( tmp_output_dir ):
+ shutil.rmtree( tmp_output_dir )
stop_err( 'Error in tophat:\n' + str( e ) )
# TODO: look for errors in program output.
- # Copy output files from tmp directory to specified files.
+ # Postprocessing: copy output files from tmp directory to specified files. Also need to remove header lines from SAM file.
try:
- shutil.copyfile( tmp_output_dir + "/coverage.wig", options.coverage_output_file )
- shutil.copyfile( tmp_output_dir + "/junctions.bed", options.junctions_output_file )
- shutil.copyfile( tmp_output_dir + "/accepted_hits.sam", options.accepted_hits_output_file )
- except Exception, e:
- stop_err( 'Error in tophat:\n' + str( e ) )
-
- # Clean up temp dirs
- if os.path.exists( tmp_output_dir ):
- shutil.rmtree( tmp_output_dir )
+ try:
+ shutil.copyfile( tmp_output_dir + "/coverage.wig", options.coverage_output_file )
+ shutil.copyfile( tmp_output_dir + "/junctions.bed", options.junctions_output_file )
+
+ # Remove headers from SAM file in place.
+ in_header = True # Headers always at start of file.
+ for line in fileinput.input( tmp_output_dir + "/accepted_hits.sam", inplace=1 ):
+ if in_header and line.startswith("@"):
+ continue
+ else:
+ in_header = False
+ sys.stdout.write( line )
+
+ # Copy SAM File.
+ shutil.copyfile( tmp_output_dir + "/accepted_hits.sam", options.accepted_hits_output_file )
+ except Exception, e:
+ stop_err( 'Error in tophat:\n' + str( e ) )
+ finally:
+ # Clean up temp dirs
+ if os.path.exists( tmp_index_dir ):
+ shutil.rmtree( tmp_index_dir )
+ if os.path.exists( tmp_output_dir ):
+ shutil.rmtree( tmp_output_dir )
if __name__=="__main__": __main__()
diff -r 436c9d1c5e64 -r c09a8456b63e tools/ngs_rna/tophat_wrapper.xml
--- a/tools/ngs_rna/tophat_wrapper.xml Wed Apr 28 17:35:02 2010 -0400
+++ b/tools/ngs_rna/tophat_wrapper.xml Thu Apr 29 19:57:00 2010 -0400
@@ -1,24 +1,104 @@
-<tool id="tophat" name="Tophat" version="1.0.13">
+<tool id="tophat" name="Tophat" version="1.0.0">
<description>Find splice junctions using RNA-seq data</description>
<command interpreter="python">
- tophat_wrapper.py
+ tophat_wrapper.py
+ ## Change this to accomodate the number of threads you have available.
--num-threads="4"
- --coverage-output=$coverage
- --junctions-output=$junctions
+
+ ## Provide outputs.
+ --coverage-output=$coverage
+ --junctions-output=$junctions
--hits-output=$accepted_hits
+
+ ## Handle reference file.
#if $refGenomeSource.genomeSource == "history":
- --indexes-dir=$refGenomeSource.ownFile
+ --own-file=$refGenomeSource.ownFile
+ --indexes-path="None"
#else:
- --indexes-dir=$refGenomeSource.index.value
+ --own-file="None"
+ --indexes-path=$refGenomeSource.index.value
#end if
+
+ ## Are reads single-end or paired?
+ --single-paired=$singlePaired.sPaired
+
+ ## First input file always required.
+ --input1=$singlePaired.input1
+
+ ## Set parms based on whether reads are single-end or paired.
#if $singlePaired.sPaired == "single":
- --input1=$singlePaired.input1
--input2="None"
- #else:
- -r $singlePaired.mean_inner_distance
- --input1=$singlePaired.input1
- --input2=$singlePaired.input2
- #end if
+ -r "None"
+ --settings=$singlePaired.sParams.sSettingsType
+ #if $singlePaired.sParams.sSettingsType == "full":
+ --mate-std-dev="None"
+ -a $singlePaired.sParams.anchor_length
+ -m $singlePaired.sParams.splice_mismatches
+ -i $singlePaired.sParams.min_intron_length
+ -I $singlePaired.sParams.max_intron_length
+ -F $singlePaired.sParams.junction_filter
+ -g $singlePaired.sParams.max_multihits
+ --min-segment-intron $singlePaired.sParams.min_segment_intron
+ --max-segment-intron $singlePaired.sParams.max_segment_intron
+ --seg-mismatches=$singlePaired.sParams.seg_mismatches
+ --seg-length=$singlePaired.sParams.seg_length
+ #if $singlePaired.sParams.closure_search.use_search == "Yes":
+ --closure-search
+ --min-closure-exon $singlePaired.sParams.closure_search.min_closure_exon
+ --min-closure-intron $singlePaired.sParams.closure_search.min_closure_intron
+ --max-closure-intron $singlePaired.sParams.closure_search.max_closure_intron
+ #else:
+ --no-closure-search
+ #end if
+ #if $singlePaired.sParams.coverage_search.use_search == "Yes":
+ --coverage-search
+ --min-coverage-intron $singlePaired.sParams.coverage_search.min_coverage_intron
+ --max-coverage-intron $singlePaired.sParams.coverage_search.max_coverage_intron
+ #else:
+ --no-coverage-search
+ #end if
+ ## No idea why the type conversion is necessary, but it seems to be.
+ #if str ($singlePaired.sParams.microexon_search) == "Yes":
+ --microexon-search
+ #end if
+ #end if
+ #else:
+ --input2=$singlePaired.input2
+ -r $singlePaired.mate_inner_distance
+ --settings=$singlePaired.pParams.pSettingsType
+ #if $singlePaired.pParams.pSettingsType == "full":
+ --mate-std-dev=$singlePaired.pParams.mate_std_dev
+ -a $singlePaired.pParams.anchor_length
+ -m $singlePaired.pParams.splice_mismatches
+ -i $singlePaired.pParams.min_intron_length
+ -I $singlePaired.pParams.max_intron_length
+ -F $singlePaired.pParams.junction_filter
+ -g $singlePaired.pParams.max_multihits
+ --min-segment-intron $singlePaired.pParams.min_segment_intron
+ --max-segment-intron $singlePaired.pParams.max_segment_intron
+ --seg-mismatches=$singlePaired.pParams.seg_mismatches
+ --seg-length=$singlePaired.pParams.seg_length
+ #if $singlePaired.pParams.closure_search.use_search == "Yes":
+ --closure-search
+ --min-closure-exon $singlePaired.pParams.closure_search.min_closure_exon
+ --min-closure-intron $singlePaired.pParams.closure_search.min_closure_intron
+ --max-closure-intron $singlePaired.pParams.closure_search.max_closure_intron
+ #else:
+ --no-closure-search
+ #end if
+ #if $singlePaired.pParams.coverage_search.use_search == "Yes":
+ --coverage-search
+ --min-coverage-intron $singlePaired.pParams.coverage_search.min_coverage_intron
+ --max-coverage-intron $singlePaired.pParams.coverage_search.max_coverage_intron
+ #else:
+ --no-coverage-search
+ #end if
+ ## No idea why the type conversion is necessary, but it seems to be.
+ #if str ($singlePaired.pParams.microexon_search) == "Yes":
+ --microexon-search
+ #end if
+ #end if
+ #end if
</command>
<inputs>
<conditional name="refGenomeSource">
@@ -27,56 +107,219 @@
<option value="history">Use one from the history</option>
</param>
<when value="indexed">
- <param name="index" type="select" label="Select a reference genome" help="If your genome of interest is not listed, contact the Galaxy team">
- <options from_file="bowtie_indices.loc">
- <column name="value" index="1" />
- <column name="name" index="0" />
- </options>
- </param>
- </when>
+ <param name="index" type="select" label="Select a reference genome" help="If your genome of interest is not listed, contact the Galaxy team">
+ <options from_file="bowtie_indices.loc">
+ <column name="value" index="1" />
+ <column name="name" index="0" />
+ </options>
+ </param>
+ </when>
<when value="history">
<param name="ownFile" type="data" format="fasta" metadata_name="dbkey" label="Select the reference genome" />
</when> <!-- history -->
- </conditional> <!-- refGenomeSource -->
+ </conditional> <!-- refGenomeSource -->
<conditional name="singlePaired">
<param name="sPaired" type="select" label="Is this library mate-paired?">
- <option value="single">Single-end</option>
- <option value="paired">Paired-end</option>
+ <option value="single">Single-end</option>
+ <option value="paired">Paired-end</option>
</param>
<when value="single">
- <param format="fastqsanger" name="input1" type="data" label="RNA-Seq FASTQ file" help="Must have Sanger-scaled quality values with ASCII offset 33"/>
+ <param format="fastqsanger" name="input1" type="data" label="RNA-Seq FASTQ file" help="Must have Sanger-scaled quality values with ASCII offset 33"/>
+ <conditional name="sParams">
+ <param name="sSettingsType" type="select" label="TopHat settings to use" help="You can use the default settings or set custom values for any of Tophat's parameters.">
+ <option value="preSet">Use Defaults</option>
+ <option value="full">Full parameter list</option>
+ </param>
+ <when value="preSet" />
+ <!-- Full/advanced parms. -->
+ <when value="full">
+ <param name="anchor_length" type="integer" value="8" label="Anchor length (at least 3)" help="Report junctions spanned by reads with at least this many bases on each side of the junction." />
+ <param name="splice_mismatches" type="integer" value="0" label="Maximum number of mismatches that can appear in the anchor region of spliced alignment" />
+ <param name="min_intron_length" type="integer" value="70" label="The minimum intron length" help="TopHat will ignore donor/acceptor pairs closer than this many bases apart." />
+ <param name="max_intron_length" type="integer" value="500000" label="The maximum intron length" help="When searching for junctions ab initio, TopHat will ignore donor/acceptor pairs farther than this many bases apart, except when such a pair is supported by a split segment alignment of a long read." />
+ <param name="junction_filter" type="float" value="0.15" label="Minimum isoform fraction: filter out junctions supported by too few alignments (number of reads divided by average depth of coverage)" help="0.0 to 1.0 (0 to turn off)" />
+ <param name="max_multihits" type="integer" value="40" label="Maximum number of alignments to be allowed" />
+ <param name="min_segment_intron" type="integer" value="50" label="Minimum intron length that may be found during split-segment (default) search" />
+ <param name="max_segment_intron" type="integer" value="500000" label="Maximum intron length that may be found during split-segment (default) search" />
+ <param name="seg_mismatches" type="integer" value="2" label="Number of mismatches allowed in each segment alignment for reads mapped independently" />
+ <param name="seg_length" type="integer" value="25" label="Minimum length of read segments" />
+ <!-- Closure search. -->
+ <conditional name="closure_search">
+ <param name="use_search" type="select" label="Use Closure Search">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ <when value="Yes">
+ <param name="min_closure_exon" type="integer" value="50" label="During closure search for paired end reads, exonic hops in the potential splice graph must be at least this long. The default is 50." />
+ <param name="min_closure_intron" type="integer" value="50" label="Minimum intron length that may be found during closure search" />
+ <param name="max_closure_intron" type="integer" value="5000" label="Maximum intron length that may be found during closure search" />
+ </when>
+ <when value="No" />
+ </conditional>
+ <!-- Coverage search. -->
+ <conditional name="coverage_search">
+ <param name="use_search" type="select" label="Use Coverage Search">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ <when value="Yes">
+ <param name="min_coverage_intron" type="integer" value="50" label="Minimum intron length that may be found during coverage search" />
+ <param name="max_coverage_intron" type="integer" value="20000" label="Maximum intron length that may be found during coverage search" />
+ </when>
+ <when value="No" />
+ </conditional>
+ <param name="microexon_search" type="select" label="Use Microexon Search" help="With this option, the pipeline will attempt to find alignments incident to microexons. Works only for reads 50bp or longer.">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ </when> <!-- full -->
+ </conditional> <!-- sParams -->
</when>
<when value="paired">
- <param format="fastqsanger" name="input1" type="data" label="RNA-Seq FASTQ file" help="Must have Sanger-scaled quality values with ASCII offset 33"/>
- <param format="fastqsanger" name="input2" type="data" label="RNA-Seq FASTQ file" help="Must have Sanger-scaled quality values with ASCII offset 33"/>
- <param format="fastqsanger" name="mean_inner_distance" type="integer" value="20" label="Mean Inner Distance between Mate Pairs"/>
+ <param format="fastqsanger" name="input1" type="data" label="RNA-Seq FASTQ file" help="Must have Sanger-scaled quality values with ASCII offset 33"/>
+ <param format="fastqsanger" name="input2" type="data" label="RNA-Seq FASTQ file" help="Must have Sanger-scaled quality values with ASCII offset 33"/>
+ <param name="mate_inner_distance" type="integer" value="20" label="Mean Inner Distance between Mate Pairs" />
+ <conditional name="pParams">
+ <param name="pSettingsType" type="select" label="TopHat settings to use" help="For most mapping needs use Commonly used settings. If you want full control use Full parameter list">
+ <option value="preSet">Commonly used</option>
+ <option value="full">Full parameter list</option>
+ </param>
+ <when value="preSet" />
+ <!-- Full/advanced parms. -->
+ <when value="full">
+ <param name="mate_std_dev" type="integer" value="20" label="Std. Dev for Distance between Mate Pairs" help="The standard deviation for the distribution on inner distances between mate pairs."/>
+ <param name="anchor_length" type="integer" value="8" label="Anchor length (at least 3)" help="Report junctions spanned by reads with at least this many bases on each side of the junction." />
+ <param name="splice_mismatches" type="integer" value="0" label="Maximum number of mismatches that can appear in the anchor region of spliced alignment" />
+ <param name="min_intron_length" type="integer" value="70" label="The minimum intron length" help="TopHat will ignore donor/acceptor pairs closer than this many bases apart." />
+ <param name="max_intron_length" type="integer" value="500000" label="The maximum intron length" help="When searching for junctions ab initio, TopHat will ignore donor/acceptor pairs farther than this many bases apart, except when such a pair is supported by a split segment alignment of a long read." />
+ <param name="junction_filter" type="float" value="0.15" label="Minimum isoform fraction: filter out junctions supported by too few alignments (number of reads divided by average depth of coverage)" help="0.0 to 1.0 (0 to turn off)" />
+ <param name="max_multihits" type="integer" value="40" label="Maximum number of alignments to be allowed" />
+ <param name="min_segment_intron" type="integer" value="50" label="Minimum intron length that may be found during split-segment (default) search" />
+ <param name="max_segment_intron" type="integer" value="500000" label="Maximum intron length that may be found during split-segment (default) search" />
+ <param name="seg_mismatches" type="integer" value="2" label="Number of mismatches allowed in each segment alignment for reads mapped independently" />
+ <param name="seg_length" type="integer" value="25" label="Minimum length of read segments" />
+ <!-- Closure search. -->
+ <conditional name="closure_search">
+ <param name="use_search" type="select" label="Use Closure Search">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ <when value="Yes">
+ <param name="min_closure_exon" type="integer" value="50" label="During closure search for paired end reads, exonic hops in the potential splice graph must be at least this long. The default is 50." />
+ <param name="min_closure_intron" type="integer" value="50" label="Minimum intron length that may be found during closure search" />
+ <param name="max_closure_intron" type="integer" value="5000" label="Maximum intron length that may be found during closure search" />
+ </when>
+ <when value="No" />
+ </conditional>
+ <!-- Coverage search. -->
+ <conditional name="coverage_search">
+ <param name="use_search" type="select" label="Use Coverage Search">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ <when value="Yes">
+ <param name="min_coverage_intron" type="integer" value="50" label="Minimum intron length that may be found during coverage search" />
+ <param name="max_coverage_intron" type="integer" value="20000" label="Maximum intron length that may be found during coverage search" />
+ </when>
+ <when value="No" />
+ </conditional>
+ <param name="microexon_search" type="select" label="Use Microexon Search" help="With this option, the pipeline will attempt to find alignments incident to microexons. Works only for reads 50bp or longer.">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ </when> <!-- full -->
+ </conditional> <!-- pParams -->
</when>
</conditional>
</inputs>
-
+
<outputs>
- <data format="sam" name="accepted_hits" label="${tool.name} on ${on_string}: accepted_hits"/>
- <data format="wig" name="coverage" label="${tool.name} on ${on_string}: coverage"/>
+ <data format="sam" name="accepted_hits" label="${tool.name} on ${on_string}: accepted_hits"/>
+ <data format="bedgraph" name="coverage" label="${tool.name} on ${on_string}: coverage"/>
<data format="bed" name="junctions" label="${tool.name} on ${on_string}: splice junctions"/>
</outputs>
-
+
<tests>
+<!-- <test>
+ <param name="genomeSource" value="indexed"/>
+ <param name="index" value="equCab2chrM"/>
+ <param name="sPaired" value="single"/>
+ <param name="input1" ftype="fastqsanger" value="tophat_in1.fq"/>
+ <param name="sSettingsType" value="preSet"/>
+--> <!--
+ Can't test this right now because first lines of file are run-specific.
+ <output name="accepted_hits" file="tophat_out1.sam"/>
+ -->
+<!-- <output name="coverage" file="tophat_out2.wig"/>
+ <output name="junctions" file="tophat_out3.bed"/>
+ </test>
+-->
+ <!-- Test using test data: paired-end reads, index from history. -->
<test>
- <param name="genomeSource" value="indexed"/>
- <param name="index" value="test_ref"/>
+ <param name="genomeSource" value="history"/>
+ <param name="ownFile" ftype="fasta" value="tophat_in3.fa"/>
<param name="sPaired" value="paired"/>
<param name="input1" ftype="fastqsanger" value="tophat_in1.fq"/>
<param name="input2" ftype="fastqsanger" value="tophat_in2.fq"/>
- <param name="mean_inner_distance" value="20"/>
- <!--
- Can't test this right now because first lines of file are run-specific.
+ <param name="mate_inner_distance" value="20"/>
+ <param name="pSettingsType" value="preSet"/>
<output name="accepted_hits" file="tophat_out1.sam"/>
- -->
<output name="coverage" file="tophat_out2.wig"/>
<output name="junctions" file="tophat_out3.bed"/>
</test>
- </tests>
-
+<!-- <test>
+ <param name="genomeSource" value="history"/>
+ <param name="ownFile" value="phiX.fasta"/>
+ <param name="sPaired" value="single"/>
+ <param name="input1" ftype="fastqsanger" value="tophat_in1.fq"/>
+ <param name="sSettingsType" value="full"/>
+ <param name="anchor_length" value="8"/>
+ <param name="splice_mismatches" value="0"/>
+ <param name="min_intron_length" value="70"/>
+ <param name="max_intron_length" value="500000"/>
+ <param name="quals_scale" value="default"/>
+ <param name="junction_filter" value="0.15"/>
+ <param name="max_multihits" value="40"/>
+ <param name="min_segment_intron" value="50" />
+ <param name="max_segment_intron" value="500000" />
+ <param name="seg_mismatches" value="2"/>
+ <param name="seg_length" value="25"/>
+--> <!--
+ Can't test this right now because first lines of file are run-specific.
+ <output name="accepted_hits" file="tophat_out1.sam"/>
+ -->
+<!-- <output name="coverage" file="tophat_out2.wig"/>
+ <output name="junctions" file="tophat_out3.bed"/>
+ </test>
+ <test>
+ <param name="genomeSource" value="indexed"/>
+ <param name="index" value="equCab2chrM"/>
+ <param name="sPaired" value="paired"/>
+ <param name="input1" ftype="fastqsanger" value="tophat_in1.fq"/>
+ <param name="input2" ftype="fastqsanger" value="tophat_in2.fq"/>
+ <param name="mate_inner_distance" value="20"/>
+ <param name="pSettingsType" value="full"/>
+ <param name="mate_std_dev" value="20"/>
+ <param name="anchor_length" value="8"/>
+ <param name="splice_mismatches" value="0"/>
+ <param name="min_intron_length" value="70"/>
+ <param name="max_intron_length" value="500000"/>
+ <param name="quals_scale" value="default"/>
+ <param name="junction_filter" value="0.15"/>
+ <param name="max_multihits" value="40"/>
+ <param name="min_coverage_intron" value="50" />
+ <param name="max_coverage_intron" value="20000" />
+ <param name="seg_mismatches" value="2"/>
+ <param name="seg_length" value="25"/>
+--> <!--
+ Can't test this right now because first lines of file are run-specific.
+ <output name="accepted_hits" file="tophat_out1.sam"/>
+ -->
+<!-- <output name="coverage" file="tophat_out2.wig"/>
+ <output name="junctions" file="tophat_out3.bed"/>
+ </test>
+--> </tests>
+
<help>
**Tophat Overview**
@@ -104,11 +347,15 @@
**Outputs**
-Tophat produces three output files::
+Tophat produces three output files:
- coverage.wig -- coverage of reads
- accepted_hits.sam -- reads that were mapped onto genome
- junctions.bed -- splice junctions identified by Tophat
+- coverage.wig -- A UCSC BedGraph_ wigglegram track, showing the depth of coverage at each position, including the spliced read alignments.
+- accepted_hits.sam -- A list of read alignments in SAM_ format.
+- junctions.bed -- A UCSC BED_ track of junctions reported by TopHat. Each junction consists of two connected BED blocks, where each block is as long as the maximal overhang of any read spanning the junction. The score is the number of alignments spanning the junction.
+
+.. _BedGraph: http://genome.ucsc.edu/goldenPath/help/bedgraph.html
+.. _SAM: http://samtools.sourceforge.net/
+.. _BED: http://genome.ucsc.edu/FAQ/FAQformat.html#format1
-------
@@ -122,8 +369,35 @@
This is a list of implemented Tophat options::
- -r This is the expected (mean) inner distance between mate pairs. For, example, for paired end runs with fragments
- selected at 300bp, where each end is 50bp, you should set -r to be 200. There is no default, and this parameter
- is required for paired end runs.
+ -r This is the expected (mean) inner distance between mate pairs. For, example, for paired end runs with fragments
+ selected at 300bp, where each end is 50bp, you should set -r to be 200. There is no default, and this parameter
+ is required for paired end runs.
+ --mate-std-dev INT The standard deviation for the distribution on inner distances between mate pairs. The default is 20bp.
+ -a/--min-anchor-length INT The "anchor length". TopHat will report junctions spanned by reads with at least this many bases on each side of the junction. Note that individual spliced
+ alignments may span a junction with fewer than this many bases on one side. However, every junction involved in spliced alignments is supported by at least one
+ read with this many bases on each side. This must be at least 3 and the default is 8.
+ -m/--splice-mismatches INT The maximum number of mismatches that may appear in the "anchor" region of a spliced alignment. The default is 0.
+ -i/--min-intron-length INT The minimum intron length. TopHat will ignore donor/acceptor pairs closer than this many bases apart. The default is 70.
+ -I/--max-intron-length INT The maximum intron length. When searching for junctions ab initio, TopHat will ignore donor/acceptor pairs farther than this many bases apart, except when such a pair is supported by a split segment alignment of a long read. The default is 500000.
+ -F/--min-isoform-fraction 0.0-1.0 TopHat filters out junctions supported by too few alignments. Suppose a junction spanning two exons, is supported by S reads. Let the average depth of coverage of
+ exon A be D, and assume that it is higher than B. If S / D is less than the minimum isoform fraction, the junction is not reported. A value of zero disables the
+ filter. The default is 0.15.
+ -g/--max-multihits INT Instructs TopHat to allow up to this many alignments to the reference for a given read, and suppresses all alignments for reads with more than this many
+ alignments. The default is 40.
+ --no-closure-search Disables the mate pair closure-based search for junctions. Currently, has no effect - closure search is off by default.
+ --closure-search Enables the mate pair closure-based search for junctions. Closure-based search should only be used when the expected inner distance between mates is small (about or less than 50bp)
+ --no-coverage-search Disables the coverage based search for junctions.
+ --coverage-search Enables the coverage based search for junctions. Use when coverage search is disabled by default (such as for reads 75bp or longer), for maximum sensitivity.
+ --microexon-search With this option, the pipeline will attempt to find alignments incident to microexons. Works only for reads 50bp or longer.
+ --butterfly-search TopHat will use a slower but potentially more sensitive algorithm to find junctions in addition to its standard search. Consider using this if you expect that your experiment produced a lot of reads from pre-mRNA, that fall within the introns of your transcripts.
+ --segment-mismatches Read segments are mapped independently, allowing up to this many mismatches in each segment alignment. The default is 2.
+ --segment-length Each read is cut up into segments, each at least this long. These segments are mapped independently. The default is 25.
+ --min-closure-exon During closure search for paired end reads, exonic hops in the potential splice graph must be at least this long. The default is 50.
+ --min-closure-intron The minimum intron length that may be found during closure search. The default is 50.
+ --max-closure-intron The maximum intron length that may be found during closure search. The default is 5000.
+ --min-coverage-intron The minimum intron length that may be found during coverage search. The default is 50.
+ --max-coverage-intron The maximum intron length that may be found during coverage search. The default is 20000.
+ --min-segment-intron The minimum intron length that may be found during split-segment search. The default is 50.
+ --max-segment-intron The maximum intron length that may be found during split-segment search. The default is 500000.
</help>
</tool>
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/853046b87a5c
changeset: 3718:853046b87a5c
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Fri Apr 30 10:15:42 2010 -0400
description:
Fix for backwards compatibility of HDAs being referenced as an integer. A hash that happened to be an integer would previously cause error.
diffstat:
lib/galaxy/web/base/controller.py | 12 ++++++++----
lib/galaxy/web/controllers/dataset.py | 12 ++++++++----
2 files changed, 16 insertions(+), 8 deletions(-)
diffs (44 lines):
diff -r c09a8456b63e -r 853046b87a5c lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py Thu Apr 29 19:57:00 2010 -0400
+++ b/lib/galaxy/web/base/controller.py Fri Apr 30 10:15:42 2010 -0400
@@ -122,10 +122,14 @@
""" Get an HDA object by id. """
# DEPRECATION: We still support unencoded ids for backward compatibility
try:
- dataset_id = int( dataset_id )
- except ValueError:
- dataset_id = trans.security.decode_id( dataset_id )
- data = trans.sa_session.query( trans.model.HistoryDatasetAssociation ).get( dataset_id )
+ data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ if data is None:
+ raise ValueError( 'Invalid reference dataset id: %s.' % dataset_id )
+ except:
+ try:
+ data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( int( dataset_id ) )
+ except:
+ data = None
if not data:
raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid dataset id: %s." % str( dataset_id ) )
if check_ownership:
diff -r c09a8456b63e -r 853046b87a5c lib/galaxy/web/controllers/dataset.py
--- a/lib/galaxy/web/controllers/dataset.py Thu Apr 29 19:57:00 2010 -0400
+++ b/lib/galaxy/web/controllers/dataset.py Fri Apr 30 10:15:42 2010 -0400
@@ -307,10 +307,14 @@
composite_extensions.append('html') # for archiving composite datatypes
# DEPRECATION: We still support unencoded ids for backward compatibility
try:
- dataset_id = int( dataset_id )
- except ValueError:
- dataset_id = trans.security.decode_id( dataset_id )
- data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dataset_id )
+ data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ if data is None:
+ raise ValueError( 'Invalid reference dataset id: %s.' % dataset_id )
+ except:
+ try:
+ data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( int( dataset_id ) )
+ except:
+ data = None
if not data:
raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % str( dataset_id ) )
current_user_roles = trans.get_current_user_roles()
1
0

10 May '10
details: http://www.bx.psu.edu/hg/galaxy/rev/436c9d1c5e64
changeset: 3716:436c9d1c5e64
user: Kanwei Li <kanwei(a)gmail.com>
date: Wed Apr 28 17:35:02 2010 -0400
description:
trackster: BAM display supports paired-end reads, displays sequence when possible
diffstat:
lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.py | 2 +-
lib/galaxy/visualization/tracks/data/bam.py | 34 +-
lib/galaxy/visualization/tracks/data/summary_tree.py | 4 +-
lib/galaxy/visualization/tracks/summary.py | 6 +-
lib/galaxy/web/controllers/tracks.py | 8 +-
static/scripts/packed/trackster.js | 2 +-
static/scripts/trackster.js | 284 +++++----
7 files changed, 185 insertions(+), 155 deletions(-)
diffs (524 lines):
diff -r a5ccb2ba4c74 -r 436c9d1c5e64 lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.py
--- a/lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.py Wed Apr 28 16:06:58 2010 -0400
+++ b/lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.py Wed Apr 28 17:35:02 2010 -0400
@@ -17,7 +17,7 @@
bamfile = csamtools.Samfile( filename=input_fname, mode='rb', index_filename=index_fname )
- st = SummaryTree(block_size=100, levels=4, draw_cutoff=100, detail_cutoff=20)
+ st = SummaryTree(block_size=25, levels=6, draw_cutoff=150, detail_cutoff=30)
for read in bamfile.fetch():
st.insert_range(bamfile.getrname(read.rname), read.pos, read.pos + read.rlen)
diff -r a5ccb2ba4c74 -r 436c9d1c5e64 lib/galaxy/visualization/tracks/data/bam.py
--- a/lib/galaxy/visualization/tracks/data/bam.py Wed Apr 28 16:06:58 2010 -0400
+++ b/lib/galaxy/visualization/tracks/data/bam.py Wed Apr 28 17:35:02 2010 -0400
@@ -33,12 +33,36 @@
if chrom.startswith( 'chr' ):
data = bamfile.fetch( start=start, end=end, reference=chrom[3:] )
else:
- raise
+ return None
# Encode reads as list of dictionaries
results = []
+ paired_pending = {}
for read in data:
- payload = [ str(read.pos) + str(read.seq), read.pos, read.pos + read.rlen, read.seq ]
- results.append(payload)
+ qname = read.qname
+ if read.is_proper_pair:
+ if qname in paired_pending: # one in dict is always first
+ pair = paired_pending[qname]
+ results.append( [ qname, pair['start'], read.pos + read.rlen, read.seq, [pair['start'], pair['end'], pair['seq']], [read.pos, read.pos + read.rlen, read.seq] ] )
+ # results.append( [read.qname, pair['start'], read.pos + read.rlen, qname, [pair['start'], pair['end']], [read.pos, read.pos + read.rlen] ] )
+ del paired_pending[qname]
+ else:
+ paired_pending[qname] = { 'start': read.pos, 'end': read.pos + read.rlen, 'seq': read.seq, 'mate_start': read.mpos, 'rlen': read.rlen }
+ else:
+ results.append( [qname, read.pos, read.pos + read.rlen, read.seq] )
+ # take care of reads whose mates are out of range
+ for qname, read in paired_pending.iteritems():
+ if read['mate_start'] < read['start']:
+ start = read['mate_start']
+ end = read['end']
+ r1 = [read['mate_start'], read['mate_start'] + read['rlen']]
+ r2 = [read['start'], read['end'], read['seq']]
+ else:
+ start = read['start']
+ end = read['mate_start'] + read['rlen']
+ r1 = [read['start'], read['end'], read['seq']]
+ r2 = [read['mate_start'], read['mate_start'] + read['rlen']]
+
+ results.append( [ qname, start, end, read['seq'], r1, r2 ] )
+
bamfile.close()
- return results
-
\ No newline at end of file
+ return results
\ No newline at end of file
diff -r a5ccb2ba4c74 -r 436c9d1c5e64 lib/galaxy/visualization/tracks/data/summary_tree.py
--- a/lib/galaxy/visualization/tracks/data/summary_tree.py Wed Apr 28 16:06:58 2010 -0400
+++ b/lib/galaxy/visualization/tracks/data/summary_tree.py Wed Apr 28 17:35:02 2010 -0400
@@ -38,6 +38,6 @@
if results == "detail":
return None
elif results == "draw":
- return "no_detail", None, None
+ return "no_detail"
else:
- return results, stats["max"], stats["avg"]
+ return results, stats[level]["max"], stats[level]["avg"], stats[level]["delta"]
diff -r a5ccb2ba4c74 -r 436c9d1c5e64 lib/galaxy/visualization/tracks/summary.py
--- a/lib/galaxy/visualization/tracks/summary.py Wed Apr 28 16:06:58 2010 -0400
+++ b/lib/galaxy/visualization/tracks/summary.py Wed Apr 28 17:35:02 2010 -0400
@@ -54,8 +54,10 @@
self.chrom_stats[chrom]["detail_level"] = level
break
else:
- self.chrom_stats[chrom]["max"] = max_val
- self.chrom_stats[chrom]["avg"] = float(max_val) / len(blocks[level])
+ self.chrom_stats[chrom][level] = {}
+ self.chrom_stats[chrom][level]["delta"] = self.block_size ** level
+ self.chrom_stats[chrom][level]["max"] = max_val
+ self.chrom_stats[chrom][level]["avg"] = float(max_val) / len(blocks[level])
cur_best = level
self.chrom_blocks[chrom] = dict([ (key, value) for key, value in blocks.iteritems() if key >= cur_best ])
diff -r a5ccb2ba4c74 -r 436c9d1c5e64 lib/galaxy/web/controllers/tracks.py
--- a/lib/galaxy/web/controllers/tracks.py Wed Apr 28 16:06:58 2010 -0400
+++ b/lib/galaxy/web/controllers/tracks.py Wed Apr 28 17:35:02 2010 -0400
@@ -236,12 +236,12 @@
indexer = dataset_type_to_data_provider[data_sources['index']]( dataset.get_converted_dataset(trans, data_sources['index']), dataset )
summary = indexer.get_summary( chrom, low, high, **kwargs )
if summary is not None:
- frequencies, max_v, avg_v = summary
- if frequencies != "no_detail":
- return { "dataset_type": data_sources['index'], "data": frequencies, "max": max_v, "avg": avg_v }
- else:
+ if summary == "no_detail":
kwargs["no_detail"] = True # meh
extra_info = "no_detail"
+ else:
+ frequencies, max_v, avg_v, delta = summary
+ return { "dataset_type": data_sources['index'], "data": frequencies, "max": max_v, "avg": avg_v, "delta": delta }
dataset_type = data_sources['data']
data_provider = dataset_type_to_data_provider[ dataset_type ]( dataset.get_converted_dataset(trans, dataset_type), dataset )
diff -r a5ccb2ba4c74 -r 436c9d1c5e64 static/scripts/packed/trackster.js
--- a/static/scripts/packed/trackster.js Wed Apr 28 16:06:58 2010 -0400
+++ b/static/scripts/packed/trackster.js Wed Apr 28 17:35:02 2010 -0400
@@ -1,1 +1,1 @@
-var DEBUG=false;var DENSITY=1000,FEATURE_LEVELS=10,DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="../images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="../images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="../images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,!
"repeat")};var left_img_inv=new Image();left_img_inv.src="../images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var Drawer=function(){};$.extend(Drawer.prototype,{intensity:function(b,a,c){},});drawer=new Drawer();var View=function(b,d,c,a){this.vis_id=c;this.dbkey=a;this.title=d;this.chrom=b;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_h!
igh=0;this.center=(this.max_high-this.max_low)/2;this.zoom_factor=3;th
is.zoom_level=0;this.track_id_counter=0};$.extend(View.prototype,{add_track:function(a){a.view=this;a.track_id=this.track_id_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[a]},update_options:function(){var b=$("ul#sortable-ul").sortable("toArray");var d=[];var c=$("#viewport > div").sort(function(g,f){return b.indexOf($(g).attr("id"))>b.indexOf($(f).attr("id"))});$("#viewport > div").remove();$("#viewport").html(c);for(var e in view.tracks){var a=view.tracks[e];if(a.update_options){a.update_options(e)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.center=this.center=(this.max_high-this.max_low)/2;this.zoom_level=0;$(".yaxislabel").remove()},redraw:function(f){this.span=this.max_high-this.max_low;var d=this.spa!
n/Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){e=this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(a,b){if(this.max_high===0||this.high-this.low<30){return}if(a){this.center=a/b.width()*(this.h!
igh-this.low)+this.low}this.zoom_level+=1;this.redraw()},zoom_out:func
tion(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1;this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div></div>").addClass("track").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.content_div.css("height","30px");if(!a.content_div.text()){a.content_div.text(DATA_LOADING)}a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_N!
OCONVERTER)}else{if(d.data&&d.data.length===0||d.data===null){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,e=this.view.high,f=e-i,d=this.view.resolution;if(DEBUG){$("#debug").text(d+" "+this.view.zoom_res)}var k=$("<div style='position: relative;'></div>");this.content_div.children(":first").remove();this.content_div.append(k);var l=this.content_div.width()/f;var h;var a=Math.floor(i/d/DENSITY);while((a*DENSITY*d)<e){var j=this.content_div.width()+"_"+this.view.zoom_level+"_"+a;var c=this.tile_cache.get(j);if(c){var g=a*DENSITY*d;var b=(g-!
i)*l;if(this.left_offset){b-=this.left_offset}c.css({left:b});k.append
(c);this.max_height=Math.max(this.max_height,c.height())}else{this.delayed_draw(this,j,i,e,a,d,k,l)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)}});var LabelTrack=function(a){Track.call(this,null,a);this.track_type="LabelTrack";this.hidden=true;this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove!
();this.content_div.append(b)}});var LineTrack=function(c,a,b){this.track_type="LineTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("line-track");this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={min_value:undefined,max_value:undefined,mode:"Line"};if(b.min_value!==undefined){this.prefs.min_value=b.min_value}if(b.max_value!==undefined){this.prefs.max_value=b.max_value}if(b.mode!==undefined){this.prefs.mode=b.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_!
maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.p
refs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var d=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,q,c,e){if(this.vertical_range===undefined){return}var r=q*DENSITY*o,a=DENSITY*o,!
b=$("<canvas class='tile'></canvas>"),u=o+"_"+q;if(this.data_cache.get(u)===undefined){this.get_data(o,q);return}var t=this.data_cache.get(u);if(t===null){return}b.css({position:"absolute",top:0,left:(r-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,s=this.total_frequency,d=this.height_px;n.beginPath();if(t.length>1){var f=Math.ceil((t[1][0]-t[0][0])*e)}else{var f=10}for(var p=0;p<t.length;p++){var j=t[p][0]-r;var h=t[p][1];if(this.prefs.mode=="Intensity"){if(h===null){continue}j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(j,0,f,this.height_px)}else{if(h===null){k=false;continue}else{j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/m*d);if(k){n.lineTo(j,h)}else{n.moveTo(j,h);k=true}}}}n.stroke();c.append(b);return b},gen_options:function(n){var a=$("<div></!
div>").addClass("form-row");var h="track_"+n+"_minval",k="track_"+n+"_
maxval",e="track_"+n+"_mode",l=$("<label></label>").attr("for",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),m=$("<input></input>").attr("id",h).val(b),g=$("<label></label>").attr("for",k).text("Max value:"),j=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",k).val(j),d=$("<label></label>").attr("for",e).text("Display mode:"),i=(this.prefs.mode===undefined?"Line":this.prefs.mode),c=$('<select id="'+e+'"><option value="Line" id="mode_Line">Line</option><option value="Intensity" id="mode_Intensity">Intensity</option></select>');c.children("#mode_"+i).attr("selected","selected");return a.append(l).append(m).append(g).append(f).append(d).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_mode option:selected").val();if(a!==this.prefs.min_value||c!==this.prefs.max_value||b!=this.prefs.mode){this.prefs.min_value=parseFloat(a);th!
is.prefs.max_value=parseFloat(c);this.prefs.mode=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(c,a,b){this.track_type="FeatureTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=3;this.default_font="9px Monaco, Lucida Console, monospace";this.left_offset=200;this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.prefs={block_color:"black",label_color:"black",show_counts:false};if(b.block_color!==undefined){this.prefs.block_color=b.block_color}if(b.label_color!==undefined){this.prefs.!
label_color=b.label_color}if(b.show_counts!==undefined){this.prefs.sho
w_counts=b.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution},function(c){a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.resolution},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,c){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,v=[],h=0,b=$("<canvas></canvas>").get(0).getContext("2d"),n=this.view.max_low;var d,f,x=[];for(var s=0,t=g.length;s<t;s++){var e=g[s],l=e[0];if(this.inc_slots[a][l]!==undefined){h=Math.max(h,this.inc_slots[a][l]);x.push(this.inc_slots[a][l]!
)}else{v.push(s)}}for(var s=0,t=v.length;s<t;s++){var e=g[v[s]];l=e[0],feature_start=e[1],feature_end=e[2],feature_name=e[3];d=Math.floor((feature_start-n)*m);f=Math.ceil((feature_end-n)*m);if(!c){var p=b.measureText(feature_name).width;if(d-p<0){f+=p}else{d-=p}}var r=0;while(true){var o=true;if(this.s_e_by_tile[a][r]!==undefined){for(var q=0,w=this.s_e_by_tile[a][r].length;q<w;q++){var u=this.s_e_by_tile[a][r][q];if(f>u[0]&&d<u[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][r]===undefined){this.s_e_by_tile[a][r]=[]}this.s_e_by_tile[a][r].push([d,f]);this.inc_slots[a][l]=r;h=Math.max(h,r);break}r++}}return h},draw_tile:function(R,h,m,ae){var z=h*DENSITY*R,X=(h+1)*DENSITY*R,w=DENSITY*R;var ac,ad,p;var Y=z+"_"+X;var ac=this.data_cache.get(Y);if(ac===undefined){this.data_queue[[z,X]]=true;this.get_data(z,X);return}if(ac.dataset_type=="array_tree"){p=30}else{var P=(ac.extra_info==="no_detail");var af=(P?this.vertical_nodetail_px:this.vertical_detail_px);p=this.incremental_slo!
ts(this.view.zoom_res,ac.data,P)*af+15;m.parent().css("height",Math.ma
x(this.height_px,p)+"px");ad=this.inc_slots[this.view.zoom_res]}var a=Math.ceil(w*ae),F=$("<canvas class='tile'></canvas>"),T=this.prefs.label_color,f=this.prefs.block_color,J=this.left_offset;F.css({position:"absolute",top:0,left:(z-this.view.low)*ae-J});F.get(0).width=a+J;F.get(0).height=p;var t=F.get(0).getContext("2d");t.fillStyle=this.prefs.block_color;t.font=this.default_font;t.textAlign="right";var C=55,W=255-C,g=W*2/3;if(ac.dataset_type=="summary_tree"){var L=ac.data;var v=ac.max;var l=ac.avg;if(ac.data.length>2){var b=Math.ceil((L[1][0]-L[0][0])*ae)}else{var b=50}for(var aa=0,s=L.length;aa<s;aa++){var N=Math.ceil((L[aa][0]-z)*ae);var M=L[aa][1];if(!M){continue}var E=Math.floor(W-(M/v)*W);t.fillStyle="rgb("+E+","+E+","+E+")";t.fillRect(N+J,0,b,20);if(this.prefs.show_counts){if(E>g){t.fillStyle="black"}else{t.fillStyle="#ddd"}t.textAlign="center";t.fillText(L[aa][1],N+J+(b/2),12)}}m.append(F);return F}var ac=ac.data;var Z=0;for(var aa=0,s=ac.length;aa<s;aa++){var G=ac!
[aa],D=G[0],ab=G[1],O=G[2],A=G[3];if(ab<=X&&O>=z){var Q=Math.floor(Math.max(0,(ab-z)*ae)),u=Math.ceil(Math.min(a,(O-z)*ae)),K=ad[D]*af;if(P){t.fillRect(Q+J,K+5,u-Q,1)}else{var r=G[4],I=G[5],S=G[6],e=G[7];var q,U,B=null,ag=null;if(I&&S){B=Math.floor(Math.max(0,(I-z)*ae));ag=Math.ceil(Math.min(a,(S-z)*ae))}if(A!==undefined&&ab>z){t.fillStyle=T;if(h===0&&Q-t.measureText(A).width<0){t.textAlign="left";t.fillText(A,u+2+J,K+8)}else{t.textAlign="right";t.fillText(A,Q-2+J,K+8)}t.fillStyle=f}if(e){if(r){if(r=="+"){t.fillStyle=RIGHT_STRAND}else{if(r=="-"){t.fillStyle=LEFT_STRAND}}t.fillRect(Q+J,K,u-Q,10);t.fillStyle=f}for(var Y=0,d=e.length;Y<d;Y++){var n=e[Y],c=Math.floor(Math.max(0,(n[0]-z)*ae)),H=Math.ceil(Math.min(a,(n[1]-z)*ae));if(c>H){continue}q=5;U=3;t.fillRect(c+J,K+U,H-c,q);if(B!==undefined&&!(c>ag||H<B)){q=9;U=1;var V=Math.max(c,B),o=Math.min(H,ag);t.fillRect(V+J,K+U,o-V,q)}}}else{q=9;U=1;t.fillRect(Q+J,K+U,u-Q,q);if(G.strand){if(G.strand=="+"){t.fillStyle=RIGHT_STRAND_INV!
}else{if(G.strand=="-"){t.fillStyle=LEFT_STRAND_INV}}t.fillRect(Q+J,K,
u-Q,10);t.fillStyle=prefs.block_color}}}Z++}}m.append(F);return F},gen_options:function(i){var a=$("<div></div>").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label></label>").attr("for",e).text("Block color:"),l=$("<input></input>").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label></label>").attr("for",j).text("Text color:"),h=$("<input></input>").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label></label>").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div></div>").append(b).append(c);return a.append(k).append(l).append(g).append(h).append(d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.labe!
l_color||a!=this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(c,a,b){FeatureTrack.call(this,c,a,b);this.track_type="ReadTrack"};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
\ No newline at end of file
+var DEBUG=false;var DENSITY=200,FEATURE_LEVELS=10,DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="../images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="../images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="../images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,"!
repeat")};var left_img_inv=new Image();left_img_inv.src="../images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var Drawer=function(){};$.extend(Drawer.prototype,{intensity:function(b,a,c){},});drawer=new Drawer();var View=function(b,d,c,a){this.vis_id=c;this.dbkey=a;this.title=d;this.chrom=b;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_hi!
gh=0;this.center=(this.max_high-this.max_low)/2;this.zoom_factor=3;thi
s.zoom_level=0;this.track_id_counter=0};$.extend(View.prototype,{add_track:function(a){a.view=this;a.track_id=this.track_id_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[a]},update_options:function(){var b=$("ul#sortable-ul").sortable("toArray");var d=[];var c=$("#viewport > div").sort(function(g,f){return b.indexOf($(g).attr("id"))>b.indexOf($(f).attr("id"))});$("#viewport > div").remove();$("#viewport").html(c);for(var e in view.tracks){var a=view.tracks[e];if(a.update_options){a.update_options(e)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.center=this.center=(this.max_high-this.max_low)/2;this.zoom_level=0;$(".yaxislabel").remove()},redraw:function(f){this.span=this.max_high-this.max_low;var d=this.span!
/Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){e=this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(a,b){if(this.max_high===0||this.high-this.low<30){return}if(a){this.center=a/b.width()*(this.hi!
gh-this.low)+this.low}this.zoom_level+=1;this.redraw()},zoom_out:funct
ion(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1;this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div></div>").addClass("track").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.content_div.css("height","30px");if(!a.content_div.text()){a.content_div.text(DATA_LOADING)}a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NO!
CONVERTER)}else{if(d.data&&d.data.length===0||d.data===null){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,e=this.view.high,f=e-i,d=this.view.resolution;if(DEBUG){$("#debug").text(d+" "+this.view.zoom_res)}var k=$("<div style='position: relative;'></div>"),l=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(k),this.max_height=0;var a=Math.floor(i/d/DENSITY);while((a*DENSITY*d)<e){var j=this.content_div.width()+"_"+this.view.zoom_level+"_"+a;var c=this.tile_cache.get(j);if(c){var g=a*DENSITY*d;!
var b=(g-i)*l;if(this.left_offset){b-=this.left_offset}c.css({left:b})
;k.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height+"px")}else{this.delayed_draw(this,j,i,e,a,d,k,l)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)}});var LabelTrack=function(a){Track.call(this,null,a);this.track_type="LabelTrack";this.hidden=true;this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute!
",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var LineTrack=function(c,a,b){this.track_type="LineTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("line-track");this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={min_value:undefined,max_value:undefined,mode:"Line"};if(b.min_value!==undefined){this.prefs.min_value=b.min_value}if(b.max_value!==undefined){this.prefs.max_value=b.max_value}if(b.mode!==undefined){this.prefs.mode=b.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("!
#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").
val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var d=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,q,c,e){if(this.vert!
ical_range===undefined){return}var r=q*DENSITY*o,a=DENSITY*o,b=$("<canvas class='tile'></canvas>"),u=o+"_"+q;if(this.data_cache.get(u)===undefined){this.get_data(o,q);return}var t=this.data_cache.get(u);if(t===null){return}b.css({position:"absolute",top:0,left:(r-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,s=this.total_frequency,d=this.height_px;n.beginPath();if(t.length>1){var f=Math.ceil((t[1][0]-t[0][0])*e)}else{var f=10}for(var p=0;p<t.length;p++){var j=t[p][0]-r;var h=t[p][1];if(this.prefs.mode=="Intensity"){if(h===null){continue}j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(j,0,f,this.height_px)}else{if(h===null){k=false;continue}else{j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/m*d);if(k){n.lineTo(j,h)}else{n.moveTo(j,h);k=true}}}}n.stroke();c!
.append(b);return b},gen_options:function(n){var a=$("<div></div>").ad
dClass("form-row");var h="track_"+n+"_minval",k="track_"+n+"_maxval",e="track_"+n+"_mode",l=$("<label></label>").attr("for",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),m=$("<input></input>").attr("id",h).val(b),g=$("<label></label>").attr("for",k).text("Max value:"),j=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",k).val(j),d=$("<label></label>").attr("for",e).text("Display mode:"),i=(this.prefs.mode===undefined?"Line":this.prefs.mode),c=$('<select id="'+e+'"><option value="Line" id="mode_Line">Line</option><option value="Intensity" id="mode_Intensity">Intensity</option></select>');c.children("#mode_"+i).attr("selected","selected");return a.append(l).append(m).append(g).append(f).append(d).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_mode option:selected").val();if(a!==this.prefs.min_value||c!==this.prefs.max_val!
ue||b!=this.prefs.mode){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.mode=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(c,a,b){this.track_type="FeatureTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=3;this.default_font="9px Monaco, Lucida Console, monospace";this.left_offset=200;this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.prefs={block_color:"black",label_color:"black",show_counts:false};if(b.block_color!==undefined){this.prefs.block_!
color=b.block_color}if(b.label_color!==undefined){this.prefs.label_col
or=b.label_color}if(b.show_counts!==undefined){this.prefs.show_counts=b.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution},function(c){a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.resolution},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,c){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,v=[],h=0,b=$("<canvas></canvas>").get(0).getContext("2d"),n=this.view.max_low;var d,f,x=[];for(var s=0,t=g.length;s<t;s++){var e=g[s],l=e[0];if(this.inc_slots[a][l]!==undefined){h!
=Math.max(h,this.inc_slots[a][l]);x.push(this.inc_slots[a][l])}else{v.push(s)}}for(var s=0,t=v.length;s<t;s++){var e=g[v[s]];l=e[0],feature_start=e[1],feature_end=e[2],feature_name=e[3];d=Math.floor((feature_start-n)*m);f=Math.ceil((feature_end-n)*m);if(!c){var p=b.measureText(feature_name).width;if(d-p<0){f+=p}else{d-=p}}var r=0;while(true){var o=true;if(this.s_e_by_tile[a][r]!==undefined){for(var q=0,w=this.s_e_by_tile[a][r].length;q<w;q++){var u=this.s_e_by_tile[a][r][q];if(f>u[0]&&d<u[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][r]===undefined){this.s_e_by_tile[a][r]=[]}this.s_e_by_tile[a][r].push([d,f]);this.inc_slots[a][l]=r;h=Math.max(h,r);break}r++}}return h},rect_or_text:function(n,o,g,f,m,b,d,k,e,i){n.textAlign="center";var j=Math.round(o/2);if(d!==undefined&&o>g){n.fillStyle="#555";n.fillRect(k,i+1,e,9);n.fillStyle="#eee";for(var h=0,l=d.length;h<l;h++){if(b+h>=f&&b+h<=m){var a=Math.floor(Math.max(0,(b+h-f)*o));n.fillText(d[h],a+this.left_offset+j,i+9)}}}else!
{n.fillStyle="#555";n.fillRect(k,i+4,e,3)}},draw_tile:function(W,h,m,a
k){var D=h*DENSITY*W,ac=(h+1)*DENSITY*W,C=DENSITY*W;var aj,r;var ad=D+"_"+ac;var w=this.data_cache.get(ad);if(w===undefined){this.data_queue[[D,ac]]=true;this.get_data(D,ac);return}if(w.dataset_type==="summary_tree"){r=30}else{var U=(w.extra_info==="no_detail");var al=(U?this.vertical_nodetail_px:this.vertical_detail_px);r=this.incremental_slots(this.view.zoom_res,w.data,U)*al+15;m.parent().css("height",Math.max(this.height_px,r)+"px");aj=this.inc_slots[this.view.zoom_res]}var a=Math.ceil(C*ak),K=$("<canvas class='tile'></canvas>"),Y=this.prefs.label_color,f=this.prefs.block_color,O=this.left_offset;K.css({position:"absolute",top:0,left:(D-this.view.low)*ak-O});K.get(0).width=a+O;K.get(0).height=r;var z=K.get(0).getContext("2d"),ah=z.measureText("A").width;z.fillStyle=this.prefs.block_color;z.font=this.default_font;z.textAlign="right";if(w.dataset_type=="summary_tree"){var J,G=55,ab=255-G,g=ab*2/3,Q=w.data,B=w.max,l=w.avg;if(Q.length>2){var b=Math.ceil((Q[1][0]-Q[0][0])*ak)}!
else{var b=50}for(var af=0,v=Q.length;af<v;af++){var S=Math.ceil((Q[af][0]-D)*ak);var R=Q[af][1];if(!R){continue}J=Math.floor(ab-(R/B)*ab);z.fillStyle="rgb("+J+","+J+","+J+")";z.fillRect(S+O,0,b,20);if(this.prefs.show_counts){if(J>g){z.fillStyle="black"}else{z.fillStyle="#ddd"}z.textAlign="center";z.fillText(Q[af][1],S+O+(b/2),12)}}m.append(K);return K}var ai=w.data;var ae=0;for(var af=0,v=ai.length;af<v;af++){var L=ai[af],I=L[0],ag=L[1],T=L[2],E=L[3];if(ag<=ac&&T>=D){var V=Math.floor(Math.max(0,(ag-D)*ak)),A=Math.ceil(Math.min(a,Math.max(0,(T-D)*ak))),P=aj[I]*al;if(w.dataset_type==="bai"){z.fillStyle="#555";if(L[4] instanceof Array){var s=Math.floor(Math.max(0,(L[4][0]-D)*ak)),H=Math.ceil(Math.min(a,Math.max(0,(L[4][1]-D)*ak))),q=Math.floor(Math.max(0,(L[5][0]-D)*ak)),o=Math.ceil(Math.min(a,Math.max(0,(L[5][1]-D)*ak)));if(L[4][1]>=D&&L[4][0]<=ac){this.rect_or_text(z,ak,ah,D,ac,L[4][0],L[4][2],s+O,H-s,P)}if(L[5][1]>=D&&L[5][0]<=ac){this.rect_or_text(z,ak,ah,D,ac,L[5][0],L[5!
][2],q+O,o-q,P)}if(q>H){z.fillStyle="#999";z.fillRect(H+O,P+5,q-H,1)}}
else{z.fillStyle="#555";this.rect_or_text(z,ak,ah,D,ac,ag,E,V+O,A-V,P)}if(!U&&ag>D){z.fillStyle=this.prefs.label_color;if(h===0&&V-z.measureText(E).width<0){z.textAlign="left";z.fillText(I,A+2+O,P+8)}else{z.textAlign="right";z.fillText(I,V-2+O,P+8)}z.fillStyle="#555"}}else{if(w.dataset_type==="interval_index"){if(U){z.fillRect(V+O,P+5,A-V,1)}else{var u=L[4],N=L[5],X=L[6],e=L[7];var t,Z,F=null,am=null;if(N&&X){F=Math.floor(Math.max(0,(N-D)*ak));am=Math.ceil(Math.min(a,Math.max(0,(X-D)*ak)))}if(E!==undefined&&ag>D){z.fillStyle=Y;if(h===0&&V-z.measureText(E).width<0){z.textAlign="left";z.fillText(E,A+2+O,P+8)}else{z.textAlign="right";z.fillText(E,V-2+O,P+8)}z.fillStyle=f}if(e){if(u){if(u=="+"){z.fillStyle=RIGHT_STRAND}else{if(u=="-"){z.fillStyle=LEFT_STRAND}}z.fillRect(V+O,P,A-V,10);z.fillStyle=f}for(var ad=0,d=e.length;ad<d;ad++){var n=e[ad],c=Math.floor(Math.max(0,(n[0]-D)*ak)),M=Math.ceil(Math.min(a,Math.max((n[1]-D)*ak)));if(c>M){continue}t=5;Z=3;z.fillRect(c+O,P+Z,M-c,t);i!
f(F!==undefined&&!(c>am||M<F)){t=9;Z=1;var aa=Math.max(c,F),p=Math.min(M,am);z.fillRect(aa+O,P+Z,p-aa,t)}}}else{t=9;Z=1;z.fillRect(V+O,P+Z,A-V,t);if(L.strand){if(L.strand=="+"){z.fillStyle=RIGHT_STRAND_INV}else{if(L.strand=="-"){z.fillStyle=LEFT_STRAND_INV}}z.fillRect(V+O,P,A-V,10);z.fillStyle=prefs.block_color}}}}}ae++}}m.append(K);return K},gen_options:function(i){var a=$("<div></div>").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label></label>").attr("for",e).text("Block color:"),l=$("<input></input>").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label></label>").attr("for",j).text("Text color:"),h=$("<input></input>").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label></label>").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div></div>").appen!
d(b).append(c);return a.append(k).append(l).append(g).append(h).append
(d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!=this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(c,a,b){FeatureTrack.call(this,c,a,b);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
\ No newline at end of file
diff -r a5ccb2ba4c74 -r 436c9d1c5e64 static/scripts/trackster.js
--- a/static/scripts/trackster.js Wed Apr 28 16:06:58 2010 -0400
+++ b/static/scripts/trackster.js Wed Apr 28 17:35:02 2010 -0400
@@ -3,7 +3,7 @@
*/
var DEBUG = false;
-var DENSITY = 1000,
+var DENSITY = 200,
FEATURE_LEVELS = 10,
DATA_ERROR = "There was an error in indexing this dataset.",
DATA_NOCONVERTER = "A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",
@@ -271,16 +271,15 @@
range = high - low,
resolution = this.view.resolution;
-
if (DEBUG) { $("#debug").text(resolution + " " + this.view.zoom_res); }
- var parent_element = $("<div style='position: relative;'></div>");
- this.content_div.children( ":first" ).remove();
- this.content_div.append( parent_element );
+ var parent_element = $("<div style='position: relative;'></div>"),
+ w_scale = this.content_div.width() / range,
+ tile_element;
- var w_scale = this.content_div.width() / range;
-
- var tile_element;
+ this.content_div.children( ":first" ).remove();
+ this.content_div.append( parent_element ),
+ this.max_height = 0;
// Index of first tile that overlaps visible region
var tile_index = Math.floor( low / resolution / DENSITY );
while ( ( tile_index * DENSITY * resolution ) < high ) {
@@ -298,6 +297,7 @@
// Our responsibility to move the element to the new parent
parent_element.append( cached );
this.max_height = Math.max( this.max_height, cached.height() );
+ this.content_div.css("height", this.max_height + "px");
} else {
this.delayed_draw(this, key, low, high, tile_index, resolution, parent_element, w_scale);
}
@@ -311,7 +311,7 @@
if ( tile_element ) {
track.tile_cache.set(key, tile_element);
track.max_height = Math.max( track.max_height, tile_element.height() );
- track.content_div.css( "height", track.max_height + "px");
+ track.content_div.css("height", track.max_height + "px");
}
}
}, 50);
@@ -679,12 +679,30 @@
return highest_slot;
},
+ rect_or_text: function( ctx, w_scale, px_per_char, tile_low, tile_high, feature_start, name, x, x_len, y_center ) {
+ ctx.textAlign = "center";
+ var gap = Math.round(w_scale / 2);
+ if (name !== undefined && w_scale > px_per_char) {
+ ctx.fillStyle = "#555";
+ ctx.fillRect(x, y_center + 1, x_len, 9);
+ ctx.fillStyle = "#eee";
+ for (var c = 0, str_len = name.length; c < str_len; c++) {
+ if (feature_start + c >= tile_low && feature_start + c <= tile_high) {
+ var c_start = Math.floor( Math.max(0, (feature_start + c - tile_low) * w_scale) );
+ ctx.fillText(name[c], c_start + this.left_offset + gap, y_center + 9);
+ }
+ }
+ } else {
+ ctx.fillStyle = "#555";
+ ctx.fillRect(x, y_center + 4, x_len, 3);
+ }
+ },
draw_tile: function( resolution, tile_index, parent_element, w_scale ) {
var tile_low = tile_index * DENSITY * resolution,
tile_high = ( tile_index + 1 ) * DENSITY * resolution,
tile_span = DENSITY * resolution;
// console.log("drawing " + tile_index);
- var data, slots, required_height;
+ var slots, required_height;
/*for (var k in this.data_cache.obj_cache) {
var k_split = k.split("_"), k_low = k_split[0], k_high = k_split[1];
@@ -696,23 +714,22 @@
// var k = this.view.low + '_' + this.view.high;
var k = tile_low + '_' + tile_high;
- var data = this.data_cache.get(k);
+ var result = this.data_cache.get(k);
- if (data === undefined) {
+ if (result === undefined) {
this.data_queue[ [tile_low, tile_high] ] = true;
this.get_data(tile_low, tile_high);
return;
}
- if (data.dataset_type == "array_tree") {
+ if (result.dataset_type === "summary_tree") {
required_height = 30;
- // Blah
} else {
// Calculate new slots incrementally for this new chunk of data and update height if necessary
- var no_detail = (data.extra_info === "no_detail");
+ var no_detail = (result.extra_info === "no_detail");
var y_scale = ( no_detail ? this.vertical_nodetail_px : this.vertical_detail_px );
- required_height = this.incremental_slots( this.view.zoom_res, data.data, no_detail ) * y_scale + 15;
+ required_height = this.incremental_slots( this.view.zoom_res, result.data, no_detail ) * y_scale + 15;
parent_element.parent().css("height", Math.max(this.height_px, required_height) + "px");
slots = this.inc_slots[this.view.zoom_res];
}
@@ -732,20 +749,21 @@
new_canvas.get(0).width = width + left_offset;
new_canvas.get(0).height = required_height;
// console.log(( tile_low - this.view.low ) * w_scale, tile_index, w_scale);
- var ctx = new_canvas.get(0).getContext("2d");
+ var ctx = new_canvas.get(0).getContext("2d"),
+ px_per_char = ctx.measureText("A").width;
ctx.fillStyle = this.prefs.block_color;
ctx.font = this.default_font;
ctx.textAlign = "right";
- var min_color = 55,
- color_span = 255 - min_color,
- color_cutoff = color_span*2/3; // Where text switches from black to white
-
- if (data.dataset_type == "summary_tree") {
- var points = data.data;
- var max = data.max;
- var avg = data.avg;
- if (data.data.length > 2) {
+ if (result.dataset_type == "summary_tree") {
+ var color,
+ min_color = 55,
+ color_span = 255 - min_color,
+ color_cutoff = color_span*2/3, // Where text switches from black to white
+ points = result.data,
+ max = result.max,
+ avg = result.avg;
+ if (points.length > 2) {
var delta_x_px = Math.ceil((points[1][0] - points[0][0]) * w_scale);
} else {
var delta_x_px = 50; // Arbitrary, fix
@@ -756,7 +774,7 @@
var y = points[i][1];
if (!y) { continue; }
- var color = Math.floor( color_span - (y / max) * color_span );
+ color = Math.floor( color_span - (y / max) * color_span );
ctx.fillStyle = "rgb(" +color+ "," +color+ "," +color+ ")";
ctx.fillRect(x + left_offset, 0, delta_x_px, 20);
@@ -770,11 +788,12 @@
ctx.fillText(points[i][1], x + left_offset + (delta_x_px/2), 12);
}
}
+
parent_element.append( new_canvas );
return new_canvas;
}
- var data = data.data;
+ var data = result.data;
var j = 0;
for (var i = 0, len = data.length; i < len; i++) {
var feature = data[i],
@@ -785,80 +804,118 @@
if (feature_start <= tile_high && feature_end >= tile_low) {
var f_start = Math.floor( Math.max(0, (feature_start - tile_low) * w_scale) ),
- f_end = Math.ceil( Math.min(width, (feature_end - tile_low) * w_scale) ),
+ f_end = Math.ceil( Math.min(width, Math.max(0, (feature_end - tile_low) * w_scale)) ),
y_center = slots[feature_uid] * y_scale;
-
- // console.log(feature_uid, feature_start, feature_end, f_start, f_end, y_center);
- if (no_detail) {
- ctx.fillRect(f_start + left_offset, y_center + 5, f_end - f_start, 1);
- } else {
- // Showing labels, blocks, details
- var feature_strand = feature[4],
- feature_ts = feature[5],
- feature_te = feature[6],
- feature_blocks = feature[7];
- var thickness, y_start, thick_start = null, thick_end = null;
- if (feature_ts && feature_te) {
- thick_start = Math.floor( Math.max(0, (feature_ts - tile_low) * w_scale) );
- thick_end = Math.ceil( Math.min(width, (feature_te - tile_low) * w_scale) );
+ if (result.dataset_type === "bai") {
+ ctx.fillStyle = "#555";
+ if (feature[4] instanceof Array) {
+ var b1_start = Math.floor( Math.max(0, (feature[4][0] - tile_low) * w_scale) ),
+ b1_end = Math.ceil( Math.min(width, Math.max(0, (feature[4][1] - tile_low) * w_scale)) ),
+ b2_start = Math.floor( Math.max(0, (feature[5][0] - tile_low) * w_scale) ),
+ b2_end = Math.ceil( Math.min(width, Math.max(0, (feature[5][1] - tile_low) * w_scale)) );
+
+ if (feature[4][1] >= tile_low && feature[4][0] <= tile_high) {
+ this.rect_or_text(ctx, w_scale, px_per_char, tile_low, tile_high, feature[4][0], feature[4][2], b1_start + left_offset, b1_end - b1_start, y_center);
+ }
+ if (feature[5][1] >= tile_low && feature[5][0] <= tile_high) {
+ this.rect_or_text(ctx, w_scale, px_per_char, tile_low, tile_high, feature[5][0], feature[5][2], b2_start + left_offset, b2_end - b2_start, y_center);
+ }
+ if (b2_start > b1_end) {
+ ctx.fillStyle = "#999";
+ ctx.fillRect(b1_end + left_offset, y_center + 5, b2_start - b1_end, 1);
+ }
+ } else {
+ ctx.fillStyle = "#555";
+ this.rect_or_text(ctx, w_scale, px_per_char, tile_low, tile_high, feature_start, feature_name, f_start + left_offset, f_end - f_start, y_center);
}
- if (feature_name !== undefined && feature_start > tile_low) {
- ctx.fillStyle = label_color;
+ if (!no_detail && feature_start > tile_low) {
+ // Draw label
+ ctx.fillStyle = this.prefs.label_color;
if (tile_index === 0 && f_start - ctx.measureText(feature_name).width < 0) {
ctx.textAlign = "left";
- ctx.fillText(feature_name, f_end + 2 + left_offset, y_center + 8);
+ ctx.fillText(feature_uid, f_end + 2 + left_offset, y_center + 8);
} else {
ctx.textAlign = "right";
- ctx.fillText(feature_name, f_start - 2 + left_offset, y_center + 8);
+ ctx.fillText(feature_uid, f_start - 2 + left_offset, y_center + 8);
}
- ctx.fillStyle = block_color;
+ ctx.fillStyle = "#555";
}
- if (feature_blocks) {
- // Draw introns
- if (feature_strand) {
- if (feature_strand == "+") {
- ctx.fillStyle = RIGHT_STRAND;
- } else if (feature_strand == "-") {
- ctx.fillStyle = LEFT_STRAND;
+
+ } else if (result.dataset_type === "interval_index") {
+
+ // console.log(feature_uid, feature_start, feature_end, f_start, f_end, y_center);
+ if (no_detail) {
+ ctx.fillRect(f_start + left_offset, y_center + 5, f_end - f_start, 1);
+ } else {
+ // Showing labels, blocks, details
+ var feature_strand = feature[4],
+ feature_ts = feature[5],
+ feature_te = feature[6],
+ feature_blocks = feature[7];
+
+ var thickness, y_start, thick_start = null, thick_end = null;
+ if (feature_ts && feature_te) {
+ thick_start = Math.floor( Math.max(0, (feature_ts - tile_low) * w_scale) );
+ thick_end = Math.ceil( Math.min(width, Math.max(0, (feature_te - tile_low) * w_scale)) );
+ }
+ if (feature_name !== undefined && feature_start > tile_low) {
+ ctx.fillStyle = label_color;
+ if (tile_index === 0 && f_start - ctx.measureText(feature_name).width < 0) {
+ ctx.textAlign = "left";
+ ctx.fillText(feature_name, f_end + 2 + left_offset, y_center + 8);
+ } else {
+ ctx.textAlign = "right";
+ ctx.fillText(feature_name, f_start - 2 + left_offset, y_center + 8);
}
- ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, 10);
ctx.fillStyle = block_color;
}
+ if (feature_blocks) {
+ // Draw introns
+ if (feature_strand) {
+ if (feature_strand == "+") {
+ ctx.fillStyle = RIGHT_STRAND;
+ } else if (feature_strand == "-") {
+ ctx.fillStyle = LEFT_STRAND;
+ }
+ ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, 10);
+ ctx.fillStyle = block_color;
+ }
- for (var k = 0, k_len = feature_blocks.length; k < k_len; k++) {
- var block = feature_blocks[k],
- block_start = Math.floor( Math.max(0, (block[0] - tile_low) * w_scale) ),
- block_end = Math.ceil( Math.min(width, (block[1] - tile_low) * w_scale) );
- if (block_start > block_end) { continue; }
- // Draw the block
- thickness = 5;
- y_start = 3;
- ctx.fillRect(block_start + left_offset, y_center + y_start, block_end - block_start, thickness);
+ for (var k = 0, k_len = feature_blocks.length; k < k_len; k++) {
+ var block = feature_blocks[k],
+ block_start = Math.floor( Math.max(0, (block[0] - tile_low) * w_scale) ),
+ block_end = Math.ceil( Math.min(width, Math.max((block[1] - tile_low) * w_scale)) );
+ if (block_start > block_end) { continue; }
+ // Draw the block
+ thickness = 5;
+ y_start = 3;
+ ctx.fillRect(block_start + left_offset, y_center + y_start, block_end - block_start, thickness);
- // Draw thick regions: check if block intersects with thick region
- if (thick_start !== undefined && !(block_start > thick_end || block_end < thick_start) ) {
- thickness = 9;
- y_start = 1;
- var block_thick_start = Math.max(block_start, thick_start),
- block_thick_end = Math.min(block_end, thick_end);
- ctx.fillRect(block_thick_start + left_offset, y_center + y_start, block_thick_end - block_thick_start, thickness);
+ // Draw thick regions: check if block intersects with thick region
+ if (thick_start !== undefined && !(block_start > thick_end || block_end < thick_start) ) {
+ thickness = 9;
+ y_start = 1;
+ var block_thick_start = Math.max(block_start, thick_start),
+ block_thick_end = Math.min(block_end, thick_end);
+ ctx.fillRect(block_thick_start + left_offset, y_center + y_start, block_thick_end - block_thick_start, thickness);
+ }
}
- }
- } else {
- // If there are no blocks, we treat the feature as one big exon
- thickness = 9;
- y_start = 1;
- ctx.fillRect(f_start + left_offset, y_center + y_start, f_end - f_start, thickness);
- if ( feature.strand ) {
- if (feature.strand == "+") {
- ctx.fillStyle = RIGHT_STRAND_INV;
- } else if (feature.strand == "-") {
- ctx.fillStyle = LEFT_STRAND_INV;
+ } else {
+ // If there are no blocks, we treat the feature as one big exon
+ thickness = 9;
+ y_start = 1;
+ ctx.fillRect(f_start + left_offset, y_center + y_start, f_end - f_start, thickness);
+ if ( feature.strand ) {
+ if (feature.strand == "+") {
+ ctx.fillStyle = RIGHT_STRAND_INV;
+ } else if (feature.strand == "-") {
+ ctx.fillStyle = LEFT_STRAND_INV;
+ }
+ ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, 10);
+ ctx.fillStyle = prefs.block_color;
}
- ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, 10);
- ctx.fillStyle = prefs.block_color;
}
}
}
@@ -899,62 +956,9 @@
var ReadTrack = function ( name, dataset_id, prefs ) {
FeatureTrack.call( this, name, dataset_id, prefs );
this.track_type = "ReadTrack";
+ this.vertical_detail_px = 10;
+ this.vertical_nodetail_px = 5;
};
$.extend( ReadTrack.prototype, TiledTrack.prototype, FeatureTrack.prototype, {
- /*draw_tile: function( resolution, tile_index, parent_element, w_scale ) {
- if (!this.values) {
- return;
- }
- var tile_low = tile_index * DENSITY * resolution,
- tile_high = ( tile_index + 1 ) * DENSITY * resolution,
- tile_span = DENSITY * resolution;
- // console.log("drawing " + tile_index);
- // Once we zoom in enough, show name labels
- var data, slots, required_height;
- required_height = this.height_px;
- slots = this.zo_slots;
- data = this.values;
-
- // console.log(tile_low, tile_high, tile_length, w_scale);
- var width = Math.ceil( tile_span * w_scale ),
- new_canvas = $("<canvas class='tile'></canvas>");
-
- new_canvas.css({
- position: "absolute",
- top: 0,
- left: ( tile_low - this.view.low ) * w_scale - this.left_offset
- });
- new_canvas.get(0).width = width + this.left_offset;
- new_canvas.get(0).height = required_height;
- // console.log(( tile_low - this.view.low ) * w_scale, tile_index, w_scale);
- var ctx = new_canvas.get(0).getContext("2d");
- ctx.fillStyle = this.prefs.block_color;
- ctx.font = this.default_font;
- ctx.textAlign = "right";
- var px_per_char = ctx.measureText("A").width;
-
- var j = 0;
- for (var i = 0, len = data.length; i < len; i++) {
- var feature = data[i];
- if (feature.start <= tile_high && feature.end >= tile_low) {
- var f_start = Math.floor( Math.max(0, (feature.start - tile_low) * w_scale) ),
- f_end = Math.ceil( Math.min(width, (feature.end - tile_low) * w_scale) ),
- y_center = slots[feature.uid] * this.vertical_detail_px;
-
- var thickness, y_start, thick_start = null, thick_end = null;
- if (w_scale > px_per_char) {
- for (var c = 0, str_len = feature.name.length; c < str_len; c++) {
- var c_start = Math.floor( Math.max(0, (feature.start + c - tile_low) * w_scale) );
- ctx.fillText(feature.name[c], c_start + this.left_offset, y_center + 8);
- }
- } else {
- ctx.fillRect(f_start + this.left_offset, y_center + 4, f_end - f_start, 3);
- }
- }
- }
-
- parent_element.append( new_canvas );
- return new_canvas;
- }*/
});
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/a5ccb2ba4c74
changeset: 3715:a5ccb2ba4c74
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Wed Apr 28 16:06:58 2010 -0400
description:
Another rev on cuffcompare wrapper.
diffstat:
tools/ngs_rna/cuffcompare_wrapper.py | 16 +++++++++++-----
tools/ngs_rna/cuffcompare_wrapper.xml | 24 +++++++++++++++++++-----
2 files changed, 30 insertions(+), 10 deletions(-)
diffs (83 lines):
diff -r b70d9444f29f -r a5ccb2ba4c74 tools/ngs_rna/cuffcompare_wrapper.py
--- a/tools/ngs_rna/cuffcompare_wrapper.py Wed Apr 28 14:54:55 2010 -0400
+++ b/tools/ngs_rna/cuffcompare_wrapper.py Wed Apr 28 16:06:58 2010 -0400
@@ -13,6 +13,8 @@
parser.add_option( '-R', action="store_true", dest='ignore_nonoverlap', help='If -r was specified, this option causes cuffcompare to ignore reference transcripts that are not overlapped by any transcript in one of cuff1.gtf,...,cuffN.gtf. Useful for ignoring annotated transcripts that are not present in your RNA-Seq samples and thus adjusting the "sensitivity" calculation in the accuracy report written in the transcripts accuracy file' )
# Wrapper / Galaxy options.
+ parser.add_option( '-1', dest='input1')
+ parser.add_option( '-2', dest='input2')
parser.add_option( '-A', '--transcripts-accuracy-output', dest='transcripts_accuracy_output_file', help='' )
parser.add_option( '-B', '--transcripts-combined-output', dest='transcripts_combined_output_file', help='' )
parser.add_option( '-C', '--transcripts-tracking-output', dest='transcripts_tracking_output_file', help='' )
@@ -37,9 +39,10 @@
print cmd
# Add input files.
- if type(args) is list:
- args = " ".join(args)
- cmd += " " + args
+ cmd += " %s" % options.input1
+ two_inputs = ( options.input2 != None)
+ if two_inputs:
+ cmd += " %s" % options.input2
# Run command.
try:
@@ -76,8 +79,11 @@
try:
try:
shutil.copyfile( tmp_output_dir + "/cc_output", options.transcripts_accuracy_output_file )
- shutil.copyfile( tmp_output_dir + "/cc_output.combined.gtf", options.transcripts_combined_output_file )
- shutil.copyfile( tmp_output_dir + "/cc_output.tracking", options.transcripts_tracking_output_file )
+ if two_inputs:
+ shutil.copyfile( tmp_output_dir + "/cc_output.combined.gtf", options.transcripts_combined_output_file )
+ shutil.copyfile( tmp_output_dir + "/cc_output.tracking", options.transcripts_tracking_output_file )
+
+ # TODO: also copy *.tmap, *.refmap to outputs?
except Exception, e:
stop_err( 'Error in cuffcompare:\n' + str( e ) )
finally:
diff -r b70d9444f29f -r a5ccb2ba4c74 tools/ngs_rna/cuffcompare_wrapper.xml
--- a/tools/ngs_rna/cuffcompare_wrapper.xml Wed Apr 28 14:54:55 2010 -0400
+++ b/tools/ngs_rna/cuffcompare_wrapper.xml Wed Apr 28 16:06:58 2010 -0400
@@ -3,20 +3,34 @@
<command interpreter="python">
cuffcompare_wrapper.py
--transcripts-accuracy-output=$transcripts_accuracy
- --transcripts-combined-output=$transcripts_combined
- --transcripts-tracking-output=$transcripts_tracking
+ #if $second_gtf.use_second_gtf == "Yes":
+ --transcripts-combined-output=$transcripts_combined
+ --transcripts-tracking-output=$transcripts_tracking
+ #end if
#if $annotation.use_ref_annotation == "Yes":
-r $annotation.reference_annotation
#if $annotation.ignore_nonoverlapping_reference:
-R
#end if
#end if
- $input1
- $input2
+ -1 $input1
+ #if $second_gtf.use_second_gtf == "Yes":
+ -2 $second_gtf.input2
+ #end if
</command>
<inputs>
<param format="gtf" name="input1" type="data" label="GTF file produced by Cufflinks" help=""/>
- <param format="gtf" name="input2" type="data" label="GTF file produced by Cufflinks" help=""/>
+ <conditional name="second_gtf">
+ <param name="use_second_gtf" type="select" label="Use Another GTF file producted by Cufflinks?">
+ <option value="No">No</option>
+ <option value="Yes">Yes</option>
+ </param>
+ <when value="Yes">
+ <param format="gtf" name="input2" type="data" label="GTF file produced by Cufflinks" help=""/>
+ </when>
+ <when value="No">
+ </when>
+ </conditional>
<conditional name="annotation">
<param name="use_ref_annotation" type="select" label="Use Reference Annotation">
<option value="No">No</option>
1
0