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

galaxy-dist commit cce2225b8eea: Improvements to the GFF filtering tool.
by commits-noreply@bitbucket.org 20 Aug '10
by commits-noreply@bitbucket.org 20 Aug '10
20 Aug '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1280946953 14400
# Node ID cce2225b8eea41c6ff3779aa4b406b0b6018913c
# Parent 99a3437916457d1971820820733377664fab403f
Improvements to the GFF filtering tool.
--- a/tools/ngs_rna/gff_filtering.py
+++ b/tools/ngs_rna/gff_filtering.py
@@ -48,7 +48,7 @@ mapped_str = {
for key, value in mapped_str.items():
cond_text = cond_text.replace( key, value )
-# Add attribute name to condition text.
+# Condition text is 'attribute meets condition.'
cond_text = attribute_name + cond_text
# Attempt to determine if the condition includes executable stuff and, if so, exit
@@ -62,7 +62,7 @@ for operand in operands:
stop_err( "Illegal value '%s' in condition '%s'" % ( operand, cond_text ) )
# Set up assignment.
-assignment = "%s = attributes[ '%s' ]" % ( attribute_name, attribute_name )
+assignment = "%s = attributes.get('%s', None)" % ( attribute_name, attribute_name )
# Set up type casting based on attribute type.
type_cast = "%s = %s(%s)" % ( attribute_name, attribute_type, attribute_name)
@@ -103,16 +103,18 @@ for i, line in enumerate( file( in_fname
value = pair[1].strip(" \\"")
attributes[name] = value
%s
- %s
if %s:
- lines_kept += 1
- print >> out, line
- except:
+ %s
+ if %s:
+ lines_kept += 1
+ print >> out, line
+ except Exception, e:
skipped_lines += 1
if not invalid_line:
first_invalid_line = i + 1
invalid_line = line
-''' % ( assignment, type_cast, cond_text )
+''' % ( assignment, attribute_name, type_cast, cond_text )
+
valid_filter = True
try:
--- a/tools/ngs_rna/gff_filtering.xml
+++ b/tools/ngs_rna/gff_filtering.xml
@@ -14,7 +14,7 @@
</param><param name="attribute_type" type="select" label="Attribute type"><option value="float">Float</option>
- <option value="integer">Integer</option>
+ <option value="int">Integer</option><option value="str">String</option></param><param name="cond" size="40" type="text" value=">0" label="With following condition" help="Double equal signs, ==, must be used as shown above. To filter for an arbitrary string, use the Select tool.">
1
0
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1280930211 14400
# Node ID 77575a5f348d7d10c1df423c5faa71abaefd5f8a
# Parent 6e19bc97f2b8ba816ab20fc53a88f3504c992e00
lims:
- bug fix: now a admin cannot delete a transfer dataset when the transfer has started.
- added user warning when a dataset is deleted.
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -593,16 +593,25 @@ class RequestsAdmin( BaseController ):
elif operation == "delete":
id_list = util.listify( kwd['id'] )
+ not_deleted = []
for id in id_list:
sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(id) )
sample_id = sample_dataset.sample_id
- trans.sa_session.delete( sample_dataset )
- trans.sa_session.flush()
+ if sample_dataset.status == sample_dataset.sample.transfer_status.NOT_STARTED:
+ trans.sa_session.delete( sample_dataset )
+ trans.sa_session.flush()
+ else:
+ not_deleted.append(sample_dataset.name)
+ message = '%i dataset(s) have been successfully deleted. ' % (len(id_list) - len(not_deleted))
+ status = 'done'
+ if not_deleted:
+ status = 'warning'
+ message = message + '%s could not be deleted. Only datasets with transfer status "Not Started" can be deleted. ' % str(not_deleted)
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
sample_id=sample_id,
- status='done',
- message="%i dataset(s) have been removed." % len(id_list)) )
+ status=status,
+ message=message) )
elif operation == "rename":
id_list = util.listify( kwd['id'] )
1
0

galaxy-dist commit c164c2fb3a65: Update tool data table config file
by commits-noreply@bitbucket.org 20 Aug '10
by commits-noreply@bitbucket.org 20 Aug '10
20 Aug '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User James Taylor <james(a)jamestaylor.org>
# Date 1278616898 14400
# Node ID c164c2fb3a6516f8a086956a118145ae4aa1093f
# Parent cd941e492bc0bc6930b8ac32190459d9d536078b
Update tool data table config file
--- a/tool_data_table_conf.xml.sample
+++ b/tool_data_table_conf.xml.sample
@@ -1,6 +1,17 @@
<tables>
+ <!-- Locations of MAF files that have been indexed with bx-python --><table name="indexed_maf_files">
- <column_names>name, value, dbkey, species</column_names>
- <file name="tool-data/maf_index.loc" />
+ <columns>name, value, dbkey, species</columns>
+ <file path="tool-data/maf_index.loc" />
+ </table>
+ <!-- Locations of indexes in the BWA mapper format -->
+ <table name="bwa_indexes">
+ <columns>name, value</columns>
+ <file path="tool-data/bwa_index.loc" />
+ </table>
+ <!-- Locations of indexes in the Bowtie mapper format -->
+ <table name="bowtie_indexes">
+ <columns>name, value</columns>
+ <file path="tool-data/bowtie_indices.loc" /></table></tables>
1
0

galaxy-dist commit 2447b9a4dae3: Make "loc files" more flexible by adding "tool data tables". These are
by commits-noreply@bitbucket.org 20 Aug '10
by commits-noreply@bitbucket.org 20 Aug '10
20 Aug '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User James Taylor <james(a)jamestaylor.org>
# Date 1278614219 14400
# Node ID 2447b9a4dae30b17df089290a6471f3401fa5f78
# Parent e683c6995fb5d032660d1c28cff2e1bc578d1298
Make "loc files" more flexible by adding "tool data tables". These are
configured at the application level. Specific tabular data files are
specified in a application config file and bound to names, the tools
then refer to these names. Thus users can configure where location
files are located without modifying tool configs.
Also:
- Simpler column name configuration
- Columns can be referred to by name in addition to index in all
dynamic option filters
- A data table can merge multiple files
- Design can support other types of data files
--- a/lib/galaxy/tools/parameters/dynamic_options.py
+++ b/lib/galaxy/tools/parameters/dynamic_options.py
@@ -46,9 +46,9 @@ class StaticValueFilter( Filter ):
Filter.__init__( self, d_option, elem )
self.value = elem.get( "value", None )
assert self.value is not None, "Required 'value' attribute missing from filter"
- self.column = elem.get( "column", None )
- assert self.column is not None, "Required 'column' attribute missing from filter, when loading from file"
- self.column = int ( self.column )
+ column = elem.get( "column", None )
+ assert column is not None, "Required 'column' attribute missing from filter, when loading from file"
+ self.column = d_option.column_spec_to_index( column )
self.keep = string_as_bool( elem.get( "keep", 'True' ) )
def filter_options( self, options, trans, other_values ):
rval = []
@@ -81,11 +81,11 @@ class DataMetaFilter( Filter ):
d_option.has_dataset_dependencies = True
self.key = elem.get( "key", None )
assert self.key is not None, "Required 'key' attribute missing from filter"
- self.column = elem.get( "column", None )
- if self.column is None:
+ column = elem.get( "column", None )
+ if column is None:
assert self.dynamic_option.file_fields is None and self.dynamic_option.dataset_ref_name is None, "Required 'column' attribute missing from filter, when loading from file"
else:
- self.column = int ( self.column )
+ self.column = d_option.column_spec_to_index( column )
self.multiple = string_as_bool( elem.get( "multiple", "False" ) )
self.separator = elem.get( "separator", "," )
def get_dependency_name( self ):
@@ -141,9 +141,9 @@ class ParamValueFilter( Filter ):
Filter.__init__( self, d_option, elem )
self.ref_name = elem.get( "ref", None )
assert self.ref_name is not None, "Required 'ref' attribute missing from filter"
- self.column = elem.get( "column", None )
- assert self.column is not None, "Required 'column' attribute missing from filter"
- self.column = int ( self.column )
+ column = elem.get( "column", None )
+ assert column is not None, "Required 'column' attribute missing from filter"
+ self.column = d_option.column_spec_to_index( column )
self.keep = string_as_bool( elem.get( "keep", 'True' ) )
def get_dependency_name( self ):
return self.ref_name
@@ -168,9 +168,9 @@ class UniqueValueFilter( Filter ):
"""
def __init__( self, d_option, elem ):
Filter.__init__( self, d_option, elem )
- self.column = elem.get( "column", None )
- assert self.column is not None, "Required 'column' attribute missing from filter"
- self.column = int ( self.column )
+ column = elem.get( "column", None )
+ assert column is not None, "Required 'column' attribute missing from filter"
+ self.column = d_option.column_spec_to_index( column )
def get_dependency_name( self ):
return self.dynamic_option.dataset_ref_name
def filter_options( self, options, trans, other_values ):
@@ -196,9 +196,9 @@ class MultipleSplitterFilter( Filter ):
def __init__( self, d_option, elem ):
Filter.__init__( self, d_option, elem )
self.separator = elem.get( "separator", "," )
- self.columns = elem.get( "column", None )
- assert self.columns is not None, "Required 'columns' attribute missing from filter"
- self.columns = [ int ( column ) for column in self.columns.split( "," ) ]
+ columns = elem.get( "column", None )
+ assert columns is not None, "Required 'columns' attribute missing from filter"
+ self.columns = [ d_option.column_spec_to_index( column ) for column in columns.split( "," ) ]
def filter_options( self, options, trans, other_values ):
rval = []
for fields in options:
@@ -302,9 +302,9 @@ class SortByColumnFilter( Filter ):
"""
def __init__( self, d_option, elem ):
Filter.__init__( self, d_option, elem )
- self.column = elem.get( "column", None )
- assert self.column is not None, "Required 'column' attribute missing from filter"
- self.column = int( self.column )
+ column = elem.get( "column", None )
+ assert column is not None, "Required 'column' attribute missing from filter"
+ self.column = d_option.column_spec_to_index( column )
def filter_options( self, options, trans, other_values ):
rval = []
for i, fields in enumerate( options ):
@@ -354,20 +354,25 @@ class DynamicOptions( object ):
data_file = elem.get( 'from_file', None )
dataset_file = elem.get( 'from_dataset', None )
from_parameter = elem.get( 'from_parameter', None )
- if data_file is not None or dataset_file is not None or from_parameter is not None:
- for column_elem in elem.findall( 'column' ):
- name = column_elem.get( 'name', None )
- assert name is not None, "Required 'name' attribute missing from column def"
- index = column_elem.get( 'index', None )
- assert index is not None, "Required 'index' attribute missing from column def"
- index = int( index )
- self.columns[name] = index
- if index > self.largest_index:
- self.largest_index = index
- assert 'value' in self.columns, "Required 'value' column missing from column def"
- if 'name' not in self.columns:
- self.columns['name'] = self.columns['value']
+ tool_data_table_name = elem.get( 'from_data_table', None )
+
+ # Options are defined from a data table loaded by the app
+ self.tool_data_table = None
+ if tool_data_table_name:
+ app = tool_param.tool.app
+ assert tool_data_table_name in app.tool_data_tables, \
+ "Data table named '%s' is required by tool but not configured" % tool_data_table_name
+ self.tool_data_table = app.tool_data_tables[ tool_data_table_name ]
+ # Column definitions are optional, but if provided override those from the table
+ if elem.find( "column" ) is not None:
+ self.parse_column_definitions( elem )
+ else:
+ self.columns = self.tool_data_table.columns
+ # Options are defined by parsing tabular text data from an data file
+ # on disk, a dataset, or the value of another parameter
+ elif data_file is not None or dataset_file is not None or from_parameter is not None:
+ self.parse_column_definitions( elem )
if data_file is not None:
data_file = data_file.strip()
if not os.path.isabs( data_file ):
@@ -388,6 +393,20 @@ class DynamicOptions( object ):
# Load Validators
for validator in elem.findall( 'validator' ):
self.validators.append( validation.Validator.from_element( self.tool_param, validator ) )
+
+ def parse_column_definitions( self, elem ):
+ for column_elem in elem.findall( 'column' ):
+ name = column_elem.get( 'name', None )
+ assert name is not None, "Required 'name' attribute missing from column def"
+ index = column_elem.get( 'index', None )
+ assert index is not None, "Required 'index' attribute missing from column def"
+ index = int( index )
+ self.columns[name] = index
+ if index > self.largest_index:
+ self.largest_index = index
+ assert 'value' in self.columns, "Required 'value' column missing from column def"
+ if 'name' not in self.columns:
+ self.columns['name'] = self.columns['value']
def parse_file_fields( self, reader ):
rval = []
@@ -421,6 +440,8 @@ class DynamicOptions( object ):
assert dataset is not None, "Required dataset '%s' missing from input" % self.dataset_ref_name
if not dataset: return [] #no valid dataset in history
options = self.parse_file_fields( open( dataset.file_name ) )
+ elif self.tool_data_table:
+ options = self.tool_data_table.get_fields()
else:
options = list( self.file_fields )
for filter in self.filters:
@@ -429,7 +450,7 @@ class DynamicOptions( object ):
def get_options( self, trans, other_values ):
rval = []
- if self.file_fields is not None or self.dataset_ref_name is not None:
+ if self.file_fields is not None or self.tool_data_table is not None or self.dataset_ref_name is not None:
options = self.get_fields( trans, other_values )
for fields in options:
rval.append( ( fields[self.columns['name']], fields[self.columns['value']], False ) )
@@ -437,3 +458,15 @@ class DynamicOptions( object ):
for filter in self.filters:
rval = filter.filter_options( rval, trans, other_values )
return rval
+
+ def column_spec_to_index( self, column_spec ):
+ """
+ Convert a column specification (as read from the config file), to an
+ index. A column specification can just be a number, a column name, or
+ a column alias.
+ """
+ # Name?
+ if column_spec in self.columns:
+ return self.columns[column_spec]
+ # Int?
+ return int( column_spec )
--- a/tools/sr_mapping/bowtie_wrapper.xml
+++ b/tools/sr_mapping/bowtie_wrapper.xml
@@ -192,10 +192,13 @@
</param><when value="indexed"><param name="index" type="select" label="Select a reference genome" help="if your genome of interest is not listed - contact Galaxy team">
+ <options from_data_table="bowtie_indexes"/>
+ <!--
<options from_file="bowtie_indices.loc"><column name="value" index="1" /><column name="name" index="0" /></options>
+ --></param></when><when value="history">
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -231,13 +231,17 @@ def rst_to_html( s ):
log.warn( str )
return docutils.core.publish_string( s, writer=HTMLFragWriter(), settings_overrides=dict( warning_stream=FakeStream() ) )
-def xml_text(root, name):
+def xml_text(root, name=None):
"""Returns the text inside an element"""
- # Try attribute first
- val = root.get(name)
- if val: return val
- # Then try as element
- elem = root.find(name)
+ if name is not None:
+ # Try attribute first
+ val = root.get(name)
+ if val:
+ return val
+ # Then try as element
+ elem = root.find(name)
+ else:
+ elem = root
if elem is not None and elem.text:
text = ''.join(elem.text.splitlines())
return text.strip()
--- a/setup.sh
+++ b/setup.sh
@@ -7,6 +7,7 @@ SAMPLES="
datatypes_conf.xml.sample
reports_wsgi.ini.sample
tool_conf.xml.sample
+tool_data_table_conf.xml.sample
universe_wsgi.ini.sample
tool-data/alignseq.loc.sample
tool-data/annotation_profiler_options.xml.sample
--- a/tools/sr_mapping/bwa_wrapper.xml
+++ b/tools/sr_mapping/bwa_wrapper.xml
@@ -34,10 +34,13 @@
</param><when value="indexed"><param name="indices" type="select" label="Select a reference genome">
+ <options from_data_table="bwa_indexes"/>
+ <!--
<options from_file="bwa_index.loc"><column name="value" index="1" /><column name="name" index="0" /></options>
+ --></param></when><when value="history">
--- a/lib/galaxy/app.py
+++ b/lib/galaxy/app.py
@@ -2,6 +2,7 @@ import sys, os, atexit
from galaxy import config, jobs, util, tools, web
import galaxy.tools.search
+import galaxy.tools.data
from galaxy.web import security
import galaxy.model
import galaxy.datatypes.registry
@@ -36,6 +37,8 @@ class UniverseApplication( object ):
self.security = security.SecurityHelper( id_secret=self.config.id_secret )
# Tag handler
self.tag_handler = GalaxyTagHandler()
+ # Tool data tables
+ self.tool_data_tables = galaxy.tools.data.ToolDataTableManager( self.config.tool_data_table_config_path )
# Initialize the tools
self.toolbox = tools.ToolBox( self.config.tool_config, self.config.tool_path, self )
# Search support for tools
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -48,6 +48,7 @@ class Configuration( object ):
self.tool_data_path = resolve_path( kwargs.get( "tool_data_path", "tool-data" ), os.getcwd() )
self.test_conf = resolve_path( kwargs.get( "test_conf", "" ), self.root )
self.tool_config = resolve_path( kwargs.get( 'tool_config_file', 'tool_conf.xml' ), self.root )
+ self.tool_data_table_config_path = resolve_path( kwargs.get( 'tool_data_table_config_path', 'tool_data_table_conf.xml' ), self.root )
self.tool_secret = kwargs.get( "tool_secret", "" )
self.id_secret = kwargs.get( "id_secret", "USING THE DEFAULT IS NOT SECURE!" )
self.set_metadata_externally = string_as_bool( kwargs.get( "set_metadata_externally", "False" ) )
--- a/tools/maf/interval2maf.xml
+++ b/tools/maf/interval2maf.xml
@@ -32,22 +32,24 @@
</when><when value="cached"><param name="mafType" type="select" label="Choose alignments">
- <options from_file="maf_index.loc">
+ <options from_data_table="indexed_maf_files">
+ <!--
<column name="name" index="0"/><column name="value" index="1"/><column name="dbkey" index="2"/><column name="species" index="3"/>
- <filter type="data_meta" ref="input1" key="dbkey" column="2" multiple="True" separator=","/>
+ -->
+ <filter type="data_meta" ref="input1" key="dbkey" column="dbkey" multiple="True" separator=","/><validator type="no_options" message="No alignments are available for the build associated with the selected interval file"/></options></param><param name="species" type="select" display="checkboxes" multiple="true" label="Choose species" help="Select species to be included in the final alignment">
- <options from_file="maf_index.loc">
+ <options from_data_table="indexed_maf_files"><column name="uid" index="1"/><column name="value" index="3"/><column name="name" index="3"/>
- <filter type="param_value" ref="mafType" name="uid" column="1"/>
- <filter type="multiple_splitter" column="3" separator=","/>
+ <filter type="param_value" ref="mafType" column="uid"/>
+ <filter type="multiple_splitter" column="name" separator=","/></options></param></when>
1
0

galaxy-dist commit 231738037345: Automated merge with https://bitbucket.org/galaxy/galaxy-central/
by commits-noreply@bitbucket.org 20 Aug '10
by commits-noreply@bitbucket.org 20 Aug '10
20 Aug '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User James Taylor <james(a)jamestaylor.org>
# Date 1280944899 14400
# Node ID 23173803734597fe8cce4215d18d8d52c061c303
# Parent 77575a5f348d7d10c1df423c5faa71abaefd5f8a
# Parent c164c2fb3a6516f8a086956a118145ae4aa1093f
Automated merge with https://bitbucket.org/galaxy/galaxy-central/
--- a/lib/galaxy/tools/parameters/dynamic_options.py
+++ b/lib/galaxy/tools/parameters/dynamic_options.py
@@ -46,9 +46,9 @@ class StaticValueFilter( Filter ):
Filter.__init__( self, d_option, elem )
self.value = elem.get( "value", None )
assert self.value is not None, "Required 'value' attribute missing from filter"
- self.column = elem.get( "column", None )
- assert self.column is not None, "Required 'column' attribute missing from filter, when loading from file"
- self.column = int ( self.column )
+ column = elem.get( "column", None )
+ assert column is not None, "Required 'column' attribute missing from filter, when loading from file"
+ self.column = d_option.column_spec_to_index( column )
self.keep = string_as_bool( elem.get( "keep", 'True' ) )
def filter_options( self, options, trans, other_values ):
rval = []
@@ -81,11 +81,11 @@ class DataMetaFilter( Filter ):
d_option.has_dataset_dependencies = True
self.key = elem.get( "key", None )
assert self.key is not None, "Required 'key' attribute missing from filter"
- self.column = elem.get( "column", None )
- if self.column is None:
+ column = elem.get( "column", None )
+ if column is None:
assert self.dynamic_option.file_fields is None and self.dynamic_option.dataset_ref_name is None, "Required 'column' attribute missing from filter, when loading from file"
else:
- self.column = int ( self.column )
+ self.column = d_option.column_spec_to_index( column )
self.multiple = string_as_bool( elem.get( "multiple", "False" ) )
self.separator = elem.get( "separator", "," )
def get_dependency_name( self ):
@@ -142,9 +142,9 @@ class ParamValueFilter( Filter ):
Filter.__init__( self, d_option, elem )
self.ref_name = elem.get( "ref", None )
assert self.ref_name is not None, "Required 'ref' attribute missing from filter"
- self.column = elem.get( "column", None )
- assert self.column is not None, "Required 'column' attribute missing from filter"
- self.column = int ( self.column )
+ column = elem.get( "column", None )
+ assert column is not None, "Required 'column' attribute missing from filter"
+ self.column = d_option.column_spec_to_index( column )
self.keep = string_as_bool( elem.get( "keep", 'True' ) )
self.ref_attribute = elem.get( "ref_attribute", None )
if self.ref_attribute:
@@ -177,9 +177,9 @@ class UniqueValueFilter( Filter ):
"""
def __init__( self, d_option, elem ):
Filter.__init__( self, d_option, elem )
- self.column = elem.get( "column", None )
- assert self.column is not None, "Required 'column' attribute missing from filter"
- self.column = int ( self.column )
+ column = elem.get( "column", None )
+ assert column is not None, "Required 'column' attribute missing from filter"
+ self.column = d_option.column_spec_to_index( column )
def get_dependency_name( self ):
return self.dynamic_option.dataset_ref_name
def filter_options( self, options, trans, other_values ):
@@ -205,9 +205,9 @@ class MultipleSplitterFilter( Filter ):
def __init__( self, d_option, elem ):
Filter.__init__( self, d_option, elem )
self.separator = elem.get( "separator", "," )
- self.columns = elem.get( "column", None )
- assert self.columns is not None, "Required 'columns' attribute missing from filter"
- self.columns = [ int ( column ) for column in self.columns.split( "," ) ]
+ columns = elem.get( "column", None )
+ assert columns is not None, "Required 'columns' attribute missing from filter"
+ self.columns = [ d_option.column_spec_to_index( column ) for column in columns.split( "," ) ]
def filter_options( self, options, trans, other_values ):
rval = []
for fields in options:
@@ -345,9 +345,9 @@ class SortByColumnFilter( Filter ):
"""
def __init__( self, d_option, elem ):
Filter.__init__( self, d_option, elem )
- self.column = elem.get( "column", None )
- assert self.column is not None, "Required 'column' attribute missing from filter"
- self.column = int( self.column )
+ column = elem.get( "column", None )
+ assert column is not None, "Required 'column' attribute missing from filter"
+ self.column = d_option.column_spec_to_index( column )
def filter_options( self, options, trans, other_values ):
rval = []
for i, fields in enumerate( options ):
@@ -398,20 +398,25 @@ class DynamicOptions( object ):
data_file = elem.get( 'from_file', None )
dataset_file = elem.get( 'from_dataset', None )
from_parameter = elem.get( 'from_parameter', None )
- if data_file is not None or dataset_file is not None or from_parameter is not None:
- for column_elem in elem.findall( 'column' ):
- name = column_elem.get( 'name', None )
- assert name is not None, "Required 'name' attribute missing from column def"
- index = column_elem.get( 'index', None )
- assert index is not None, "Required 'index' attribute missing from column def"
- index = int( index )
- self.columns[name] = index
- if index > self.largest_index:
- self.largest_index = index
- assert 'value' in self.columns, "Required 'value' column missing from column def"
- if 'name' not in self.columns:
- self.columns['name'] = self.columns['value']
+ tool_data_table_name = elem.get( 'from_data_table', None )
+
+ # Options are defined from a data table loaded by the app
+ self.tool_data_table = None
+ if tool_data_table_name:
+ app = tool_param.tool.app
+ assert tool_data_table_name in app.tool_data_tables, \
+ "Data table named '%s' is required by tool but not configured" % tool_data_table_name
+ self.tool_data_table = app.tool_data_tables[ tool_data_table_name ]
+ # Column definitions are optional, but if provided override those from the table
+ if elem.find( "column" ) is not None:
+ self.parse_column_definitions( elem )
+ else:
+ self.columns = self.tool_data_table.columns
+ # Options are defined by parsing tabular text data from an data file
+ # on disk, a dataset, or the value of another parameter
+ elif data_file is not None or dataset_file is not None or from_parameter is not None:
+ self.parse_column_definitions( elem )
if data_file is not None:
data_file = data_file.strip()
if not os.path.isabs( data_file ):
@@ -432,6 +437,20 @@ class DynamicOptions( object ):
# Load Validators
for validator in elem.findall( 'validator' ):
self.validators.append( validation.Validator.from_element( self.tool_param, validator ) )
+
+ def parse_column_definitions( self, elem ):
+ for column_elem in elem.findall( 'column' ):
+ name = column_elem.get( 'name', None )
+ assert name is not None, "Required 'name' attribute missing from column def"
+ index = column_elem.get( 'index', None )
+ assert index is not None, "Required 'index' attribute missing from column def"
+ index = int( index )
+ self.columns[name] = index
+ if index > self.largest_index:
+ self.largest_index = index
+ assert 'value' in self.columns, "Required 'value' column missing from column def"
+ if 'name' not in self.columns:
+ self.columns['name'] = self.columns['value']
def parse_file_fields( self, reader ):
rval = []
@@ -465,6 +484,8 @@ class DynamicOptions( object ):
assert dataset is not None, "Required dataset '%s' missing from input" % self.dataset_ref_name
if not dataset: return [] #no valid dataset in history
options = self.parse_file_fields( open( dataset.file_name ) )
+ elif self.tool_data_table:
+ options = self.tool_data_table.get_fields()
else:
options = list( self.file_fields )
for filter in self.filters:
@@ -473,7 +494,7 @@ class DynamicOptions( object ):
def get_options( self, trans, other_values ):
rval = []
- if self.file_fields is not None or self.dataset_ref_name is not None:
+ if self.file_fields is not None or self.tool_data_table is not None or self.dataset_ref_name is not None:
options = self.get_fields( trans, other_values )
for fields in options:
rval.append( ( fields[self.columns['name']], fields[self.columns['value']], False ) )
@@ -481,3 +502,15 @@ class DynamicOptions( object ):
for filter in self.filters:
rval = filter.filter_options( rval, trans, other_values )
return rval
+
+ def column_spec_to_index( self, column_spec ):
+ """
+ Convert a column specification (as read from the config file), to an
+ index. A column specification can just be a number, a column name, or
+ a column alias.
+ """
+ # Name?
+ if column_spec in self.columns:
+ return self.columns[column_spec]
+ # Int?
+ return int( column_spec )
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -48,6 +48,7 @@ class Configuration( object ):
self.tool_data_path = resolve_path( kwargs.get( "tool_data_path", "tool-data" ), os.getcwd() )
self.test_conf = resolve_path( kwargs.get( "test_conf", "" ), self.root )
self.tool_config = resolve_path( kwargs.get( 'tool_config_file', 'tool_conf.xml' ), self.root )
+ self.tool_data_table_config_path = resolve_path( kwargs.get( 'tool_data_table_config_path', 'tool_data_table_conf.xml' ), self.root )
self.tool_secret = kwargs.get( "tool_secret", "" )
self.id_secret = kwargs.get( "id_secret", "USING THE DEFAULT IS NOT SECURE!" )
self.set_metadata_externally = string_as_bool( kwargs.get( "set_metadata_externally", "False" ) )
1
0

galaxy-dist commit cd941e492bc0: Two missing files from previous commit (tool data tables)
by commits-noreply@bitbucket.org 20 Aug '10
by commits-noreply@bitbucket.org 20 Aug '10
20 Aug '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User James Taylor <james(a)jamestaylor.org>
# Date 1278615679 14400
# Node ID cd941e492bc0bc6930b8ac32190459d9d536078b
# Parent 2447b9a4dae30b17df089290a6471f3401fa5f78
Two missing files from previous commit (tool data tables)
--- /dev/null
+++ b/tool_data_table_conf.xml.sample
@@ -0,0 +1,6 @@
+<tables>
+ <table name="indexed_maf_files">
+ <column_names>name, value, dbkey, species</column_names>
+ <file name="tool-data/maf_index.loc" />
+ </table>
+</tables>
--- /dev/null
+++ b/lib/galaxy/tools/data/__init__.py
@@ -0,0 +1,131 @@
+"""
+Manage tool data tables, which store (at the application level) data that is
+used by tools, for example in the generation of dynamic options. Tables are
+loaded and stored by names which tools use to refer to them. This allows
+users to configure data tables for a local Galaxy instance without needing
+to modify the tool configurations.
+"""
+
+import logging, sys, os.path
+from galaxy import util
+
+log = logging.getLogger( __name__ )
+
+class ToolDataTableManager( object ):
+ """
+ Manages a collection of tool data tables
+ """
+
+ def __init__( self, config_filename=None ):
+ self.data_tables = {}
+ if config_filename:
+ self.add_from_config_file( config_filename )
+
+ def __getitem__( self, key ):
+ return self.data_tables.__getitem__( key )
+
+ def __contains__( self, key ):
+ return self.data_tables.__contains__( key )
+
+ def add_from_config_file( self, config_filename ):
+ tree = util.parse_xml( config_filename )
+ root = tree.getroot()
+ for table_elem in root.findall( 'table' ):
+ type = table_elem.get( 'type', 'tabular' )
+ assert type in tool_data_table_types, "Unknown data table type '%s'" % type
+ table = tool_data_table_types[ type ]( table_elem )
+ self.data_tables[ table.name ] = table
+ log.debug( "Loaded tool data table '%s", table.name )
+ print >> sys.stderr, repr( self.data_tables )
+
+class ToolDataTable( object ):
+ def __init__( self, config_element ):
+ self.name = config_element.get( 'name' )
+
+class TabularToolDataTable( ToolDataTable ):
+ """
+ Data stored in a tabular / separated value format on disk, allows multiple
+ files to be merged but all must have the same column definitions.
+
+ <table type="tabular" name="test">
+ <column name='...' index = '...' />
+ <file path="..." />
+ <file path="..." />
+ </table>
+ """
+
+ type_key = 'tabular'
+
+ def __init__( self, config_element ):
+ super( TabularToolDataTable, self ).__init__( config_element )
+ self.configure_and_load( config_element )
+
+ def configure_and_load( self, config_element ):
+ """
+ Configure and load table from an XML element.
+ """
+ self.separator = config_element.get( 'separator', '\t' )
+ self.comment_char = config_element.get( 'comment_char', '#' )
+ # Configure columns
+ self.parse_column_spec( config_element )
+ # Read every file
+ all_rows = []
+ for file_element in config_element.findall( 'file' ):
+ filename = file_element.get( 'path' )
+ assert os.path.exists( filename ), \
+ "Cannot find index file '%s' for tool data table '%s'" % ( filename, self.name )
+ all_rows.extend( self.parse_file_fields( open( filename ) ) )
+ self.data = all_rows
+
+ def get_fields( self ):
+ return self.data
+
+ def parse_column_spec( self, config_element ):
+ """
+ Parse column definitions, which can either be a set of 'column' elements
+ with a name and index (as in dynamic options config), or a shorthand
+ comma separated list of names in order as the text of a 'column_names'
+ element.
+
+ A column named 'value' is required.
+ """
+ self.columns = {}
+ if config_element.find( 'columns' ) is not None:
+ column_names = util.xml_text( config_element.find( 'columns' ) )
+ column_names = [ n.strip() for n in column_names.split( ',' ) ]
+ for index, name in enumerate( column_names ):
+ self.columns[ name ] = index
+ self.largest_index = index
+ else:
+ for column_elem in config_element.findall( 'column' ):
+ name = column_elem.get( 'name', None )
+ assert name is not None, "Required 'name' attribute missing from column def"
+ index = column_elem.get( 'index', None )
+ assert index is not None, "Required 'index' attribute missing from column def"
+ index = int( index )
+ self.columns[name] = index
+ if index > self.largest_index:
+ self.largest_index = index
+ assert 'value' in self.columns, "Required 'value' column missing from column def"
+ if 'name' not in self.columns:
+ self.columns['name'] = self.columns['value']
+
+ def parse_file_fields( self, reader ):
+ """
+ Parse separated lines from file and return a list of tuples.
+
+ TODO: Allow named access to fields using the column names.
+ """
+ rval = []
+ for line in reader:
+ if line.lstrip().startswith( self.comment_char ):
+ continue
+ line = line.rstrip( "\n\r" )
+ if line:
+ fields = line.split( self.separator )
+ if self.largest_index < len( fields ):
+ rval.append( fields )
+ return rval
+
+# Registry of tool data types by type_key
+tool_data_table_types = dict( [ ( cls.type_key, cls ) for cls in [ TabularToolDataTable ] ] )
1
0

galaxy-dist commit 6e19bc97f2b8: - Fix dbkey migrate script for downgrade and rare case when config is empty
by commits-noreply@bitbucket.org 20 Aug '10
by commits-noreply@bitbucket.org 20 Aug '10
20 Aug '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Kanwei Li <kanwei(a)gmail.com>
# Date 1280868063 14400
# Node ID 6e19bc97f2b8ba816ab20fc53a88f3504c992e00
# Parent 0366c4a4765631188f1515fbd77fddbbf1532d4f
- Fix dbkey migrate script for downgrade and rare case when config is empty
- Fix grid sorting in "Visualize in Trackster" modal box
- Refactor out sort_order in grids, fixing a bug with sort arrow not showing
--- a/templates/tracks/add_to_viz.mako
+++ b/templates/tracks/add_to_viz.mako
@@ -6,8 +6,3 @@
${render_grid_header( grid, False )}
${render_grid_table( grid, show_item_checkboxes=True )}
-## Initialize the grid.
-<script type="text/javascript">
- init_grid_elements();
- init_grid_controls();
-</script>
--- a/lib/galaxy/web/framework/helpers/grids.py
+++ b/lib/galaxy/web/framework/helpers/grids.py
@@ -136,7 +136,6 @@ class Grid( object ):
extra_url_args[ "f-" + column.key ] = column_filter.encode("utf-8")
# Process sort arguments.
sort_key = None
- sort_order = None
if 'sort' in kwargs:
sort_key = kwargs['sort']
elif base_sort_key:
@@ -144,11 +143,9 @@ class Grid( object ):
encoded_sort_key = sort_key
if sort_key:
if sort_key.startswith( "-" ):
- sort_order = 'desc'
# Can't use lower() on timestamp or integer objects, so func.lower() is not used here...
query = query.order_by( self.model_class.table.c.get( sort_key[1:] ).desc() )
else:
- sort_order = 'asc'
# See reason for not using lower() to do case-insensitive search.
query = query.order_by( self.model_class.table.c.get( sort_key ).asc() )
extra_url_args['sort'] = encoded_sort_key
@@ -226,7 +223,6 @@ class Grid( object ):
cur_filter_dict=cur_filter_dict,
sort_key=sort_key,
encoded_sort_key=encoded_sort_key,
- sort_order=sort_order,
current_item=current_item,
ids = kwargs.get( 'id', [] ),
url = url,
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -739,8 +739,8 @@
href = ""
extra = ""
if column.sortable:
- if sort_key == column.key:
- if sort_order == "asc":
+ if sort_key.endswith(column.key):
+ if not sort_key.startswith("-"):
href = url( sort=( "-" + column.key ) )
extra = "↓"
else:
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -205,7 +205,7 @@ class UsesVisualization( SharableItemSec
# Set dbkey.
try:
- dbkey = latest_revision.config['dbkey']
+ dbkey = latest_revision.dbkey
except KeyError:
dbkey = None
--- a/lib/galaxy/model/migrate/versions/0054_visualization_dbkey.py
+++ b/lib/galaxy/model/migrate/versions/0054_visualization_dbkey.py
@@ -14,13 +14,13 @@ log = logging.getLogger( __name__ )
metadata = MetaData( migrate_engine )
db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) )
-Visualization_table = Table( "visualization", metadata, autoload=True )
-Visualization_revision_table = Table( "visualization_revision", metadata, autoload=True )
-
def upgrade():
print __doc__
metadata.reflect()
+
+ Visualization_table = Table( "visualization", metadata, autoload=True )
+ Visualization_revision_table = Table( "visualization_revision", metadata, autoload=True )
# Create dbkey columns.
x = Column( "dbkey", TEXT, index=True )
@@ -47,12 +47,16 @@ def upgrade():
for viz in all_viz:
viz_id = viz['viz_id']
viz_rev_id = viz['viz_rev_id']
- dbkey = from_json_string(viz[Visualization_revision_table.c.config]).get('dbkey', "").replace("'", "\\'")
- db_session.execute("UPDATE visualization_revision SET dbkey='%s' WHERE id=%s" % (dbkey, viz_rev_id))
- db_session.execute("UPDATE visualization SET dbkey='%s' WHERE id=%s" % (dbkey, viz_id))
+ if viz[Visualization_revision_table.c.config]:
+ dbkey = from_json_string(viz[Visualization_revision_table.c.config]).get('dbkey', "").replace("'", "\\'")
+ db_session.execute("UPDATE visualization_revision SET dbkey='%s' WHERE id=%s" % (dbkey, viz_rev_id))
+ db_session.execute("UPDATE visualization SET dbkey='%s' WHERE id=%s" % (dbkey, viz_id))
def downgrade():
metadata.reflect()
+
+ Visualization_table = Table( "visualization", metadata, autoload=True )
+ Visualization_revision_table = Table( "visualization_revision", metadata, autoload=True )
Visualization_table.c.dbkey.drop()
Visualization_revision_table.c.dbkey.drop()
1
0

galaxy-dist commit 07e7db914c6d: Modified Group tool by adding Mode function to the list of aggregate operations.
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User gua110
# Date 1280420469 14400
# Node ID 07e7db914c6d2090ef7579a5e2c0583af50c39f3
# Parent 79baa9583d9390bb6341d3484aa9f55b8e3808f8
Modified Group tool by adding Mode function to the list of aggregate operations.
--- a/tools/stats/grouping.py
+++ b/tools/stats/grouping.py
@@ -23,6 +23,12 @@ def main():
cols.append(var.split()[1])
rounds.append(var.split()[2])
+ if 'Mode' in ops:
+ try:
+ r.library('prettyR')
+ except:
+ stop_err('R package prettyR could not be loaded. Please make sure it is installed.')
+
"""
At this point, ops, cols and rounds will look something like this:
ops: ['mean', 'min', 'c']
@@ -46,9 +52,10 @@ def main():
except:
stop_err( "Group column not specified." )
+ str_ops = ['c', 'length', 'unique', 'random', 'cuniq', 'Mode'] #ops that can handle string/non-numeric inputs
for k,col in enumerate(cols):
col = int(col)-1
- if ops[k] not in ['c', 'length', 'unique', 'random', 'cuniq']:
+ if ops[k] not in str_ops:
# We'll get here only if the user didn't choose 'Concatenate' or 'Count' or 'Count Distinct' or 'pick randmly', which are the
# only aggregation functions that can be used on columns containing strings.
try:
@@ -109,7 +116,7 @@ def main():
valid = True
# Before appending the current value, make sure it is numeric if the
# operation for the column requires it.
- if ops[i] not in ['c','length', 'unique','random','cuniq']:
+ if ops[i] not in str_ops:
try:
float( fields[col].strip())
except:
@@ -128,13 +135,14 @@ def main():
due to the sort on group_col we've applied to the data above.
"""
out_str = prev_item
-
+ multiple_modes = False
+ mode_index = None
for i, op in enumerate( ops ):
if op == 'cuniq':
rfunc = "r.c"
else:
rfunc = "r." + op
- if op not in ['c','length','unique','random','cuniq']:
+ if op not in str_ops:
for j, elem in enumerate( prev_vals[i] ):
prev_vals[i][j] = float( elem )
rout = eval( rfunc )( prev_vals[i] )
@@ -148,7 +156,10 @@ def main():
else:
rand_index = random.randint(0,len(prev_vals[i])-1)
rout = prev_vals[i][rand_index]
-
+
+ if op == 'Mode' and rout == '>1 mode':
+ multiple_modes = True
+ mode_index = i
if op == 'unique':
rfunc = "r.length"
rout = eval( rfunc )( rout )
@@ -165,8 +176,13 @@ def main():
out_str += "\t" + str(rout)
else:
out_str += "\t" + str(rout)
-
- print >>fout, out_str
+ if multiple_modes and mode_index != None:
+ out_str_list = out_str.split('\t')
+ for val in prev_vals[mode_index]:
+ out_str = '\t'.join(out_str_list[:mode_index+1]) + '\t' + str(val) + '\t' + '\t'.join(out_str_list[mode_index+2:])
+ print >>fout, out_str.rstrip('\t')
+ else:
+ print >>fout, out_str
prev_item = item
prev_vals = []
@@ -195,14 +211,15 @@ def main():
# Handle the last grouped value
out_str = prev_item
-
+ multiple_modes = False
+ mode_index = None
for i, op in enumerate(ops):
if op == 'cuniq':
rfunc = "r.c"
else:
rfunc = "r." + op
try:
- if op not in ['c','length','unique','random','cuniq']:
+ if op not in str_ops:
for j, elem in enumerate( prev_vals[i] ):
prev_vals[i][j] = float( elem )
rout = eval( rfunc )( prev_vals[i] )
@@ -216,7 +233,10 @@ def main():
else:
rand_index = random.randint(0,len(prev_vals[i])-1)
rout = prev_vals[i][rand_index]
-
+
+ if op == 'Mode' and rout == '>1 mode':
+ multiple_modes = True
+ mode_index = i
if op == 'unique':
rfunc = "r.length"
rout = eval( rfunc )( rout )
@@ -238,7 +258,13 @@ def main():
if not first_invalid_line:
first_invalid_line = ii+1
- print >>fout, out_str
+ if multiple_modes and mode_index != None:
+ out_str_list = out_str.split('\t')
+ for val in prev_vals[mode_index]:
+ out_str = '\t'.join(out_str_list[:mode_index+1]) + '\t' + str(val) + '\t' + '\t'.join(out_str_list[mode_index+2:])
+ print >>fout, out_str.rstrip('\t')
+ else:
+ print >>fout, out_str
# Generate a useful info message.
msg = "--Group by c%d: " %(group_col+1)
--- a/tools/stats/grouping.xml
+++ b/tools/stats/grouping.xml
@@ -1,4 +1,4 @@
-<tool id="Grouping1" name="Group" version="1.9.0">
+<tool id="Grouping1" name="Group" version="1.9.1"><description>data by a column and perform aggregate operation on other columns.</description><command interpreter="python">
grouping.py
@@ -22,6 +22,7 @@
<param name="optype" type="select" label="Type"><option value="mean">Mean</option><option value="median">Median</option>
+ <option value="Mode">Mode</option><option value="max">Maximum</option><option value="min">Minimum</option><option value="sum">Sum</option>
@@ -77,10 +78,12 @@
**Syntax**
-This tool allows you to group the input dataset by a particular column and perform aggregate functions like Mean, Sum, Max, Min and Concatenate on other columns.
+This tool allows you to group the input dataset by a particular column and perform aggregate functions like Mean, Median, Mode, Sum, Max, Min, Count, Random draw and Concatenate on other columns.
- All invalid, blank and comment lines are skipped when performing the aggregate functions. The number of skipped lines is displayed in the resulting history item.
+- If multiple modes are present, all are reported.
+
-----
**Example**
1
0

galaxy-dist commit a8ef6a46d622: Added AttributeValueSplitterFilter to tool select parameter filters so that GFF attributes can be read and used as tool inputs. Updated gff_filtering tool to use this filter.
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1280439706 14400
# Node ID a8ef6a46d6221d8eb3a7bc919fc3e2b13c1bc2aa
# Parent 66e03194e2dae00723d446509e077a869f274922
Added AttributeValueSplitterFilter to tool select parameter filters so that GFF attributes can be read and used as tool inputs. Updated gff_filtering tool to use this filter.
--- a/lib/galaxy/tools/parameters/dynamic_options.py
+++ b/lib/galaxy/tools/parameters/dynamic_options.py
@@ -206,6 +206,40 @@ class MultipleSplitterFilter( Filter ):
for field in fields[column].split( self.separator ):
rval.append( fields[0:column] + [field] + fields[column:] )
return rval
+
+class AttributeValueSplitterFilter( Filter ):
+ """
+ Filters a list of attribute-value pairs to be unique attribute names.
+
+ Type: attribute_value_splitter
+
+ Required Attributes:
+ column: column in options to compare with
+ Optional Attributes:
+ pair_separator: Split column by this (,)
+ name_val_separator: Split name-value pair by this ( whitespace )
+ """
+ def __init__( self, d_option, elem ):
+ Filter.__init__( self, d_option, elem )
+ self.pair_separator = elem.get( "pair_separator", "," )
+ self.name_val_separator = elem.get( "name_val_separator", None )
+ self.columns = elem.get( "column", None )
+ assert self.columns is not None, "Required 'columns' attribute missing from filter"
+ self.columns = [ int ( column ) for column in self.columns.split( "," ) ]
+ def filter_options( self, options, trans, other_values ):
+ attr_names = []
+ rval = []
+ for fields in options:
+ for column in self.columns:
+ for pair in fields[column].split( self.pair_separator ):
+ ary = pair.split( self.name_val_separator )
+ if len( ary ) == 2:
+ name, value = ary
+ if name not in attr_names:
+ rval.append( fields[0:column] + [name] + fields[column:] )
+ attr_names.append( name )
+ return rval
+
class AdditionalValueFilter( Filter ):
"""
@@ -322,6 +356,7 @@ filter_types = dict( data_meta = DataMet
static_value = StaticValueFilter,
unique_value = UniqueValueFilter,
multiple_splitter = MultipleSplitterFilter,
+ attribute_value_splitter = AttributeValueSplitterFilter,
add_value = AdditionalValueFilter,
remove_value = RemoveValueFilter,
sort_by = SortByColumnFilter )
--- a/tools/ngs_rna/gff_filtering.xml
+++ b/tools/ngs_rna/gff_filtering.xml
@@ -5,8 +5,12 @@
</command><inputs><param format="gff" name="input" type="data" label="Filter" help="Query missing? See TIP below."/>
- <param name="attribute_name" size="40" type="text" label="Attribute name" help="">
- <validator type="empty_field" message="Enter a valid attribute name."/>
+ <param name="attribute_name" type="select" label="Attribute name" help="">
+ <options from_dataset="input">
+ <column name="name" index="8"/>
+ <column name="value" index="8"/>
+ <filter type="attribute_value_splitter" pair_separator=";" column="8"/>
+ </options></param><param name="attribute_type" type="select" label="Attribute type"><option value="float">Float</option>
1
0
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1280424157 14400
# Node ID 66e03194e2dae00723d446509e077a869f274922
# Parent 07e7db914c6d2090ef7579a5e2c0583af50c39f3
lims:
- sample dataset size now appear correctly in the grid. The path of the datasets no longer appears with it.
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1624,7 +1624,7 @@ class Sample( object ):
output = pexpect.run(cmd, events={'.ssword:*': datatx_info['password']+'\r\n',
pexpect.TIMEOUT:print_ticks},
timeout=10)
- return output.split('\t')[0]
+ return output.replace(filepath, '').strip()
class SampleState( object ):
def __init__(self, name=None, desc=None, request_type=None):
1
0

galaxy-dist commit 0736d9af9e6e: Allow access to API Key creation when use_remote_user is true.
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1280416177 14400
# Node ID 0736d9af9e6e7facef8fad93baa3a303e0cf9526
# Parent d27912281a74ba74a8df6f5d72db7c553ca846c5
Allow access to API Key creation when use_remote_user is true.
--- /dev/null
+++ b/templates/webapps/galaxy/user/api_keys.mako
@@ -0,0 +1,35 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolForm">
+ <form name="user_api_keys" id="user_api_keys" action="${h.url_for( controller='user', action='api_keys' )}" method="post" >
+ <div class="toolFormTitle">Web API Key</div>
+ <div class="toolFormBody">
+ <div class="form-row">
+ <label>Current API key:</label>
+ %if user.api_keys:
+ ${user.api_keys[0].key}
+ %else:
+ none set
+ %endif
+ </div>
+ <div class="form-row">
+ <input type="submit" name="new_api_key_button" value="Generate a new key now"/>
+ %if user.api_keys:
+ (invalidates old key)
+ %endif
+ <div class="toolParamHelp" style="clear: both;">
+ An API key will allow you to access Galaxy via its web
+ API (documentation forthcoming). Please note that
+ <strong>this key acts as an alternate means to access
+ your account, and should be treated with the same care
+ as your login password</strong>.
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
--- a/lib/galaxy/web/framework/middleware/remoteuser.py
+++ b/lib/galaxy/web/framework/middleware/remoteuser.py
@@ -95,6 +95,8 @@ class RemoteUser( object ):
return self.error( start_response, title, message )
if path_info.startswith( '/user/create' ) and environ[ 'HTTP_REMOTE_USER' ] in self.admin_users:
pass # admins can create users
+ elif path_info.startswith( '/user/api_keys' ):
+ pass
elif path_info.startswith( '/user' ):
title = "Access to Galaxy user controls is disabled"
message = """
--- a/lib/galaxy/web/controllers/user.py
+++ b/lib/galaxy/web/controllers/user.py
@@ -1003,24 +1003,20 @@ class User( BaseController ):
@web.expose
- def new_api_key( self, trans, **kwd ):
+ def api_keys( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- admin_view = util.string_as_bool( params.get( 'admin_view', False ) )
error = ''
- user = trans.sa_session.query( trans.app.model.User ).get( int( params.get( 'user_id', None ) ) )
if params.get( 'new_api_key_button', None ) == 'Generate a new key now':
new_key = trans.app.model.APIKeys()
- new_key.user_id = user.id
+ new_key.user_id = trans.user.id
new_key.key = trans.app.security.get_new_guid()
trans.sa_session.add( new_key )
trans.sa_session.flush()
message = "Generated a new web API key"
status = "done"
- return trans.response.send_redirect( web.url_for( controller='user',
- action='show_info',
- admin_view=admin_view,
- user_id=user.id,
- message=message,
- status=status ) )
+ return trans.fill_template( 'webapps/galaxy/user/api_keys.mako',
+ user=trans.user,
+ message=message,
+ status=status )
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -130,6 +130,9 @@
%if app.config.get_bool( 'enable_pages', False ):
<li><a href="${h.url_for( controller='/page', action='list' )}">Pages</a></li>
%endif
+ %if app.config.enable_api:
+ <li><a target="galaxy_main" href="${h.url_for( controller='/user', action='api_keys' )}">API Keys</a></li>
+ %endif
</ul></div></td>
--- a/templates/webapps/galaxy/user/info.mako
+++ b/templates/webapps/galaxy/user/info.mako
@@ -120,36 +120,3 @@
</div></form></div>
-
-<p/>
-
-%if trans.app.config.enable_api:
- <div class="toolForm">
- <form name="user_api_keys" id="user_api_keys" action="${h.url_for( controller='user', action='new_api_key', user_id=user.id, admin_view=admin_view )}" method="post" >
- <div class="toolFormTitle">Web API Key</div>
- <div class="toolFormBody">
- <div class="form-row">
- <label>Current API key:</label>
- %if user.api_keys:
- ${user.api_keys[0].key}
- %else:
- none set
- %endif
- </div>
- <div class="form-row">
- <input type="submit" name="new_api_key_button" value="Generate a new key now"/>
- %if user.api_keys:
- (invalidates old key)
- %endif
- <div class="toolParamHelp" style="clear: both;">
- An API key will allow you to access Galaxy via its web
- API (documentation forthcoming). Please note that
- <strong>this key acts as an alternate means to access
- your account, and should be treated with the same care
- as your login password</strong>.
- </div>
- </div>
- </div>
- </form>
- </div>
-%endif
--- a/templates/user/index.mako
+++ b/templates/user/index.mako
@@ -12,6 +12,9 @@
%if webapp == 'galaxy':
<li><a href="${h.url_for( controller='user', action='show_info' )}">${_('Manage your information')}</a></li><li><a href="${h.url_for( controller='user', action='set_default_permissions' )}">${_('Change default permissions')}</a> for new histories</li>
+ %if trans.app.config.enable_api:
+ <li><a href="${h.url_for( controller='user', action='api_keys' )}">${_('Manage your API Keys')}</a> for new histories</li>
+ %endif
%else:
<li><a href="${h.url_for( controller='user', action='show_info', webapp='community' )}">${_('Manage your information')}</a></li>
%endif
1
0

galaxy-dist commit c653ccfa1a1e: Allow access to /api without HTTP_REMOTE_USER set if use_remote_user = True, since the API controllers handle authentication internally.
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1280416580 14400
# Node ID c653ccfa1a1ef7c2a384ee4b3ab72da0b391ae14
# Parent ab48c0e20a948f310ad3b072c23560faa8433aa3
Allow access to /api without HTTP_REMOTE_USER set if use_remote_user = True, since the API controllers handle authentication internally.
--- a/lib/galaxy/web/framework/middleware/remoteuser.py
+++ b/lib/galaxy/web/framework/middleware/remoteuser.py
@@ -75,8 +75,8 @@ class RemoteUser( object ):
# Apache sets REMOTE_USER to the string '(null)' when using the
# Rewrite* method for passing REMOTE_USER and a user is
# un-authenticated. Any other possible values need to go here as well.
+ path_info = environ.get('PATH_INFO', '')
if environ.has_key( 'HTTP_REMOTE_USER' ) and environ[ 'HTTP_REMOTE_USER' ] != '(null)':
- path_info = environ.get('PATH_INFO', '')
if not environ[ 'HTTP_REMOTE_USER' ].count( '@' ):
if self.maildomain is not None:
environ[ 'HTTP_REMOTE_USER' ] += '@' + self.maildomain
@@ -96,7 +96,7 @@ class RemoteUser( object ):
if path_info.startswith( '/user/create' ) and environ[ 'HTTP_REMOTE_USER' ] in self.admin_users:
pass # admins can create users
elif path_info.startswith( '/user/api_keys' ):
- pass
+ pass # api keys can be managed when remote_user is in use
elif path_info.startswith( '/user' ):
title = "Access to Galaxy user controls is disabled"
message = """
@@ -105,6 +105,9 @@ class RemoteUser( object ):
"""
return self.error( start_response, title, message )
return self.app( environ, start_response )
+ elif path_info.startswith( '/api/' ):
+ # The API handles its own authentication via keys
+ return self.app( environ, start_response )
else:
title = "Access to Galaxy is denied"
message = """
1
0

galaxy-dist commit 8d255150cb3e: Use env for the API example scripts instead of directly referring to /usr/bin/python
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1280416695 14400
# Node ID 8d255150cb3e868974777eee030843d2c3e7ad16
# Parent c653ccfa1a1ef7c2a384ee4b3ab72da0b391ae14
Use env for the API example scripts instead of directly referring to /usr/bin/python
--- a/scripts/api/library_upload_from_import_dir.py
+++ b/scripts/api/library_upload_from_import_dir.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
import os, sys
sys.path.insert( 0, os.path.dirname( __file__ ) )
--- a/scripts/api/library_create_library.py
+++ b/scripts/api/library_create_library.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
import os, sys
sys.path.insert( 0, os.path.dirname( __file__ ) )
--- a/scripts/api/library_create_folder.py
+++ b/scripts/api/library_create_folder.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
import os, sys
sys.path.insert( 0, os.path.dirname( __file__ ) )
--- a/scripts/api/display.py
+++ b/scripts/api/display.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
import os, sys
sys.path.insert( 0, os.path.dirname( __file__ ) )
1
0
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1280417621 14400
# Node ID 79baa9583d9390bb6341d3484aa9f55b8e3808f8
# Parent 8d255150cb3e868974777eee030843d2c3e7ad16
lims:
- fixed a migration script bug when moving the sample datasets info from the 'sample' to 'sample_dataset' table
--- a/lib/galaxy/model/migrate/versions/0052_sample_dataset_table.py
+++ b/lib/galaxy/model/migrate/versions/0052_sample_dataset_table.py
@@ -61,19 +61,20 @@ def upgrade():
result = db_session.execute( cmd )
for r in result:
sample_id = r[0]
- dataset_files = from_json_string(r[1])
- for df in dataset_files:
- cmd = "INSERT INTO sample_dataset VALUES (%s, %s, %s, %s, '%s', '%s', '%s', '%s', '%s')"
- cmd = cmd % ( nextval('sample_dataset'),
- localtimestamp(),
- localtimestamp(),
- str(sample_id),
- df['name'],
- df['filepath'],
- df['status'],
- df['error_msg'].replace('"', '').replace("'", ""),
- df['size'] )
- db_session.execute( cmd )
+ if r[1]:
+ dataset_files = from_json_string(r[1])
+ for df in dataset_files:
+ cmd = "INSERT INTO sample_dataset VALUES (%s, %s, %s, %s, '%s', '%s', '%s', '%s', '%s')"
+ cmd = cmd % ( nextval('sample_dataset'),
+ localtimestamp(),
+ localtimestamp(),
+ str(sample_id),
+ df['name'],
+ df['filepath'],
+ df['status'].replace('"', '').replace("'", ""),
+ "",
+ df['size'].replace('"', '').replace("'", "") )
+ db_session.execute( cmd )
# Delete the dataset_files column in the Sample table
try:
1
0

galaxy-dist commit ab48c0e20a94: Merge with 0736d9af9e6e7facef8fad93baa3a303e0cf9526
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1280416325 14400
# Node ID ab48c0e20a948f310ad3b072c23560faa8433aa3
# Parent b0cf8217826e7a289eec88fd31894683818d2bee
# Parent 0736d9af9e6e7facef8fad93baa3a303e0cf9526
Merge with 0736d9af9e6e7facef8fad93baa3a303e0cf9526
1
0
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1280416255 14400
# Node ID b0cf8217826e7a289eec88fd31894683818d2bee
# Parent d27912281a74ba74a8df6f5d72db7c553ca846c5
lims:
- fixed couple of broken links
- comminting the db migrate script (0052) skipped in the previous commit
--- /dev/null
+++ b/lib/galaxy/model/migrate/versions/0052_sample_dataset_table.py
@@ -0,0 +1,92 @@
+"""
+Migration script to add the sample_dataset table and remove the 'dataset_files' column
+from the 'sample' table
+"""
+
+from sqlalchemy import *
+from sqlalchemy.orm import *
+from migrate import *
+from migrate.changeset import *
+from sqlalchemy.exc import *
+
+from galaxy.model.custom_types import *
+from galaxy.util.json import from_json_string, to_json_string
+
+import datetime
+now = datetime.datetime.utcnow
+
+import logging
+log = logging.getLogger( __name__ )
+
+metadata = MetaData( migrate_engine )
+db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) )
+
+
+def nextval( table, col='id' ):
+ if migrate_engine.name == 'postgres':
+ return "nextval('%s_%s_seq')" % ( table, col )
+ elif migrate_engine.name == 'mysql' or migrate_engine.name == 'sqlite':
+ return "null"
+ else:
+ raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name )
+
+def localtimestamp():
+ if migrate_engine.name == 'postgres' or migrate_engine.name == 'mysql':
+ return "LOCALTIMESTAMP"
+ elif migrate_engine.name == 'sqlite':
+ return "current_date || ' ' || current_time"
+ else:
+ raise Exception( 'Unable to convert data for unknown database type: %s' % db )
+
+SampleDataset_table = Table('sample_dataset', metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "sample_id", Integer, ForeignKey( "sample.id" ), index=True ),
+ Column( "name", TrimmedString( 255 ), nullable=False ),
+ Column( "file_path", TrimmedString( 255 ), nullable=False ),
+ Column( "status", TrimmedString( 255 ), nullable=False ),
+ Column( "error_msg", TEXT ),
+ Column( "size", TrimmedString( 255 ) ) )
+
+def upgrade():
+ print __doc__
+ metadata.reflect()
+ try:
+ SampleDataset_table.create()
+ except Exception, e:
+ log.debug( "Creating sample_dataset table failed: %s" % str( e ) )
+
+ cmd = "SELECT id, dataset_files FROM sample"
+ result = db_session.execute( cmd )
+ for r in result:
+ sample_id = r[0]
+ dataset_files = from_json_string(r[1])
+ for df in dataset_files:
+ cmd = "INSERT INTO sample_dataset VALUES (%s, %s, %s, %s, '%s', '%s', '%s', '%s', '%s')"
+ cmd = cmd % ( nextval('sample_dataset'),
+ localtimestamp(),
+ localtimestamp(),
+ str(sample_id),
+ df['name'],
+ df['filepath'],
+ df['status'],
+ df['error_msg'].replace('"', '').replace("'", ""),
+ df['size'] )
+ db_session.execute( cmd )
+
+ # Delete the dataset_files column in the Sample table
+ try:
+ Sample_table = Table( "sample", metadata, autoload=True )
+ except NoSuchTableError:
+ Sample_table = None
+ log.debug( "Failed loading table sample" )
+ if Sample_table:
+ try:
+ Sample_table.c.dataset_files.drop()
+ except Exception, e:
+ log.debug( "Deleting column 'dataset_files' from the 'sample' table failed: %s" % ( str( e ) ) )
+
+
+def downgrade():
+ pass
--- a/templates/requests/common/events.mako
+++ b/templates/requests/common/events.mako
@@ -4,7 +4,7 @@
<h2>History of Sequencing Request "${request.name}"</h2><ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='show_request', id=trans.security.encode_id(request.id) )}">
+ <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='show', id=trans.security.encode_id(request.id) )}"><span>Browse this request</span></a></li><li>
--- a/templates/admin/requests/reject.mako
+++ b/templates/admin/requests/reject.mako
@@ -8,12 +8,12 @@
<h2>Reject Sequencing Request "${request.name}"</h2><ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='show_request', id=trans.security.encode_id(request.id) )}">
- <span>Browse this request</span></a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='events', id=trans.security.encode_id(request.id) )}">
+ <span>Events</span></a></li><li>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='events', id=trans.security.encode_id(request.id) )}">
- <span>Events</span></a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='show', id=trans.security.encode_id(request.id) )}">
+ <span>Browse this request</span></a></li></ul><h3>User: ${request.user.email}</h3>
--- a/templates/admin/requests/datasets_grid.mako
+++ b/templates/admin/requests/datasets_grid.mako
@@ -4,7 +4,6 @@
<%def name="custom_javascripts()"><script type="text/javascript">
$("#select-dataset-action-button").bind( "click", function(e) {
- alert('afdhblvi')
$.ajax({
url: "${h.url_for( controller='requests_admin', action='remote_file_browser' )}",
data: {id: 6},
1
0
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Kanwei Li <kanwei(a)gmail.com>
# Date 1280357492 14400
# Node ID 75e99661d24caaeda1381b14cd062ffdcf7c3ecd
# Parent fd9514c5349fadaf31abec04fd8725cc50a4213f
trackster:
- Fix Reference track incorrect display offset
- When no tracks exist in editor, show suggestion to add tracks
- Clicking "Visualize in Trackster" in history now shows a grid of current browsers, but actual adding is still work in progress
--- a/lib/galaxy/web/controllers/tracks.py
+++ b/lib/galaxy/web/controllers/tracks.py
@@ -87,6 +87,35 @@ class DatasetSelectionGrid( grids.Grid )
.filter( model.History.deleted == False ) \
.filter( model.HistoryDatasetAssociation.deleted == False )
+class TracksterSelectionGrid( grids.Grid ):
+# class DbKeyColumn( grids.GridColumn ):
+# def filter( self, trans, user, query, dbkey ):
+# """ Filter by dbkey. """
+# # use raw SQL b/c metadata is a BLOB
+# dbkey = dbkey.replace("'", "\\'")
+# return query.filter( or_( "metadata like '%%\"dbkey\": [\"%s\"]%%'" % dbkey, "metadata like '%%\"dbkey\": \"%s\"%%'" % dbkey ) )
+
+ # Grid definition.
+ title = "Insert into visualization"
+ template = "/tracks/add_to_viz.mako"
+ async_template = "/page/select_items_grid_async.mako"
+ model_class = model.Visualization
+ default_filter = { "deleted" : "False" , "shared" : "All" }
+ default_sort_key = "title"
+ use_async = True
+ use_paging = False
+ columns = [
+ grids.TextColumn( "Title", key="title", model_class=model.Visualization )
+ ]
+ columns.append(
+ grids.MulticolFilterColumn( "Search", cols_to_filter=[ columns[0] ],
+ key="free-text-search", visible=False, filterable="standard" )
+ )
+
+ def build_initial_query( self, trans, **kwargs ):
+ return trans.sa_session.query( self.model_class )
+ def apply_query_filter( self, trans, query, **kwargs ):
+ return query.filter( self.model_class.user_id == trans.user.id )
class TracksController( BaseController, UsesVisualization ):
"""
@@ -138,7 +167,7 @@ class TracksController( BaseController,
@web.expose
@web.require_login()
- def browser(self, trans, id, chrom=""):
+ def browser(self, trans, id, chrom="", **kwargs):
"""
Display browser for the datasets listed in `dataset_ids`.
"""
@@ -149,7 +178,7 @@ class TracksController( BaseController,
# Set config chrom.
viz_config[ 'chrom' ] = chrom
- return trans.fill_template( 'tracks/browser.mako', config=viz_config )
+ return trans.fill_template( 'tracks/browser.mako', config=viz_config, add_dataset=kwargs.get("dataset_id", None) )
@web.json
def chroms(self, trans, vis_id=None, dbkey=None ):
@@ -288,10 +317,6 @@ class TracksController( BaseController,
data = data['data']
return { 'dataset_type': dataset_type, 'extra_info': extra_info, 'data': data, 'message': message }
- @web.expose
- def list_tracks( self, trans, hid ):
- return None
-
@web.json
def save( self, trans, **kwargs ):
session = trans.sa_session
@@ -328,6 +353,7 @@ class TracksController( BaseController,
return trans.security.encode_id(vis.id)
data_grid = DatasetSelectionGrid()
+ tracks_grid = TracksterSelectionGrid()
@web.expose
@web.require_login( "see all available datasets" )
@@ -336,3 +362,8 @@ class TracksController( BaseController,
# Render the list view
return self.data_grid( trans, **kwargs )
+
+ @web.expose
+ def list_tracks( self, trans, **kwargs ):
+ return self.tracks_grid( trans, **kwargs )
+
--- /dev/null
+++ b/templates/tracks/add_to_viz.mako
@@ -0,0 +1,13 @@
+## Template generates a grid that enables user to add tracks
+<%namespace file="../grid_base.mako" import="*" />
+
+${stylesheets()}
+${grid_javascripts()}
+${render_grid_header( grid, False )}
+${render_grid_table( grid, show_item_checkboxes=True )}
+
+## Initialize the grid.
+<script type="text/javascript">
+ init_grid_elements();
+ init_grid_controls();
+</script>
--- a/static/scripts/packed/galaxy.base.js
+++ b/static/scripts/packed/galaxy.base.js
@@ -1,1 +1,1 @@
-$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function ensure_popup_helper(){if($("#popup-helper").length===0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}function make_popupmenu(c,b){ensure_popup_helper();var a=$("<ul id='"+c.attr("id")
+"-menu'></ul>");$.each(b,function(f,e){if(e){$("<li/>").html(f).click(e).appendTo(a)}else{$("<li class='head'/>").html(f).appendTo(a)}});var d=$("<div class='popmenu-wrapper'>");d.append(a).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(c,d)}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}function array_length(b){if(b.length){return b.length}var c=0;for(var a in b){c++}return c}function naturalSort(i,g){var n=/(-?[0-9\.]+)/g,j=i.toString().toLowerCase()||"",f=g.toString().toLowerCase()||"",k=String.fromCharCode(0),l=j.replace(n,k+"
$1"+k).split(k),e=f.replace(n,k+"$1"+k).split(k),d=(new Date(j)).getTime(),m=d?(new Date(f)).getTime():null;if(m){if(d<m){return -1}else{if(d>m){return 1}}}for(var h=0,c=Math.max(l.length,e.length);h<c;h++){oFxNcL=parseFloat(l[h])||l[h];oFyNcL=parseFloat(e[h])||e[h];if(oFxNcL<oFyNcL){return -1}else{if(oFxNcL>oFyNcL){return 1}}}return 0}function replace_big_select_inputs(a,b){if(!jQuery().autocomplete){return}if(a===undefined){a=20}if(b===undefined){b=3000}$("select").each(function(){var e=$(this);var h=e.find("option").length;if((h<a)||(h>b)){return}if(e.attr("multiple")==true){return}if(e.hasClass("no-autocomplete")){return}var l=e.attr("value");var c=$("<input type='text' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",e.attr("name"));c.attr("id",e.attr("id"));c.click(function(){var m=$(this).val();$(this).val("Loading...");$(this).showAllInCache();$(this).val(m);$(this).select()});var f=[];var i={};e.children("option").each(function(){var n
=$(this).text();var m=$(this).attr("value");f.push(n);i[n]=m;i[m]=m;if(m==l){c.attr("value",n)}});if(l==""||l=="?"){c.attr("value","Click to Search or Select")}if(e.attr("name")=="dbkey"){f=f.sort(naturalSort)}var g={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:b,minChars:0,hideForLessThanMinChars:false};c.autocomplete(f,g);e.replaceWith(c);var k=function(){var n=c.attr("value");var m=i[n];if(m!==null&&m!==undefined){c.attr("value",m)}else{if(l!=""){c.attr("value",l)}else{c.attr("value","?")}}};c.parents("form").submit(function(){k()});$(document).bind("convert_dbkeys",function(){k()});if(e.attr("refresh_on_change")=="true"){var d=e.attr("refresh_on_change_values");if(d!==undefined){d=d.split(",")}var j=function(){var o=c.attr("value");var n=i[o];if(n!==null&&n!==undefined){refresh=false;if(d!==undefined){for(var m=0;m<d.length;m++){if(n==d[m]){refresh=true;break}}}else{refresh=true}if(refresh){c.attr("value",n);c.parents("form").submit()}}};c.bind
("result",j);c.keyup(function(m){if(m.keyCode===13){j()}});c.keydown(function(m){if(m.keyCode===13){return false}})}})}function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text(k)}else{j=$("<input type='text'></input>").attr({value:k,size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){l.text(o);if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStore.store("history_expand_state");if(e){for(var g in e){$
("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStore.remove("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id;var h=$(this).children("div.historyItemBody");var i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){var k=$.jStore.store("history_expand_state");if(k){delete k[j];$.jStore.store("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){var k=$.jStore.store("history_expand_state");if(k===undefined){k={}}k[j]=true;$.jStore.store("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStore.store("history_expand_state");if(h===undefined)
{h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStore.store("history_expand_state",h)}).show()};if(a){b()}else{$.jStore.init("galaxy");$.jStore.engineReady(function(){b()})}}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length==0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!="recently_used_wrapper"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.
val("search tools");b.css("font-style","italic")}}function GalaxyAsync(a){this.url_dict={};this.log_action=(a===undefined?false:a)}GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(document).ready(function(){$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tipsy){$(".tooltip").tipsy({gravity:"s"})}make_popup_menus();replace_big_select_inputs(20,1500)});
+$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function ensure_popup_helper(){if($("#popup-helper").length===0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}function make_popupmenu(c,b){ensure_popup_helper();var a=$("<ul id='"+c.attr("id")
+"-menu'></ul>");$.each(b,function(f,e){if(e){$("<li/>").html(f).click(e).appendTo(a)}else{$("<li class='head'/>").html(f).appendTo(a)}});var d=$("<div class='popmenu-wrapper'>");d.append(a).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(c,d)}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}function array_length(b){if(b.length){return b.length}var c=0;for(var a in b){c++}return c}function naturalSort(i,g){var n=/(-?[0-9\.]+)/g,j=i.toString().toLowerCase()||"",f=g.toString().toLowerCase()||"",k=String.fromCharCode(0),l=j.replace(n,k+"
$1"+k).split(k),e=f.replace(n,k+"$1"+k).split(k),d=(new Date(j)).getTime(),m=d?(new Date(f)).getTime():null;if(m){if(d<m){return -1}else{if(d>m){return 1}}}for(var h=0,c=Math.max(l.length,e.length);h<c;h++){oFxNcL=parseFloat(l[h])||l[h];oFyNcL=parseFloat(e[h])||e[h];if(oFxNcL<oFyNcL){return -1}else{if(oFxNcL>oFyNcL){return 1}}}return 0}function replace_big_select_inputs(a,b){if(!jQuery().autocomplete){return}if(a===undefined){a=20}if(b===undefined){b=3000}$("select").each(function(){var e=$(this);var h=e.find("option").length;if((h<a)||(h>b)){return}if(e.attr("multiple")==true){return}if(e.hasClass("no-autocomplete")){return}var l=e.attr("value");var c=$("<input type='text' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",e.attr("name"));c.attr("id",e.attr("id"));c.click(function(){var m=$(this).val();$(this).val("Loading...");$(this).showAllInCache();$(this).val(m);$(this).select()});var f=[];var i={};e.children("option").each(function(){var n
=$(this).text();var m=$(this).attr("value");f.push(n);i[n]=m;i[m]=m;if(m==l){c.attr("value",n)}});if(l==""||l=="?"){c.attr("value","Click to Search or Select")}if(e.attr("name")=="dbkey"){f=f.sort(naturalSort)}var g={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:b,minChars:0,hideForLessThanMinChars:false};c.autocomplete(f,g);e.replaceWith(c);var k=function(){var n=c.attr("value");var m=i[n];if(m!==null&&m!==undefined){c.attr("value",m)}else{if(l!=""){c.attr("value",l)}else{c.attr("value","?")}}};c.parents("form").submit(function(){k()});$(document).bind("convert_dbkeys",function(){k()});if(e.attr("refresh_on_change")=="true"){var d=e.attr("refresh_on_change_values");if(d!==undefined){d=d.split(",")}var j=function(){var o=c.attr("value");var n=i[o];if(n!==null&&n!==undefined){refresh=false;if(d!==undefined){for(var m=0;m<d.length;m++){if(n==d[m]){refresh=true;break}}}else{refresh=true}if(refresh){c.attr("value",n);c.parents("form").submit()}}};c.bind
("result",j);c.keyup(function(m){if(m.keyCode===13){j()}});c.keydown(function(m){if(m.keyCode===13){return false}})}})}function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text(k)}else{j=$("<input type='text'></input>").attr({value:k,size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){l.text(o);if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStore.store("history_expand_state");if(e){for(var g in e){$
("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStore.remove("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id;var h=$(this).children("div.historyItemBody");var i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){var k=$.jStore.store("history_expand_state");if(k){delete k[j];$.jStore.store("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){var k=$.jStore.store("history_expand_state");if(k===undefined){k={}}k[j]=true;$.jStore.store("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStore.store("history_expand_state");if(h===undefined)
{h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStore.store("history_expand_state",h)}).show()};if(a){b()}else{$.jStore.init("galaxy");$.jStore.engineReady(function(){b()})}}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length==0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!="recently_used_wrapper"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.
val("search tools");b.css("font-style","italic")}}function GalaxyAsync(a){this.url_dict={};this.log_action=(a===undefined?false:a)}GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(".trackster-add").live("click",function(){var b=this,a=$(this);$.ajax({url:a.attr("data-url"),data:{"f-dbkey":"hi"},dataType:"html",error:function(){alert("Could not add this dataset to browser.")},success:function(c){var d=window.parent;d.show_modal("Add to Browser:",c,{"Insert Dataset In
to":function(){$(d.document).find("input[name=id]:checked").each(function(){var e=$(this).val();d.location=a.attr("action-url")+"&id="+e});d.hide_modal()},Cancel:function(){d.hide_modal()}})}})});$(document).ready(function(){$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tipsy){$(".tooltip").tipsy({gravity:"s"})}make_popup_menus();replace_big_select_inputs(20,1500)});
--- a/static/scripts/packed/trackster.js
+++ b/static/scripts/packed/trackster.js
@@ -1,1 +1,1 @@
-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"),PX_PER_CHAR=CONTEXT.measureText("A").width,RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/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=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONT
EXT.createPattern(right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src=image_path+"/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};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 View=function(a,c,e,d,b){this.container=a;this.vis_id=d;this.dbkey=b;this.title=e;this.chrom=c;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.track_id_counter=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{in
it:function(){var b=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(b);this.content_div=$("<div/>").addClass("content").css("position","relative").appendTo(b);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide().appendTo(b);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);this.viewport=$("<div/>").addClass("viewport").appendTo(this.viewport_container);this.nav_container=$("<div/>").addClass("nav-container").appendTo(b);this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.nav_container);this.nav=$("<div/>").addClass("nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.nav);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.
nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_form=$("<form/>").attr("action",function(){void (0)}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);this.low_input=$("<input/>").addClass("low").css("width","10em").appendTo(this.chrom_form);$("<span/>").text(" - ").appendTo(this.chrom_form);this.high_input=$("<input/>").addClass("high").css("width","10em").appendTo(this.chrom_form);if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.chrom_form)}this.zi_link=$("<a/>").click(function(){a.zoom_in();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom.png" />').appendTo(this.chrom_form);this.zo_link=$("<a/>").click(function(){a.zoom_out();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom-out.png" />').appendTo(t
his.chrom_form);$.ajax({url:chrom_url,data:(this.vis_id!==undefined?{vis_id:this.vis_id}:{dbkey:this.dbkey}),dataType:"json",success:function(c){if(c.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=c.chrom_info;var f='<option value="">Select Chrom/Contig</option>';for(i in a.chrom_data){var e=a.chrom_data[i]["chrom"];f+='<option value="'+e+'">'+e+"</option>"}a.chrom_select.html(f);var d=function(){if(a.chrom_select.val()===""){a.intro_div.show();a.content_div.hide()}else{a.intro_div.hide();a.content_div.show()}a.chrom=a.chrom_select.val();var h=$.grep(a.chrom_data,function(k,l){return k.chrom===a.chrom})[0];a.max_high=(h!==undefined?h.len:0);a.reset();a.redraw(true);for(var j in a.tracks){var g=a.tracks[j];if(g.init){g.init()}}a.redraw()};a.chrom_select.bind("change",d);d()},error:function(){alert("Could not load chroms for this dbkey:",a.dbkey)}});this.content_div.bind("dblclick",function(c){a.zoom_in(c.pageX,this.viewport_container)});this.overview_box.bin
d("dragstart",function(c){this.current_x=c.offsetX}).bind("drag",function(c){var f=c.offsetX-this.current_x;this.current_x=c.offsetX;var d=Math.round(f/a.viewport_container.width()*(a.max_high-a.max_low));a.move_delta(-d)});this.viewport_container.bind("dragstart",function(c){this.original_low=a.low;this.current_height=c.clientY;this.current_x=c.offsetX}).bind("drag",function(f){var c=$(this);var h=f.offsetX-this.current_x;var d=c.scrollTop()-(f.clientY-this.current_height);c.scrollTop(d);this.current_height=f.clientY;this.current_x=f.offsetX;var g=Math.round(h/a.viewport_container.width()*(a.high-a.low));a.move_delta(g)});this.top_labeltrack.bind("dragstart",function(c){this.drag_origin_x=c.clientX;this.drag_origin_pos=c.clientX/a.viewport_container.width()*(a.high-a.low)+a.low;this.drag_div=$("<div />").css({height:a.viewport_container.height(),top:"0px",position:"absolute","background-color":"#cfc",border:"1px solid #6a6",opacity:0.5,"z-index":1000}).appendTo($(this))}).b
ind("drag",function(h){var d=Math.min(h.clientX,this.drag_origin_x)-a.container.offset().left,c=Math.max(h.clientX,this.drag_origin_x)-a.container.offset().left,g=(a.high-a.low),f=a.viewport_container.width();a.low_input.val(commatize(Math.round(d/f*g)+a.low));a.high_input.val(commatize(Math.round(c/f*g)+a.low));this.drag_div.css({left:d+"px",width:(c-d)+"px"})}).bind("dragend",function(j){var d=Math.min(j.clientX,this.drag_origin_x),c=Math.max(j.clientX,this.drag_origin_x),g=(a.high-a.low),f=a.viewport_container.width(),h=a.low;a.low=Math.round(d/f*g)+h;a.high=Math.round(c/f*g)+h;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(this,this.top_labeltrack));this.add_label_track(new LabelTrack(this,this.nav_labeltrack))},move_delta:function(c){var a=this;var b=a.high-a.low;if(a.low-c<a.max_low){a.low=a.max_low;a.high=a.max_low+b}else{if(a.high-c>a.max_high){a.high=a.max_high;a.low=a.max_high-b}else{a.high-=c;a.low-=c}}a.redraw()},add_track:function(a){a.v
iew=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){this.has_changes=true;a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[this.tracks.indexOf(a)]},update_options:function(){this.has_changes=true;var b=$("ul#sortable-ul").sortable("toArray");for(var c in b){var e=b[c].split("_li")[0].split("track_")[1];this.viewport.append($("#track_"+e))}for(var d in view.tracks){var a=view.tracks[d];if(a&&a.update_options){a.update_options(d)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},redraw:function(f){var d=this.high-this.low,b=this.low,e=this.high;if(b<this.max_low){b=this.max_low}if(e>this.max_high){e=this.max_high}if(this.high!==0&&d<this.min_separation){e=b+this.min_separation}this.low=
Math.floor(b);this.high=Math.ceil(e);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))));this.overview_box.css({left:(this.low/(this.max_high-this.max_low))*this.overview_viewport.width(),width:Math.max(12,(this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())}).show();this.low_input.val(commatize(this.low));this.high_input.val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c]&&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(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(e-a);th
is.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);this.high=Math.round(c+a);this.redraw()}});var Track=function(b,a,c){this.name=b;this.parent_element=c;this.view=a;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 />").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","auto");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"||d.kind==="error"){a.container_div.addClas
s("error");a.content_div.text(DATA_ERROR);if(d.message){var f=a.view.tracks.indexOf(a);var e=$("<a href='javascript:void(0);'></a>").attr("id",f+"_error");e.text("Click to view error");$("#"+f+"_error").live("click",function(){show_modal("Trackster Error","<pre>"+d.message+"</pre>",{Close:hide_modal})});a.content_div.append(e)}}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NOCONVERTER)}else{if(d.data!==undefined&&(d.data===null||d.data.length===0)){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(){this.left_offset=200};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var j=th
is.view.low,e=this.view.high,f=e-j,d=this.view.resolution;var l=$("<div style='position: relative;'></div>"),m=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(l),this.max_height=0;var a=Math.floor(j/d/DENSITY);while((a*DENSITY*d)<e){var k=this.content_div.width()+"_"+m+"_"+a;var c=this.tile_cache.get(k);if(c){var g=a*DENSITY*d;var b=(g-j)*m;if(this.left_offset){b-=this.left_offset}c.css({left:b});l.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,k,j,e,a,d,l,m)}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,b){Track.call(this,null,a,b);this.track_type="LabelT
rack";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 ReferenceTrack=function(a){this.track_type="ReferenceTrack";Track.call(this,null,a,a.top_labeltrack);TiledTrack.call(this);this.hidden=true;this.height_px=12;this.container_div.addClass("reference-track");this.dummy_canvas=$("<canvas></canvas>").get(0).getContext("2d");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.prototype,{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:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,e=$("<canvas class='tile'></canvas>"),n=e.get(0).getContext("2d"),j=f+"_"+b;if(o>PX_PER_CHAR){if(this.data_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o+this.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o);n.fillText(m[h],a+this.left_offset,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b,a,c){this.tra
ck_type="LineTrack";Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=100;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(c.min_value!==undefined){this.prefs.min_value=c.min_value}if(c.max_value!==undefined){this.prefs.max_value=c.max_value}if(c.mode!==undefined){this.prefs.mode=c.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){a.container_div.addClass("line-track");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 />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var d=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px",left:"10px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px",left:"10px"});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,j){console.log(h,g,j)}})}},draw_tile:function(p,r,c,e){if(this.vertical_range===undefined){return}var s=r*DEN
SITY*p,a=DENSITY*p,b=$("<canvas class='tile'></canvas>"),v=p+"_"+r;if(this.data_cache.get(v)===undefined){this.get_data(p,r);return}var j=this.data_cache.get(v);if(j===null){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e+this.left_offset);b.get(0).height=this.height_px;var o=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,n=this.vertical_range,t=this.total_frequency,d=this.height_px,m=this.prefs.mode;o.beginPath();if(data.length>1){var f=Math.ceil((data[1][0]-data[0][0])*e)}else{var f=10}var u,h;for(var q=0;q<data.length;q++){u=(data[q][0]-s)*e;h=data[q][1];if(m=="Intensity"){if(h===null){continue}if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/n*255);o.fillStyle="rgb("+h+","+h+","+h+")";o.fillRect(u,0,f,this.height_px)}else{if(h===null){if(k&&m==="Filled"){o.lineTo(u,d)}k=false;continue}else{if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/n*d);if(k){o.lineTo(u,h)}else{k=true;if(m===
"Filled"){o.moveTo(u,d);o.lineTo(u,h)}else{o.moveTo(u,h)}}}}}if(m==="Filled"){if(k){o.lineTo(u,d)}o.fill()}else{o.stroke()}c.append(b);return b},gen_options:function(o){var a=$("<div />").addClass("form-row");var h="track_"+o+"_minval",m=$("<label></label>").attr("for",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),n=$("<input></input>").attr("id",h).val(b),l="track_"+o+"_maxval",g=$("<label></label>").attr("for",l).text("Max value:"),k=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",l).val(k),e="track_"+o+"_mode",d=$("<label></label>").attr("for",e).text("Display mode:"),j=(this.prefs.mode===undefined?"Line":this.prefs.mode),c=$('<select id="'+e+'"><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>');c.children("#mode_"+j).attr("selected","selected");return a.append(m).a
ppend(n).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(d,b,a,c){this.track_type="FeatureTrack";Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=0;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.inc_slots={};thi
s.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(c.block_color!==undefined){this.prefs.block_color=c.block_color}if(c.label_color!==undefined){this.prefs.label_color=c.label_color}if(c.show_counts!==undefined){this.prefs.show_counts=c.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;a.mode="Auto";if(a.mode_div){a.mode_div.remove()}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(d){a.mode_div=$("<div class='right-float menubutton popup' />").text("Display Mode");a.header_div.append(a.mode_div);a.mode="Auto";var c=function(e){a.mode_div.text(e);a.mode=e;a.tile_cache.clear();a.draw()};make_popupmenu(a.mode_div,{Auto:function(){c("Auto")},Dense:function(){c("Dense")},Sq
uish:function(){c("Squish")},Pack:function(){c("Pack")}});a.data_cache.set(b,d);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,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,h,c,r){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.inc_slots[a].mode=r;this.s_e_by_tile[a]={}}var n=this.inc_slots[a].w_scale,z=[],l=0,b=$("<canvas></canvas>").get(0).getContext("2d"),o=this.view.max_low;var B=[];if(this.inc_slots[a].mode!==r){delete this.inc_slots[a];this.inc_slots[a]={mode:r,w_scale:n};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var w=0,x=h.length;w<x;w++){var g=h[w],m=g[0];if(this.inc_slots[a][m]!==undefined){l=Math.max(l,this.inc_slots[a][m]);B.push(this.inc_slots[a][m])}else{z.push(w)}}for(var w=0,x=z.length;w<x;w++
){var g=h[z[w]],m=g[0],s=g[1],d=g[2],q=g[3],e=Math.floor((s-o)*n),f=Math.ceil((d-o)*n);if(q!==undefined&&!c){var t=b.measureText(q).width;if(e-t<0){f+=t}else{e-=t}}var v=0;while(true){var p=true;if(this.s_e_by_tile[a][v]!==undefined){for(var u=0,A=this.s_e_by_tile[a][v].length;u<A;u++){var y=this.s_e_by_tile[a][v][u];if(f>y[0]&&e<y[1]){p=false;break}}}if(p){if(this.s_e_by_tile[a][v]===undefined){this.s_e_by_tile[a][v]=[]}this.s_e_by_tile[a][v].push([e,f]);this.inc_slots[a][m]=v;l=Math.max(l,v);break}v++}}return l},rect_or_text:function(n,o,f,m,b,d,k,e,h){n.textAlign="center";var j=Math.round(o/2);if((this.mode==="Pack"||this.mode==="Auto")&&d!==undefined&&o>PX_PER_CHAR){n.fillStyle=this.prefs.block_color;n.fillRect(k,h+1,e,9);n.fillStyle="#eee";for(var g=0,l=d.length;g<l;g++){if(b+g>=f&&b+g<=m){var a=Math.floor(Math.max(0,(b+g-f)*o));n.fillText(d[g],a+this.left_offset+j,h+9)}}}else{n.fillStyle=this.prefs.block_color;n.fillRect(k,h+4,e,3)}},draw_tile:function(X,h,n,ak){var E=
h*DENSITY*X,ad=(h+1)*DENSITY*X,D=DENSITY*X;var ae=E+"_"+ad;var z=this.data_cache.get(ae);if(z===undefined){this.data_queue[[E,ad]]=true;this.get_data(E,ad);return}var a=Math.ceil(D*ak),L=$("<canvas class='tile'></canvas>"),Z=this.prefs.label_color,f=this.prefs.block_color,m=this.mode,V=(m==="Squish")||(m==="Dense")&&(m!=="Pack")||(m==="Auto"&&(z.extra_info==="no_detail")),P=this.left_offset,aj,s,al;if(z.dataset_type==="summary_tree"){s=30}else{if(m==="Dense"){s=15;al=10}else{al=(V?this.vertical_nodetail_px:this.vertical_detail_px);s=this.incremental_slots(this.view.zoom_res,z.data,V,m)*al+15;aj=this.inc_slots[this.view.zoom_res]}}L.css({position:"absolute",top:0,left:(E-this.view.low)*ak-P});L.get(0).width=a+P;L.get(0).height=s;n.parent().css("height",Math.max(this.height_px,s)+"px");var A=L.get(0).getContext("2d");A.fillStyle=f;A.font=this.default_font;A.textAlign="right";if(z.dataset_type=="summary_tree"){var K,H=55,ac=255-H,g=ac*2/3,R=z.data,C=z.max,l=z.avg;if(R.length>2)
{var b=Math.ceil((R[1][0]-R[0][0])*ak)}else{var b=50}for(var ag=0,w=R.length;ag<w;ag++){var T=Math.ceil((R[ag][0]-E)*ak);var S=R[ag][1];if(!S){continue}K=Math.floor(ac-(S/C)*ac);A.fillStyle="rgb("+K+","+K+","+K+")";A.fillRect(T+P,0,b,20);if(this.prefs.show_counts){if(K>g){A.fillStyle="black"}else{A.fillStyle="#ddd"}A.textAlign="center";A.fillText(R[ag][1],T+P+(b/2),12)}}n.append(L);return L}var ai=z.data;var af=0;for(var ag=0,w=ai.length;ag<w;ag++){var M=ai[ag],J=M[0],ah=M[1],U=M[2],F=M[3];if(ah<=ad&&U>=E){var W=Math.floor(Math.max(0,(ah-E)*ak)),B=Math.ceil(Math.min(a,Math.max(0,(U-E)*ak))),Q=(m==="Dense"?0:aj[J]*al);if(z.dataset_type==="bai"){A.fillStyle=f;if(M[4] instanceof Array){var t=Math.floor(Math.max(0,(M[4][0]-E)*ak)),I=Math.ceil(Math.min(a,Math.max(0,(M[4][1]-E)*ak))),r=Math.floor(Math.max(0,(M[5][0]-E)*ak)),p=Math.ceil(Math.min(a,Math.max(0,(M[5][1]-E)*ak)));if(M[4][1]>=E&&M[4][0]<=ad){this.rect_or_text(A,ak,E,ad,M[4][0],M[4][2],t+P,I-t,Q)}if(M[5][1]>=E&&M[5][0]<=
ad){this.rect_or_text(A,ak,E,ad,M[5][0],M[5][2],r+P,p-r,Q)}if(r>I){A.fillStyle="#999";A.fillRect(I+P,Q+5,r-I,1)}}else{A.fillStyle=f;this.rect_or_text(A,ak,E,ad,ah,F,W+P,B-W,Q)}if(m!=="Dense"&&!V&&ah>E){A.fillStyle=this.prefs.label_color;if(h===0&&W-A.measureText(F).width<0){A.textAlign="left";A.fillText(J,B+2+P,Q+8)}else{A.textAlign="right";A.fillText(J,W-2+P,Q+8)}A.fillStyle=f}}else{if(z.dataset_type==="interval_index"){if(V){A.fillRect(W+P,Q+5,B-W,1)}else{var v=M[4],O=M[5],Y=M[6],e=M[7];var u,aa,G=null,am=null;if(O&&Y){G=Math.floor(Math.max(0,(O-E)*ak));am=Math.ceil(Math.min(a,Math.max(0,(Y-E)*ak)))}if(m!=="Dense"&&F!==undefined&&ah>E){A.fillStyle=Z;if(h===0&&W-A.measureText(F).width<0){A.textAlign="left";A.fillText(F,B+2+P,Q+8)}else{A.textAlign="right";A.fillText(F,W-2+P,Q+8)}A.fillStyle=f}if(e){if(v){if(v=="+"){A.fillStyle=RIGHT_STRAND}else{if(v=="-"){A.fillStyle=LEFT_STRAND}}A.fillRect(W+P,Q,B-W,10);A.fillStyle=f}for(var ae=0,d=e.length;ae<d;ae++){var o=e[ae],c=Math.flo
or(Math.max(0,(o[0]-E)*ak)),N=Math.ceil(Math.min(a,Math.max((o[1]-E)*ak)));if(c>N){continue}u=5;aa=3;A.fillRect(c+P,Q+aa,N-c,u);if(G!==undefined&&!(c>am||N<G)){u=9;aa=1;var ab=Math.max(c,G),q=Math.min(N,am);A.fillRect(ab+P,Q+aa,q-ab,u)}}}else{u=9;aa=1;A.fillRect(W+P,Q+aa,B-W,u);if(M.strand){if(M.strand=="+"){A.fillStyle=RIGHT_STRAND_INV}else{if(M.strand=="-"){A.fillStyle=LEFT_STRAND_INV}}A.fillRect(W+P,Q,B-W,10);A.fillStyle=prefs.block_color}}}}}af++}}n.append(L);return L},gen_options:function(j){var a=$("<div />").addClass("form-row");var e="track_"+j+"_block_color",l=$("<label />").attr("for",e).text("Block color:"),m=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),k="track_"+j+"_label_color",g=$("<label />").attr("for",k).text("Text color:"),h=$("<input />").attr("id",k).attr("name",k).val(this.prefs.label_color),f="track_"+j+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></in
put>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(l).append(m).append(g).append(h).append(d)},update_options:function(e){var b=$("#track_"+e+"_block_color").val(),d=$("#track_"+e+"_label_color").val(),c=$("#track_"+e+"_mode option:selected").val(),a=$("#track_"+e+"_show_count").attr("checked");if(b!==this.prefs.block_color||d!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=d;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,c){FeatureTrack.call(this,d,b,a,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
+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"),PX_PER_CHAR=CONTEXT.measureText("A").width,RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/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=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONT
EXT.createPattern(right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src=image_path+"/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};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 View=function(a,c,e,d,b){this.container=a;this.vis_id=d;this.dbkey=b;this.title=e;this.chrom=c;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(
View.prototype,{init:function(){var b=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(b);this.content_div=$("<div/>").addClass("content").css("position","relative").appendTo(b);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide().appendTo(b);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);this.viewport=$("<div/>").addClass("viewport").appendTo(this.viewport_container);this.nav_container=$("<div/>").addClass("nav-container").appendTo(b);this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.nav_container);this.nav=$("<div/>").addClass("nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.nav);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overvi
ew_viewport);this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_form=$("<form/>").attr("action",function(){void (0)}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);this.low_input=$("<input/>").addClass("low").css("width","10em").appendTo(this.chrom_form);$("<span/>").text(" - ").appendTo(this.chrom_form);this.high_input=$("<input/>").addClass("high").css("width","10em").appendTo(this.chrom_form);if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.chrom_form)}this.zi_link=$("<a/>").click(function(){a.zoom_in();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom.png" />').appendTo(this.chrom_form);this.zo_link=$("<a/>").click(function(){a.zoom_out();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom-out.pn
g" />').appendTo(this.chrom_form);$.ajax({url:chrom_url,data:(this.vis_id!==undefined?{vis_id:this.vis_id}:{dbkey:this.dbkey}),dataType:"json",success:function(c){if(c.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=c.chrom_info;var f='<option value="">Select Chrom/Contig</option>';for(i in a.chrom_data){var e=a.chrom_data[i]["chrom"];f+='<option value="'+e+'">'+e+"</option>"}a.chrom_select.html(f);var d=function(){if(a.chrom_select.val()===""){a.intro_div.show();a.content_div.hide()}else{a.intro_div.hide();a.content_div.show()}a.chrom=a.chrom_select.val();var h=$.grep(a.chrom_data,function(k,l){return k.chrom===a.chrom})[0];a.max_high=(h!==undefined?h.len:0);a.reset();a.redraw(true);for(var j in a.tracks){var g=a.tracks[j];if(g.init){g.init()}}a.redraw()};a.chrom_select.bind("change",d);d()},error:function(){alert("Could not load chroms for this dbkey:",a.dbkey)}});this.content_div.bind("dblclick",function(c){a.zoom_in(c.pageX,this.viewport_container)});thi
s.overview_box.bind("dragstart",function(c){this.current_x=c.offsetX}).bind("drag",function(c){var f=c.offsetX-this.current_x;this.current_x=c.offsetX;var d=Math.round(f/a.viewport_container.width()*(a.max_high-a.max_low));a.move_delta(-d)});this.viewport_container.bind("dragstart",function(c){this.original_low=a.low;this.current_height=c.clientY;this.current_x=c.offsetX}).bind("drag",function(f){var c=$(this);var h=f.offsetX-this.current_x;var d=c.scrollTop()-(f.clientY-this.current_height);c.scrollTop(d);this.current_height=f.clientY;this.current_x=f.offsetX;var g=Math.round(h/a.viewport_container.width()*(a.high-a.low));a.move_delta(g)});this.top_labeltrack.bind("dragstart",function(c){this.drag_origin_x=c.clientX;this.drag_origin_pos=c.clientX/a.viewport_container.width()*(a.high-a.low)+a.low;this.drag_div=$("<div />").css({height:a.viewport_container.height(),top:"0px",position:"absolute","background-color":"#cfc",border:"1px solid #6a6",opacity:0.5,"z-index":1000}).app
endTo($(this))}).bind("drag",function(h){var d=Math.min(h.clientX,this.drag_origin_x)-a.container.offset().left,c=Math.max(h.clientX,this.drag_origin_x)-a.container.offset().left,g=(a.high-a.low),f=a.viewport_container.width();a.low_input.val(commatize(Math.round(d/f*g)+a.low));a.high_input.val(commatize(Math.round(c/f*g)+a.low));this.drag_div.css({left:d+"px",width:(c-d)+"px"})}).bind("dragend",function(j){var d=Math.min(j.clientX,this.drag_origin_x),c=Math.max(j.clientX,this.drag_origin_x),g=(a.high-a.low),f=a.viewport_container.width(),h=a.low;a.low=Math.round(d/f*g)+h;a.high=Math.round(c/f*g)+h;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(this,this.top_labeltrack));this.add_label_track(new LabelTrack(this,this.nav_labeltrack))},move_delta:function(c){var a=this;var b=a.high-a.low;if(a.low-c<a.max_low){a.low=a.max_low;a.high=a.max_low+b}else{if(a.high-c>a.max_high){a.high=a.max_high;a.low=a.max_high-b}else{a.high-=c;a.low-=c}}a.redraw()},add_tra
ck: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;this.num_tracks+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){this.has_changes=true;a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[this.tracks.indexOf(a)];this.num_tracks-=1},update_options:function(){this.has_changes=true;var b=$("ul#sortable-ul").sortable("toArray");for(var c in b){var e=b[c].split("_li")[0].split("track_")[1];this.viewport.append($("#track_"+e))}for(var d in view.tracks){var a=view.tracks[d];if(a&&a.update_options){a.update_options(d)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},redraw:function(f){var d=this.high-this.low,b=this.low,e=this.high;if(b<this.max_low){b=this.max_low}if(e>this.max_high){e=this.max_high}if(this.high!==0&&
d<this.min_separation){e=b+this.min_separation}this.low=Math.floor(b);this.high=Math.ceil(e);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))));this.overview_box.css({left:(this.low/(this.max_high-this.max_low))*this.overview_viewport.width(),width:Math.max(12,(this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())}).show();this.low_input.val(commatize(this.low));this.high_input.val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c]&&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(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(
this.high-this.low)+this.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);this.high=Math.round(c+a);this.redraw()}});var Track=function(b,a,c){this.name=b;this.parent_element=c;this.view=a;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 />").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","auto");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"||d.kind==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR);if(d.message){var f=a.view.tracks.indexOf(a);var e=$("<a href='javascript:void(0);'></a>").attr("id",f+"_error");e.text("Click to view error");$("#"+f+"_error").live("click",function(){show_modal("Trackster Error","<pre>"+d.message+"</pre>",{Close:hide_modal})});a.content_div.append(e)}}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NOCONVERTER)}else{if(d.data!==undefined&&(d.data===null||d.data.length===0)){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(){this.left_offset=200};$.extend(TiledT
rack.prototype,Track.prototype,{draw:function(){var j=this.view.low,e=this.view.high,f=e-j,d=this.view.resolution;var l=$("<div style='position: relative;'></div>"),m=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(l),this.max_height=0;var a=Math.floor(j/d/DENSITY);while((a*DENSITY*d)<e){var k=this.content_div.width()+"_"+m+"_"+a;var c=this.tile_cache.get(k);if(c){var g=a*DENSITY*d;var b=(g-j)*m;if(this.left_offset){b-=this.left_offset}c.css({left:b});l.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,k,j,e,a,d,l,m)}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=functio
n(a,b){Track.call(this,null,a,b);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 ReferenceTrack=function(a){this.track_type="ReferenceTrack";Track.call(this,null,a,a.top_labeltrack);TiledTrack.call(this);this.hidden=true;this.height_px=12;this.container_div.addClass("reference-track");this.dummy_canvas=$("<canvas></canvas>").get(0).getContext("2d");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend
(ReferenceTrack.prototype,TiledTrack.prototype,{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:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,e=$("<canvas class='tile'></canvas>"),n=e.get(0).getContext("2d"),j=f+"_"+b;if(o>PX_PER_CHAR){if(this.data_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-this.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o);n.fillText(m[h],a+this.left_offset,10)}k.append(e);return e}this.content_div.css("he
ight","0px")}});var LineTrack=function(d,b,a,c){this.track_type="LineTrack";Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=100;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(c.min_value!==undefined){this.prefs.min_value=c.min_value}if(c.max_value!==undefined){this.prefs.max_value=c.max_value}if(c.mode!==undefined){this.prefs.mode=c.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){a.container_div.addClass("line-track");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").va
l(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 />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var d=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px",left:"10px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px",left:"10px"});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,j){console.log(h,g,j)}})}},draw_tile:function(p,r,c,e
){if(this.vertical_range===undefined){return}var s=r*DENSITY*p,a=DENSITY*p,b=$("<canvas class='tile'></canvas>"),v=p+"_"+r;if(this.data_cache.get(v)===undefined){this.get_data(p,r);return}var j=this.data_cache.get(v);if(j===null){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e+this.left_offset);b.get(0).height=this.height_px;var o=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,n=this.vertical_range,t=this.total_frequency,d=this.height_px,m=this.prefs.mode;o.beginPath();if(data.length>1){var f=Math.ceil((data[1][0]-data[0][0])*e)}else{var f=10}var u,h;for(var q=0;q<data.length;q++){u=(data[q][0]-s)*e;h=data[q][1];if(m=="Intensity"){if(h===null){continue}if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/n*255);o.fillStyle="rgb("+h+","+h+","+h+")";o.fillRect(u,0,f,this.height_px)}else{if(h===null){if(k&&m==="Filled"){o.lineTo(u,d)}k=false;continue}else{if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.ro
und(d-(h-l)/n*d);if(k){o.lineTo(u,h)}else{k=true;if(m==="Filled"){o.moveTo(u,d);o.lineTo(u,h)}else{o.moveTo(u,h)}}}}}if(m==="Filled"){if(k){o.lineTo(u,d)}o.fill()}else{o.stroke()}c.append(b);return b},gen_options:function(o){var a=$("<div />").addClass("form-row");var h="track_"+o+"_minval",m=$("<label></label>").attr("for",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),n=$("<input></input>").attr("id",h).val(b),l="track_"+o+"_maxval",g=$("<label></label>").attr("for",l).text("Max value:"),k=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",l).val(k),e="track_"+o+"_mode",d=$("<label></label>").attr("for",e).text("Display mode:"),j=(this.prefs.mode===undefined?"Line":this.prefs.mode),c=$('<select id="'+e+'"><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>');c.children("#mo
de_"+j).attr("selected","selected");return a.append(m).append(n).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(d,b,a,c){this.track_type="FeatureTrack";Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=0;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.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(c.block_color!==undefined){this.prefs.block_color=c.block_color}if(c.label_color!==undefined){this.prefs.label_color=c.label_color}if(c.show_counts!==undefined){this.prefs.show_counts=c.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;a.mode="Auto";if(a.mode_div){a.mode_div.remove()}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(d){a.mode_div=$("<div class='right-float menubutton popup' />").text("Display Mode");a.header_div.append(a.mode_div);a.mode="Auto";var c=function(e){a.mode_div.text(e);a.mode=e;a.tile_cache.clear();a.draw()};make_popupmenu(a.mode_div,{Au
to:function(){c("Auto")},Dense:function(){c("Dense")},Squish:function(){c("Squish")},Pack:function(){c("Pack")}});a.data_cache.set(b,d);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,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,h,c,r){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.inc_slots[a].mode=r;this.s_e_by_tile[a]={}}var n=this.inc_slots[a].w_scale,z=[],l=0,b=$("<canvas></canvas>").get(0).getContext("2d"),o=this.view.max_low;var B=[];if(this.inc_slots[a].mode!==r){delete this.inc_slots[a];this.inc_slots[a]={mode:r,w_scale:n};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var w=0,x=h.length;w<x;w++){var g=h[w],m=g[0];if(this.inc_slots[a][m]!==undefined){l=Math.max(l,this.inc_slots[a][m]);B.push(this.inc_slo
ts[a][m])}else{z.push(w)}}for(var w=0,x=z.length;w<x;w++){var g=h[z[w]],m=g[0],s=g[1],d=g[2],q=g[3],e=Math.floor((s-o)*n),f=Math.ceil((d-o)*n);if(q!==undefined&&!c){var t=b.measureText(q).width;if(e-t<0){f+=t}else{e-=t}}var v=0;while(true){var p=true;if(this.s_e_by_tile[a][v]!==undefined){for(var u=0,A=this.s_e_by_tile[a][v].length;u<A;u++){var y=this.s_e_by_tile[a][v][u];if(f>y[0]&&e<y[1]){p=false;break}}}if(p){if(this.s_e_by_tile[a][v]===undefined){this.s_e_by_tile[a][v]=[]}this.s_e_by_tile[a][v].push([e,f]);this.inc_slots[a][m]=v;l=Math.max(l,v);break}v++}}return l},rect_or_text:function(n,o,f,m,b,d,k,e,h){n.textAlign="center";var j=Math.round(o/2);if((this.mode==="Pack"||this.mode==="Auto")&&d!==undefined&&o>PX_PER_CHAR){n.fillStyle=this.prefs.block_color;n.fillRect(k,h+1,e,9);n.fillStyle="#eee";for(var g=0,l=d.length;g<l;g++){if(b+g>=f&&b+g<=m){var a=Math.floor(Math.max(0,(b+g-f)*o));n.fillText(d[g],a+this.left_offset+j,h+9)}}}else{n.fillStyle=this.prefs.block_color;n.f
illRect(k,h+4,e,3)}},draw_tile:function(X,h,n,ak){var E=h*DENSITY*X,ad=(h+1)*DENSITY*X,D=DENSITY*X;var ae=E+"_"+ad;var z=this.data_cache.get(ae);if(z===undefined){this.data_queue[[E,ad]]=true;this.get_data(E,ad);return}var a=Math.ceil(D*ak),L=$("<canvas class='tile'></canvas>"),Z=this.prefs.label_color,f=this.prefs.block_color,m=this.mode,V=(m==="Squish")||(m==="Dense")&&(m!=="Pack")||(m==="Auto"&&(z.extra_info==="no_detail")),P=this.left_offset,aj,s,al;if(z.dataset_type==="summary_tree"){s=30}else{if(m==="Dense"){s=15;al=10}else{al=(V?this.vertical_nodetail_px:this.vertical_detail_px);s=this.incremental_slots(this.view.zoom_res,z.data,V,m)*al+15;aj=this.inc_slots[this.view.zoom_res]}}L.css({position:"absolute",top:0,left:(E-this.view.low)*ak-P});L.get(0).width=a+P;L.get(0).height=s;n.parent().css("height",Math.max(this.height_px,s)+"px");var A=L.get(0).getContext("2d");A.fillStyle=f;A.font=this.default_font;A.textAlign="right";if(z.dataset_type=="summary_tree"){var K,H=55,a
c=255-H,g=ac*2/3,R=z.data,C=z.max,l=z.avg;if(R.length>2){var b=Math.ceil((R[1][0]-R[0][0])*ak)}else{var b=50}for(var ag=0,w=R.length;ag<w;ag++){var T=Math.ceil((R[ag][0]-E)*ak);var S=R[ag][1];if(!S){continue}K=Math.floor(ac-(S/C)*ac);A.fillStyle="rgb("+K+","+K+","+K+")";A.fillRect(T+P,0,b,20);if(this.prefs.show_counts){if(K>g){A.fillStyle="black"}else{A.fillStyle="#ddd"}A.textAlign="center";A.fillText(R[ag][1],T+P+(b/2),12)}}n.append(L);return L}var ai=z.data;var af=0;for(var ag=0,w=ai.length;ag<w;ag++){var M=ai[ag],J=M[0],ah=M[1],U=M[2],F=M[3];if(ah<=ad&&U>=E){var W=Math.floor(Math.max(0,(ah-E)*ak)),B=Math.ceil(Math.min(a,Math.max(0,(U-E)*ak))),Q=(m==="Dense"?0:aj[J]*al);if(z.dataset_type==="bai"){A.fillStyle=f;if(M[4] instanceof Array){var t=Math.floor(Math.max(0,(M[4][0]-E)*ak)),I=Math.ceil(Math.min(a,Math.max(0,(M[4][1]-E)*ak))),r=Math.floor(Math.max(0,(M[5][0]-E)*ak)),p=Math.ceil(Math.min(a,Math.max(0,(M[5][1]-E)*ak)));if(M[4][1]>=E&&M[4][0]<=ad){this.rect_or_text(A,ak,
E,ad,M[4][0],M[4][2],t+P,I-t,Q)}if(M[5][1]>=E&&M[5][0]<=ad){this.rect_or_text(A,ak,E,ad,M[5][0],M[5][2],r+P,p-r,Q)}if(r>I){A.fillStyle="#999";A.fillRect(I+P,Q+5,r-I,1)}}else{A.fillStyle=f;this.rect_or_text(A,ak,E,ad,ah,F,W+P,B-W,Q)}if(m!=="Dense"&&!V&&ah>E){A.fillStyle=this.prefs.label_color;if(h===0&&W-A.measureText(F).width<0){A.textAlign="left";A.fillText(J,B+2+P,Q+8)}else{A.textAlign="right";A.fillText(J,W-2+P,Q+8)}A.fillStyle=f}}else{if(z.dataset_type==="interval_index"){if(V){A.fillRect(W+P,Q+5,B-W,1)}else{var v=M[4],O=M[5],Y=M[6],e=M[7];var u,aa,G=null,am=null;if(O&&Y){G=Math.floor(Math.max(0,(O-E)*ak));am=Math.ceil(Math.min(a,Math.max(0,(Y-E)*ak)))}if(m!=="Dense"&&F!==undefined&&ah>E){A.fillStyle=Z;if(h===0&&W-A.measureText(F).width<0){A.textAlign="left";A.fillText(F,B+2+P,Q+8)}else{A.textAlign="right";A.fillText(F,W-2+P,Q+8)}A.fillStyle=f}if(e){if(v){if(v=="+"){A.fillStyle=RIGHT_STRAND}else{if(v=="-"){A.fillStyle=LEFT_STRAND}}A.fillRect(W+P,Q,B-W,10);A.fillStyle=f}f
or(var ae=0,d=e.length;ae<d;ae++){var o=e[ae],c=Math.floor(Math.max(0,(o[0]-E)*ak)),N=Math.ceil(Math.min(a,Math.max((o[1]-E)*ak)));if(c>N){continue}u=5;aa=3;A.fillRect(c+P,Q+aa,N-c,u);if(G!==undefined&&!(c>am||N<G)){u=9;aa=1;var ab=Math.max(c,G),q=Math.min(N,am);A.fillRect(ab+P,Q+aa,q-ab,u)}}}else{u=9;aa=1;A.fillRect(W+P,Q+aa,B-W,u);if(M.strand){if(M.strand=="+"){A.fillStyle=RIGHT_STRAND_INV}else{if(M.strand=="-"){A.fillStyle=LEFT_STRAND_INV}}A.fillRect(W+P,Q,B-W,10);A.fillStyle=prefs.block_color}}}}}af++}}n.append(L);return L},gen_options:function(j){var a=$("<div />").addClass("form-row");var e="track_"+j+"_block_color",l=$("<label />").attr("for",e).text("Block color:"),m=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),k="track_"+j+"_label_color",g=$("<label />").attr("for",k).text("Text color:"),h=$("<input />").attr("id",k).attr("name",k).val(this.prefs.label_color),f="track_"+j+"_show_count",c=$("<label />").attr("for",f).text("Show summary count
s"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(l).append(m).append(g).append(h).append(d)},update_options:function(e){var b=$("#track_"+e+"_block_color").val(),d=$("#track_"+e+"_label_color").val(),c=$("#track_"+e+"_mode option:selected").val(),a=$("#track_"+e+"_show_count").attr("checked");if(b!==this.prefs.block_color||d!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=d;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,c){FeatureTrack.call(this,d,b,a,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -79,6 +79,7 @@ var View = function( container, chrom, t
this.label_tracks = [];
this.max_low = 0;
this.max_high = 0;
+ this.num_tracks = 0;
this.track_id_counter = 0;
this.zoom_factor = 3;
this.min_separation = 30;
@@ -267,6 +268,7 @@ var View = function( container, chrom, t
if (track.init) { track.init(); }
track.container_div.attr('id', 'track_' + track.track_id);
this.track_id_counter += 1;
+ this.num_tracks += 1;
},
add_label_track: function (label_track) {
label_track.view = this;
@@ -276,6 +278,7 @@ var View = function( container, chrom, t
this.has_changes = true;
track.container_div.fadeOut('slow', function() { $(this).remove(); });
delete this.tracks[this.tracks.indexOf(track)];
+ this.num_tracks -= 1;
},
update_options: function() {
this.has_changes = true;
@@ -570,7 +573,7 @@ var ReferenceTrack = function (view) {
canvas.css( {
position: "absolute",
top: 0,
- left: ( tile_low - this.view.low ) * w_scale + this.left_offset
+ left: ( tile_low - this.view.low ) * w_scale - this.left_offset
});
for (var c = 0, str_len = seq.length; c < str_len; c++) {
--- a/static/scripts/galaxy.base.js
+++ b/static/scripts/galaxy.base.js
@@ -553,6 +553,34 @@ GalaxyAsync.prototype.log_user_action =
});
};
+// Add to trackster browser functionality
+$(".trackster-add").live("click", function() {
+ var dataset = this,
+ dataset_jquery = $(this);
+ $.ajax({
+ url: dataset_jquery.attr("data-url"),
+ data: { "f-dbkey": "hi" },
+ dataType: "html",
+ error: function() { alert( "Could not add this dataset to browser." ); },
+ success: function(table_html) {
+ var parent = window.parent;
+ parent.show_modal("Add to Browser:", table_html, {
+ "Insert Dataset Into": function() {
+ $(parent.document).find('input[name=id]:checked').each(function() {
+ var vis_id = $(this).val();
+ parent.location = dataset_jquery.attr("action-url") + "&id=" + vis_id;
+ });
+ parent.hide_modal();
+ },
+ "Cancel": function() {
+ parent.hide_modal();
+ }
+ });
+ }
+ });
+});
+
+
$(document).ready( function() {
// Links with confirmation
$( "a[confirm]" ).click( function() {
--- a/templates/tracks/new_browser.mako
+++ b/templates/tracks/new_browser.mako
@@ -17,4 +17,8 @@
</div><div style="clear: both;"></div></div>
+ <div class="form-row">
+ Is your build not listed here?
+ <a href="${h.url_for( controller='user', action='dbkeys', panels=True )}">Add a Custom Build</a>
+ </div></form>
--- a/templates/tracks/browser.mako
+++ b/templates/tracks/browser.mako
@@ -56,6 +56,9 @@
</div><form action="#" onsubmit="view.update_options();return false;"><div id="show-hide-move">
+ <div id="no-tracks" class="warningmessage" style="margin: 5px; display:none;">
+ There are currently no tracks in this browser. Add tracks via the button above.
+ </div><ul id="sortable-ul"></ul></div><input type="submit" id="refresh-button" value="Refresh" style="display:none" />
@@ -123,6 +126,14 @@
// Execute initializer for EDITOR specific javascript
function init() {
+ %if add_dataset:
+ console.log("Adding dataset");
+ ## Code for adding new dataset
+ %endif
+
+ if (view.num_tracks === 0) {
+ $("#no-tracks").show();
+ }
$("#title").text(view.title + " (" + view.dbkey + ")");
$("ul#sortable-ul").sortable({
update: function(event, ui) {
@@ -161,6 +172,7 @@
view.add_track(new_track);
view.has_changes = true;
+ $("#no-tracks").hide();
sidebar_box(new_track);
}
});
@@ -231,6 +243,9 @@
del_icon.bind("click", function() {
$("#track_" + track_id + "_li").fadeOut('slow', function() { $("#track_" + track_id + "_li").remove(); });
view.remove_track(track);
+ if (view.num_tracks === 0) {
+ $("#no-tracks").show();
+ }
});
icon_div.append(edit_icon).append(del_icon);
title.append(label).prepend(icon_div);
--- a/templates/root/history_common.mako
+++ b/templates/root/history_common.mako
@@ -119,7 +119,8 @@
%if for_editing:
<a href="${h.url_for( controller='tool_runner', action='rerun', id=data.id )}" target="galaxy_main" title="Run this job again" class="icon-button arrow-circle tooltip"></a>
%if app.config.get_bool( 'enable_tracks', False ) and data.ext in app.datatypes_registry.get_available_tracks():
- <a class="icon-button vis-chart tooltip trackster" title="Visualize in Trackster" id="visualize_${hid}"></a>
+ <a data-url="${h.url_for( controller='tracks', action='list_tracks' )}" class="icon-button vis-chart tooltip trackster-add"
+ action-url="${h.url_for( controller='tracks', action='browser', dataset_id=dataset_id)}" title="Visualize in Trackster"></a>
%endif
%if trans.user:
<div style="float: right">
1
0
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1280415308 14400
# Node ID d27912281a74ba74a8df6f5d72db7c553ca846c5
# Parent af48a13e46b9ab0136bcbf14508bd9bb044ad257
lims:
- added a new table 'sample_dataset' to store sample datasets & their info when they are transfered from the sequencer
- the datasets transfer page now uses a grid to facilitate bulk renaming
- bulk renaming possible to fix the problem with the way SOLiD generates datasets
- the remote file browser is now independent of a specific sample, the user may select any sample when transferring datasets from the sequencer
--- a/templates/requests/common/sample_datasets.mako
+++ b/templates/requests/common/sample_datasets.mako
@@ -1,5 +1,5 @@
<%def name="render_sample_datasets( cntrller, sample )">
- <a href="${h.url_for(controller='requests_common', cntrller=cntrller, action='show_datatx_page', sample_id=trans.security.encode_id(sample.id))}">${sample.transferred_dataset_files()}/${len(sample.dataset_files)}</a>
+ <a href="${h.url_for(controller='requests_common', cntrller=cntrller, action='show_datatx_page', sample_id=trans.security.encode_id(sample.id))}">${sample.transferred_dataset_files()}/${len(sample.datasets)}</a></%def>
--- /dev/null
+++ b/templates/admin/requests/get_data.mako
@@ -0,0 +1,144 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+
+<script type="text/javascript">
+$(document).ready(function(){
+ //hide the all of the element with class msg_body
+ $(".msg_body").hide();
+ //toggle the componenet with class msg_body
+ $(".msg_head").click(function(){
+ $(this).next(".msg_body").slideToggle(450);
+ });
+});
+
+
+
+
+</script>
+
+<script type="text/javascript">
+ function display_file_details(request_id, folder_path)
+ {
+ var w = document.get_data.files_list.selectedIndex;
+ var selected_value = document.get_data.files_list.options[w].value;
+ var cell = $("#file_details");
+ if(selected_value.charAt(selected_value.length-1) != '/')
+ {
+ // Make ajax call
+ $.ajax( {
+ type: "POST",
+ url: "${h.url_for( controller='requests_admin', action='get_file_details' )}",
+ dataType: "json",
+ data: { id: request_id, folder_path: document.get_data.folder_path.value+selected_value },
+ success : function ( data ) {
+ cell.html( '<label>'+data+'</label>' )
+ }
+ });
+ }
+ else
+ {
+ cell.html( '' )
+ }
+
+
+ }
+</script>
+
+<script type="text/javascript">
+ function open_folder1(request_id, folder_path)
+ {
+ var w = document.get_data.files_list.selectedIndex;
+ var selected_value = document.get_data.files_list.options[w].value;
+ var cell = $("#file_details");
+ if(selected_value.charAt(selected_value.length-1) == '/')
+ {
+ document.get_data.folder_path.value = document.get_data.folder_path.value+selected_value
+ // Make ajax call
+ $.ajax( {
+ type: "POST",
+ url: "${h.url_for( controller='requests_admin', action='open_folder' )}",
+ dataType: "json",
+ data: { id: request_id, folder_path: document.get_data.folder_path.value },
+ success : function ( data ) {
+ document.get_data.files_list.options.length = 0
+ for(i=0; i<data.length; i++)
+ {
+ var newOpt = new Option(data[i], data[i]);
+ document.get_data.files_list.options[i] = newOpt;
+ }
+ //cell.html( '<label>'+data+'</label>' )
+
+ }
+ });
+ }
+ else
+ {
+ cell.html( '' )
+ }
+ }
+</script>
+
+
+<style type="text/css">
+.msg_head {
+ padding: 0px 0px;
+ cursor: pointer;
+}
+
+}
+</style>
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+<br/>
+<br/>
+<ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_request_types', operation='view', id=trans.security.encode_id(request.type.id) )}">
+ <span>Sequencer information</span></a>
+ </li>
+ <li>
+ <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='show', id=trans.security.encode_id(request.id) )}">
+ <span>Browse this request</span></a>
+ </li>
+</ul>
+
+<form name="get_data" id="get_data" action="${h.url_for( controller='requests_admin', cntrller=cntrller, action='get_data', request_id=request.id)}" method="post" >
+ <div class="toolFormTitle">Select files for transfer</div>
+ <div class="toolForm">
+ <div class="form-row">
+ <label>Sample:</label>
+ ${samples_selectbox.get_html()}
+ <div class="toolParamHelp" style="clear: both;">
+ Select the sample with which you want to associate the dataset(s)
+ </div>
+ <br/>
+ <label>Folder path on the sequencer:</label>
+ <input type="text" name="folder_path" value="${folder_path}" size="100"/>
+ <input type="submit" name="browse_button" value="List contents"/>
+ ##<input type="submit" name="open_folder" value="Open folder"/>
+ <input type="submit" name="folder_up" value="Up"/>
+ </div>
+ <div class="form-row">
+ <select name="files_list" id="files_list" style="max-width: 60%; width: 98%; height: 150px; font-size: 100%;" ondblclick="open_folder1(${request.id}, '${folder_path}')" onChange="display_file_details(${request.id}, '${folder_path}')" multiple>
+ %for index, f in enumerate(files):
+ <option value="${f}">${f}</option>
+ %endfor
+ </select>
+ <br/>
+ <div id="file_details" class="toolParamHelp" style="clear: both;">
+
+ </div>
+ </div>
+ <div class="form-row">
+<!-- <div class="toolParamHelp" style="clear: both;">
+ After selecting dataset(s), be sure to click on the <b>Start transfer</b> button.
+ Once the transfer is complete the dataset(s) will show up on this page.
+ </div>-->
+ <input type="submit" name="select_show_datasets_button" value="Select & show datasets"/>
+ <input type="submit" name="select_more_button" value="Select more"/>
+ </div>
+ </div>
+</form>
--- /dev/null
+++ b/templates/admin/requests/rename_datasets.mako
@@ -0,0 +1,66 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+<h3>Rename datasets for Sample "${sample.name}"</h3>
+<br/>
+${render_msg('A dataset can be renamed only if its status is "Not Started"', 'ok')}
+
+<ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_datasets', sample_id=sample.id )}">
+ <span>Browse datasets</span></a>
+ </li>
+ <li>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='show', id=trans.security.encode_id(sample.request.id) )}">
+ <span>Browse this request</span></a>
+ </li>
+</ul>
+
+<form name="rename_datasets" id="rename_datasets" action="${h.url_for( controller='requests_admin', action='rename_datasets', id_list=id_list, sample_id=trans.security.encode_id(sample.id))}" method="post" >
+ <table class="grid">
+ <thead>
+ <tr>
+ <th>Prepend directory name</th>
+ <th>Name</th>
+ <th>Path on Sequencer</th>
+ </tr>
+ <thead>
+ <tbody>
+ %for id in id_list:
+ <%
+ sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(id) )
+ import os
+ id = trans.security.decode_id(id)
+ %>
+ %if sample_dataset.status == trans.app.model.Sample.transfer_status.NOT_STARTED:
+ <tr>
+ <td>
+ <select name="prepend_${sample_dataset.id}" last_selected_value="2">
+ <option value="None" selected></option>
+ %for option_index, option in enumerate(sample_dataset.file_path.split(os.sep)[:-1]):
+ %if option.strip():
+ <option value="${option}">${option}</option>
+ %endif
+ %endfor
+ </select>
+ </td>
+ <td>
+ <input type="text" name="name_${sample_dataset.id}" value="${sample_dataset.name}" size="100"/>
+ </td>
+ <td>
+ ${sample_dataset.file_path}
+ </td>
+ </tr>
+ %endif
+ %endfor
+ </tbody>
+ </table>
+ <br/>
+ <div class="form-row">
+ <input type="submit" name="save_button" value="Save"/>
+ <input type="submit" name="cancel_button" value="Close"/>
+ </div>
+</form>
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1584,7 +1584,7 @@ class Sample( object ):
COMPLETE = 'Complete',
ERROR = 'Error')
def __init__(self, name=None, desc=None, request=None, form_values=None,
- bar_code=None, library=None, folder=None, dataset_files=None):
+ bar_code=None, library=None, folder=None):
self.name = name
self.desc = desc
self.request = request
@@ -1592,27 +1592,26 @@ class Sample( object ):
self.bar_code = bar_code
self.library = library
self.folder = folder
- self.dataset_files = dataset_files
def current_state(self):
if self.events:
return self.events[0].state
return None
def untransferred_dataset_files(self):
count = 0
- for df in self.dataset_files:
- if df['status'] == self.transfer_status.NOT_STARTED:
+ for df in self.datasets:
+ if df.status == self.transfer_status.NOT_STARTED:
count = count + 1
return count
def inprogress_dataset_files(self):
count = 0
- for df in self.dataset_files:
- if df['status'] not in [self.transfer_status.NOT_STARTED, self.transfer_status.COMPLETE]:
+ for df in self.datasets:
+ if df.status not in [self.transfer_status.NOT_STARTED, self.transfer_status.COMPLETE]:
count = count + 1
return count
def transferred_dataset_files(self):
count = 0
- for df in self.dataset_files:
- if df['status'] == self.transfer_status.COMPLETE:
+ for df in self.datasets:
+ if df.status == self.transfer_status.COMPLETE:
count = count + 1
return count
def dataset_size(self, filepath):
@@ -1639,6 +1638,16 @@ class SampleEvent( object ):
self.state = sample_state
self.comment = comment
+class SampleDataset( object ):
+ def __init__(self, sample=None, name=None, file_path=None,
+ status=None, error_msg=None, size=None):
+ self.sample = sample
+ self.name = name
+ self.file_path = file_path
+ self.status = status
+ self.error_msg = error_msg
+ self.size = size
+
class UserAddress( object ):
def __init__(self, user=None, desc=None, name=None, institution=None,
address=None, city=None, state=None, postal_code=None,
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -34,7 +34,7 @@ class RequestsCommon( BaseController ):
if sample.current_state().name != state:
rval[id] = {
"state": sample.current_state().name,
- "datasets": len(sample.dataset_files),
+ "datasets": len(sample.datasets),
"html_state": unicode( trans.fill_template( "requests/common/sample_state.mako", sample=sample, cntrller=cntrller ), 'utf-8' ),
"html_datasets": unicode( trans.fill_template( "requests/common/sample_datasets.mako", trans=trans, sample=sample, cntrller=cntrller ), 'utf-8' )
}
@@ -518,7 +518,6 @@ class RequestsCommon( BaseController ):
barcode=s.bar_code,
library=s.library,
folder=s.folder,
- dataset_files=s.dataset_files,
field_values=s.values.content,
lib_widget=lib_widget,
folder_widget=folder_widget))
@@ -530,7 +529,6 @@ class RequestsCommon( BaseController ):
barcode='',
library=None,
folder=None,
- dataset_files=[],
field_values=['' for field in request.type.sample_form.fields],
lib_widget=lib_widget,
folder_widget=folder_widget))
@@ -792,8 +790,7 @@ class RequestsCommon( BaseController ):
request, form_values,
current_samples[sample_index]['barcode'],
current_samples[sample_index]['library'],
- current_samples[sample_index]['folder'],
- dataset_files=[])
+ current_samples[sample_index]['folder'])
trans.sa_session.add( s )
trans.sa_session.flush()
@@ -1078,12 +1075,17 @@ class RequestsCommon( BaseController ):
operation='show',
status='error',
message="Set a data library and folder for <b>%s</b> to transfer dataset(s)." % sample.name,
- id=trans.security.encode_id(sample.request.id) ) )
+ id=trans.security.encode_id(sample.request.id) ) )
+ if cntrller == 'requests_admin':
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='manage_datasets',
+ sample_id=sample.id) )
+
if params.get( 'folder_path', '' ):
folder_path = util.restore_text( params.get( 'folder_path', '' ) )
else:
- if len(sample.dataset_files):
- folder_path = os.path.dirname(sample.dataset_files[-1]['filepath'][:-1])
+ if len(sample.datasets):
+ folder_path = os.path.dirname(sample.datasets[-1]['filepath'][:-1])
else:
folder_path = util.restore_text( sample.request.type.datatx_info.get('data_dir', '') )
if folder_path and folder_path[-1] != os.sep:
@@ -1094,6 +1096,6 @@ class RequestsCommon( BaseController ):
message = 'The sequencer login information is incomplete. Click on the <b>Sequencer information</b> to add login details.'
return trans.fill_template( '/requests/common/get_data.mako',
cntrller=cntrller, sample=sample,
- dataset_files=sample.dataset_files,
+ dataset_files=sample.datasets,
message=message, status=status, files=[],
folder_path=folder_path )
--- a/scripts/galaxy_messaging/server/data_transfer.py
+++ b/scripts/galaxy_messaging/server/data_transfer.py
@@ -70,12 +70,12 @@ class DataTransfer(object):
self.config_id_secret = config_id_secret
count=0
while True:
- index = self.get_value_index(self.dom, 'index', count)
+ dataset_id = self.get_value_index(self.dom, 'dataset_id', count)
file = self.get_value_index(self.dom, 'file', count)
name = self.get_value_index(self.dom, 'name', count)
if file:
self.dataset_files.append(dict(name=name,
- index=int(index),
+ dataset_id=int(dataset_id),
file=file))
else:
break
@@ -149,7 +149,7 @@ class DataTransfer(object):
def print_ticks(d):
pass
for i, df in enumerate(self.dataset_files):
- self.update_status(Sample.transfer_status.TRANSFERRING, df['index'])
+ self.update_status(Sample.transfer_status.TRANSFERRING, df['dataset_id'])
try:
cmd = "scp %s@%s:'%s' '%s/%s'" % ( self.username,
self.host,
@@ -168,7 +168,7 @@ class DataTransfer(object):
raise Exception(msg)
except Exception, e:
msg = traceback.format_exc()
- self.update_status('Error', df['index'], msg)
+ self.update_status('Error', df['dataset_id'], msg)
def add_to_library(self):
@@ -189,29 +189,17 @@ class DataTransfer(object):
log.debug(e)
self.error_and_exit(str(e))
- def update_status(self, status, dataset_index='All', msg=''):
+ def update_status(self, status, dataset_id='All', msg=''):
'''
Update the data transfer status for this dataset in the database
'''
try:
- log.debug('Setting status "%s" for dataset "%s" of sample "%s"' % ( status, str(dataset_index), str(self.sample_id) ) )
- df = from_json_string(self.galaxydb.get_sample_dataset_files(self.sample_id))
- if dataset_index == 'All':
+ log.debug('Setting status "%s" for dataset "%s" of sample "%s"' % ( status, str(dataset_id), str(self.sample_id) ) )
+ if dataset_id == 'All':
for dataset in self.dataset_files:
- df[dataset['index']]['status'] = status
- if status == 'Error':
- df[dataset['index']]['error_msg'] = msg
- else:
- df[dataset['index']]['error_msg'] = ''
-
+ self.galaxydb.set_sample_dataset_status(dataset['dataset_id'], status, msg)
else:
- df[dataset_index]['status'] = status
- if status == 'Error':
- df[dataset_index]['error_msg'] = msg
- else:
- df[dataset_index]['error_msg'] = ''
-
- self.galaxydb.set_sample_dataset_files(self.sample_id, to_json_string(df))
+ self.galaxydb.set_sample_dataset_status(dataset_id, status, msg)
log.debug('done.')
except:
log.error(traceback.format_exc())
@@ -229,12 +217,12 @@ class DataTransfer(object):
rc = rc + node.data
return rc
- def get_value_index(self, dom, tag_name, index):
+ def get_value_index(self, dom, tag_name, dataset_id):
'''
This method extracts the tag value from the xml message
'''
try:
- nodelist = dom.getElementsByTagName(tag_name)[index].childNodes
+ nodelist = dom.getElementsByTagName(tag_name)[dataset_id].childNodes
except:
return None
rc = ""
--- a/scripts/galaxy_messaging/server/galaxydb_interface.py
+++ b/scripts/galaxy_messaging/server/galaxydb_interface.py
@@ -28,11 +28,12 @@ class GalaxyDbInterface(object):
def __init__(self, dbstr):
self.dbstr = dbstr
self.db_engine = create_engine(self.dbstr)
- self.db_engine.echo = False
+ self.db_engine.echo = True
self.metadata = MetaData(self.db_engine)
self.session = sessionmaker(bind=self.db_engine)
self.event_table = Table('sample_event', self.metadata, autoload=True )
self.sample_table = Table('sample', self.metadata, autoload=True )
+ self.sample_dataset_table = Table('sample_dataset', self.metadata, autoload=True )
self.request_table = Table('request', self.metadata, autoload=True )
self.request_event_table = Table('request_event', self.metadata, autoload=True )
self.state_table = Table('sample_state', self.metadata, autoload=True )
@@ -105,7 +106,7 @@ class GalaxyDbInterface(object):
create_time=datetime.utcnow(),
sample_id=sample_id,
sample_state_id=int(new_state_id),
- comment='bar code scanner')
+ comment='Update by barcode scan')
# if all the samples for this request are in the final state
# then change the request state to 'Complete'
result = select(columns=[self.sample_table.c.id],
@@ -126,23 +127,22 @@ class GalaxyDbInterface(object):
request_id=self.request_id,
state=request_state,
comment='All samples of this request have finished processing.')
-
- def get_sample_dataset_files(self, sample_id):
- subsubquery = select(columns=[self.sample_table.c.dataset_files],
- whereclause=self.sample_table.c.id==sample_id)
- return subsubquery.execute().fetchall()[0][0]
-
- def set_sample_dataset_files(self, sample_id, value):
- u = self.sample_table.update(whereclause=self.sample_table.c.id==sample_id)
- u.execute(dataset_files=value)
-
+ def set_sample_dataset_status(self, id, new_status, msg=None):
+ u = self.sample_dataset_table.update(whereclause=self.sample_dataset_table.c.id==int(id))
+ u.execute(status=new_status)
+ if new_status == 'Error':
+ u.execute(error_msg=msg)
+ else:
+ u.execute(error_msg='')
+ return
+
if __name__ == '__main__':
print '''This file should not be run directly. To start the Galaxy AMQP Listener:
%sh run_galaxy_listener.sh'''
- dbstr = 'postgres://postgres:postgres@localhost/galaxy_uft'
+ dbstr = 'postgres://postgres:postgres@localhost/g2'
parser = optparse.OptionParser()
parser.add_option('-n', '--name', help='name of the sample field', dest='name', \
--- /dev/null
+++ b/templates/admin/requests/datasets_grid.mako
@@ -0,0 +1,32 @@
+
+
+
+<%def name="custom_javascripts()">
+ <script type="text/javascript">
+ $("#select-dataset-action-button").bind( "click", function(e) {
+ alert('afdhblvi')
+ $.ajax({
+ url: "${h.url_for( controller='requests_admin', action='remote_file_browser' )}",
+ data: {id: 6},
+ error: function() { alert( "Couldn't create new browser" ) },
+ success: function(form_html) {
+ show_modal("Select file", form_html, {
+ "Cancel": function() { window.location = "${h.url_for( controller='requests_admin', action='list' )}"; },
+ "Continue": function() { $(document).trigger("convert_dbkeys"); continue_fn(); }
+ });
+ $("#new-title").focus();
+ replace_big_select_inputs();
+ }
+ });
+ }
+ </script>
+</%def>
+
+<%def name="javascripts()">
+ ${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jquery.event.drag", "jquery.autocomplete", "jquery.mousewheel", "trackster", "ui.core", "ui.sortable" )}
+ ${self.custom_javascripts()}
+ ${parent.javascripts()}
+</%def>
+
+<%inherit file="/grid_base.mako"/>
+
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -574,7 +574,6 @@ Sample.table = Table('sample', metadata,
Column( "bar_code", TrimmedString( 255 ), index=True ),
Column( "library_id", Integer, ForeignKey( "library.id" ), index=True ),
Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ),
- Column( "dataset_files", JSONType() ),
Column( "deleted", Boolean, index=True, default=False ) )
SampleState.table = Table('sample_state', metadata,
@@ -593,6 +592,17 @@ SampleEvent.table = Table('sample_event'
Column( "sample_state_id", Integer, ForeignKey( "sample_state.id" ), index=True ),
Column( "comment", TEXT ) )
+SampleDataset.table = Table('sample_dataset', metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "sample_id", Integer, ForeignKey( "sample.id" ), index=True ),
+ Column( "name", TrimmedString( 255 ), nullable=False ),
+ Column( "file_path", TrimmedString( 255 ), nullable=False ),
+ Column( "status", TrimmedString( 255 ), nullable=False ),
+ Column( "error_msg", TEXT ),
+ Column( "size", TrimmedString( 255 ) ) )
+
Page.table = Table( "page", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
@@ -792,6 +802,8 @@ assign_mapper( context, Sample, Sample.t
properties=dict(
events=relation( SampleEvent, backref="sample",
order_by=desc(SampleEvent.table.c.update_time) ),
+ datasets=relation( SampleDataset, backref="sample",
+ order_by=desc(SampleDataset.table.c.update_time) ),
values=relation( FormValues,
primaryjoin=( Sample.table.c.form_values_id == FormValues.table.c.id ) ),
request=relation( Request,
@@ -865,6 +877,9 @@ assign_mapper( context, SampleEvent, Sam
assign_mapper( context, SampleState, SampleState.table,
properties=None )
+assign_mapper( context, SampleDataset, SampleDataset.table,
+ properties=None )
+
assign_mapper( context, UserAddress, UserAddress.table,
properties=dict(
user=relation( User,
--- a/templates/display_common.mako
+++ b/templates/display_common.mako
@@ -79,6 +79,8 @@
class_plural = "Libraries"
elif a_class == model.HistoryDatasetAssociation:
class_plural = "Datasets"
+ elif a_class == model.SampleDataset:
+ class_plural = "Sample Datasets"
elif a_class == model.FormDefinitionCurrent:
class_plural = "Forms"
else:
--- a/templates/requests/common/get_data.mako
+++ b/templates/requests/common/get_data.mako
@@ -94,8 +94,7 @@
</style>
-<h2>Data transfer from Sequencer</h2>
-<h3>Sample "${sample.name}" of Request "${sample.request.name}"</h3>
+<h2>Datasets of Sample "${sample.name}"</h2><br/><br/>
--- a/templates/requests/common/show_request.mako
+++ b/templates/requests/common/show_request.mako
@@ -147,30 +147,29 @@ function showContent(vThis)
</div><ul class="manage-table-actions">
-
- %if request.unsubmitted() and request.samples:
- <li>
+ <li><a class="action-button" id="seqreq-${request.id}-popup" class="menubutton">Sequencing Request Actions</a></li>
+ <div popupmenu="seqreq-${request.id}-popup">
+ %if request.unsubmitted() and request.samples:
<a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller=cntrller, action='list', operation='Submit', id=trans.security.encode_id(request.id) )}">
- <span>Submit request</span></a>
- </li>
- %endif
- %if cntrller == 'requests_admin' and trans.user_is_admin():
- %if request.submitted():
- <li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='reject', id=trans.security.encode_id(request.id))}">
- <span>Reject request</span></a>
- </li>
+ <span>Submit</span></a>
%endif
- %endif
- <li>
+ <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='Edit', id=trans.security.encode_id(request.id))}">
+ <span>Edit</span></a><a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='events', id=trans.security.encode_id(request.id) )}"><span>History</span></a>
- </li>
+ %if cntrller == 'requests_admin' and trans.user_is_admin():
+ %if request.submitted():
+ <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='reject', id=trans.security.encode_id(request.id))}">
+ <span>Reject</span></a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', show_page=True, request_id=request.id)}">
+ <span>Select dataset(s) to transfer</span></a>
+ %endif
+ %endif
+ </div><li><a class="action-button" href="${h.url_for( controller=cntrller, action='list')}"><span>Browse requests</span></a></li>
-
</ul>
@@ -396,12 +395,21 @@ function showContent(vThis)
<td>${info['name']}</td><td>${info['barcode']}</td>
%if sample.request.unsubmitted():
- <td>Unsubmitted</td>
+ ##<td>Unsubmitted</td>
+ <td>
+ <div id="history-name-container">
+ <div id="history-name" class="tooltip editable-text" title="Click to rename history">Unsubmitted</div>
+ </div>
+ </td>
%else:
<td id="sampleState-${sample.id}">${render_sample_state( cntrller, sample )}</td>
%endif
%if info['library']:
- <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
+ %if cntrller == 'requests':
+ <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
+ %elif cntrller == 'requests_admin':
+ <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library_admin', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
+ %endif
%else:
<td></td>
%endif
--- a/templates/admin/requests/dataset.mako
+++ b/templates/admin/requests/dataset.mako
@@ -12,60 +12,44 @@
<ul class="manage-table-actions"><li><a class="action-button" href="${h.url_for( controller='requests_common', action='show_datatx_page', cntrller='requests_admin', sample_id=trans.security.encode_id(sample.id) )}">
- <span>Dataset transfer page</span></a>
+ <span>Browse datasets</span></a></li></ul><div class="toolForm">
- <div class="toolFormTitle">Dataset details</div>
+ <div class="toolFormTitle">Dataset Information</div><div class="toolFormBody">
- <form name="dataset_details" action="${h.url_for( controller='requests_admin', action='dataset_details', save_changes=True, sample_id=trans.security.encode_id(sample.id), dataset_index=dataset_index )}" method="post" >
- <%
- dataset = sample.dataset_files[dataset_index]
- %><div class="form-row"><label>Name:</label><div style="float: left; width: 250px; margin-right: 10px;">
- %if dataset['status'] in [sample.transfer_status.IN_QUEUE, sample.transfer_status.NOT_STARTED]:
- <input type="text" name="name" value="${dataset['name']}" size="60"/>
- %else:
- ${dataset['name']}
- %endif
-
+ ${sample_dataset.name}
</div><div style="clear: both"></div></div><div class="form-row"><label>File on the Sequencer:</label><div style="float: left; width: 250px; margin-right: 10px;">
- ${dataset['filepath']}
- ##<input type="text" name="filepath" value="${dataset['filepath']}" size="100" readonly/>
+ ${sample_dataset.file_path}
</div><div style="clear: both"></div></div><div class="form-row"><label>Size:</label><div style="float: left; width: 250px; margin-right: 10px;">
- ${dataset.get('size', 'Unknown')}
+ ${sample_dataset.size}
</div><div style="clear: both"></div></div><div class="form-row"><label>Transfer status:</label><div style="float: left; width: 250px; margin-right: 10px;">
- ${dataset['status']}
+ ${sample_dataset.status}
<br/>
- %if dataset['status'] == sample.transfer_status.ERROR:
- ${dataset['error_msg']}
+ %if sample_dataset.status == sample.transfer_status.ERROR:
+ ${sample_dataset.error_msg}
%endif
</div><div style="clear: both"></div></div>
- %if dataset['status'] in [sample.transfer_status.IN_QUEUE, sample.transfer_status.NOT_STARTED]:
- <div class="form-row">
- <input type="submit" name="save" value="Save"/>
- </div>
- %endif
- </form></div></div>
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -146,7 +146,7 @@ class RequestsGrid( grids.Grid ):
#
-# ---- Request Type Gridr ------------------------------------------------------
+# ---- Request Type Grid ------------------------------------------------------
#
class RequestTypeGrid( grids.Grid ):
# Custom column types
@@ -207,6 +207,57 @@ class RequestTypeGrid( grids.Grid ):
action='create_request_type' ) )
]
+
+# ---- Data Transfer Grid ------------------------------------------------------
+#
+class DataTransferGrid( grids.Grid ):
+ # Custom column types
+ class NameColumn( grids.TextColumn ):
+ def get_value(self, trans, grid, sample_dataset):
+ return sample_dataset.name
+ class SizeColumn( grids.TextColumn ):
+ def get_value(self, trans, grid, sample_dataset):
+ return sample_dataset.size
+ class StatusColumn( grids.TextColumn ):
+ def get_value(self, trans, grid, sample_dataset):
+ return sample_dataset.status
+ # Grid definition
+ title = "Sample Datasets"
+ template = "admin/requests/datasets_grid.mako"
+ model_class = model.SampleDataset
+ default_sort_key = "-create_time"
+ num_rows_per_page = 50
+ preserve_state = True
+ use_paging = True
+ #default_filter = dict( deleted="False" )
+ columns = [
+ NameColumn( "Name",
+ #key="name",
+ model_class=model.SampleDataset,
+ link=( lambda item: dict( operation="view", id=item.id ) ),
+ attach_popup=True,
+ filterable="advanced" ),
+ SizeColumn( "Size",
+ #key='size',
+ model_class=model.SampleDataset,
+ filterable="advanced" ),
+ StatusColumn( "Status",
+ #key='status',
+ model_class=model.SampleDataset,
+ filterable="advanced" ),
+ ]
+ columns.append( grids.MulticolFilterColumn( "Search",
+ cols_to_filter=[ columns[0] ],
+ key="free-text-search",
+ visible=False,
+ filterable="standard" ) )
+ operations = [
+ grids.GridOperation( "Start Transfer", allow_multiple=True, condition=( lambda item: item.status in [model.Sample.transfer_status.NOT_STARTED] ) ),
+ grids.GridOperation( "Rename", allow_multiple=True, allow_popup=False, condition=( lambda item: item.status in [model.Sample.transfer_status.NOT_STARTED] ) ),
+ grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.status in [model.Sample.transfer_status.NOT_STARTED] ) ),
+ ]
+ def apply_query_filter( self, trans, query, **kwd ):
+ return query.filter_by( sample_id=kwd['sample_id'] )
#
# ---- Request Controller ------------------------------------------------------
#
@@ -214,6 +265,7 @@ class RequestTypeGrid( grids.Grid ):
class RequestsAdmin( BaseController ):
request_grid = RequestsGrid()
requesttype_grid = RequestTypeGrid()
+ datatx_grid = DataTransferGrid()
@web.expose
@@ -280,11 +332,11 @@ class RequestsAdmin( BaseController ):
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
- sample = trans.sa_session.query( self.app.model.Sample ).get( int(id) )
- datatx_info = sample.request.type.datatx_info
+ request = trans.sa_session.query( self.app.model.Request ).get( int(id) )
+ datatx_info = request.type.datatx_info
cmd = 'ssh %s@%s "ls -oghp \'%s\'"' % ( datatx_info['username'],
- datatx_info['host'],
- folder_path )
+ datatx_info['host'],
+ folder_path )
output = pexpect.run(cmd, events={'.ssword:*': datatx_info['password']+'\r\n',
pexpect.TIMEOUT:print_ticks},
timeout=10)
@@ -297,8 +349,8 @@ class RequestsAdmin( BaseController ):
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
- sample = trans.sa_session.query( self.app.model.Sample ).get( int(id) )
- return self.__get_files(trans, sample, folder_path)
+ request = trans.sa_session.query( self.app.model.Request ).get( int(id) )
+ return self.__get_files(trans, request.type, folder_path)
def __reject_request(self, trans, **kwd):
try:
@@ -522,12 +574,120 @@ class RequestsAdmin( BaseController ):
#
# Data transfer from sequencer
#
+
+ @web.expose
+ @web.require_admin
+ def manage_datasets( self, trans, **kwd ):
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if not kwd.get( 'id', None ):
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ status='error',
+ message="Invalid sample dataset ID") )
+ if operation == "view":
+ sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(kwd['id']) )
+ return trans.fill_template( '/admin/requests/dataset.mako',
+ sample=sample_dataset.sample,
+ sample_dataset=sample_dataset)
- def __get_files(self, trans, sample, folder_path):
+ elif operation == "delete":
+ id_list = util.listify( kwd['id'] )
+ for id in id_list:
+ sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(id) )
+ sample_id = sample_dataset.sample_id
+ trans.sa_session.delete( sample_dataset )
+ trans.sa_session.flush()
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='manage_datasets',
+ sample_id=sample_id,
+ status='done',
+ message="%i dataset(s) have been removed." % len(id_list)) )
+
+ elif operation == "rename":
+ id_list = util.listify( kwd['id'] )
+ sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(id_list[0]) )
+ return trans.fill_template( '/admin/requests/rename_datasets.mako',
+ sample=sample_dataset.sample,
+ id_list=id_list )
+ elif operation == "start transfer":
+ id_list = util.listify( kwd['id'] )
+ sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(id_list[0]) )
+ self.__start_datatx(trans, sample_dataset.sample, id_list)
+
+
+ # Render the grid view
+ try:
+ sample = trans.sa_session.query( trans.app.model.Sample ).get( kwd['sample_id'] )
+ except:
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ status='error',
+ message="Invalid sample ID" ) )
+ self.datatx_grid.title = 'Datasets of Sample "%s"' % sample.name
+ self.datatx_grid.global_actions = [
+ grids.GridAction( "Refresh",
+ dict( controller='requests_admin',
+ action='manage_datasets',
+ sample_id=sample.id) ),
+ grids.GridAction( "Select Datasets",
+ dict(controller='requests_admin',
+ action='get_data',
+ request_id=sample.request.id,
+ folder_path=sample.request.type.datatx_info['data_dir'],
+ sample_id=sample.id,
+ show_page=True)),
+ grids.GridAction( 'Data Library "%s"' % sample.library.name,
+ dict(controller='library_common',
+ action='browse_library',
+ cntrller='library_admin',
+ id=trans.security.encode_id( sample.library.id))),
+ grids.GridAction( "Browse this request",
+ dict( controller='requests_admin',
+ action='list',
+ operation='show',
+ id=trans.security.encode_id(sample.request.id)))]
+ return self.datatx_grid( trans, **kwd )
+
+ @web.expose
+ @web.require_admin
+ def rename_datasets( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ try:
+ sample = trans.sa_session.query( trans.app.model.Sample ).get( trans.security.decode_id(kwd['sample_id']))
+ except:
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ status='error',
+ message="Invalid sample ID" ) )
+ if params.get( 'save_button', False ):
+ id_list = util.listify( kwd['id_list'] )
+ for id in id_list:
+ sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(id) )
+ prepend = util.restore_text( params.get( 'prepend_%i' % sample_dataset.id, '' ) )
+ name = util.restore_text( params.get( 'name_%i' % sample_dataset.id, sample_dataset.name ) )
+ if prepend == 'None':
+ sample_dataset.name = name
+ else:
+ sample_dataset.name = prepend+'_'+name
+ trans.sa_session.add( sample_dataset )
+ trans.sa_session.flush()
+ return trans.fill_template( '/admin/requests/rename_datasets.mako',
+ sample=sample, id_list=id_list,
+ message='Changes saved successfully.',
+ status='done' )
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='manage_datasets',
+ sample_id=sample.id) )
+
+
+ def __get_files(self, trans, request_type, folder_path):
'''
This method retrieves the filenames to be transfer from the remote host.
'''
- datatx_info = sample.request.type.datatx_info
+ datatx_info = request_type.datatx_info
if not datatx_info['host'] or not datatx_info['username'] or not datatx_info['password']:
message = "Error in sequencer login information."
return trans.response.send_redirect( web.url_for( controller='requests_common',
@@ -555,123 +715,152 @@ class RequestsAdmin( BaseController ):
return output.splitlines()
- def __get_files_in_dir(self, trans, sample, folder_path):
- tmpfiles = self.__get_files(trans, sample, folder_path)
- for tf in tmpfiles:
- if tf[-1] == os.sep:
- self.__get_files_in_dir(trans, sample, os.path.join(folder_path, tf))
+# def __get_files_in_dir(self, trans, sample, folder_path):
+# tmpfiles = self.__get_files(trans, sample, folder_path)
+# for tf in tmpfiles:
+# if tf[-1] == os.sep:
+# self.__get_files_in_dir(trans, sample, os.path.join(folder_path, tf))
+# else:
+# sample.dataset_files.append([os.path.join(folder_path, tf),
+# sample.transfer_status.NOT_STARTED])
+# trans.sa_session.add( sample )
+# trans.sa_session.flush()
+# return
+
+
+ def __samples_selectbox(self, trans, request, sample_id=None):
+ samples_selectbox = SelectField('sample_id')
+ for i, s in enumerate(request.samples):
+ if str(s.id) == sample_id:
+ samples_selectbox.add_option(s.name, s.id, selected=True)
else:
- sample.dataset_files.append([os.path.join(folder_path, tf),
- sample.transfer_status.NOT_STARTED])
- trans.sa_session.add( sample )
- trans.sa_session.flush()
- return
+ samples_selectbox.add_option(s.name, s.id)
+ return samples_selectbox
+
@web.expose
@web.require_admin
def get_data(self, trans, **kwd):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
try:
- sample = trans.sa_session.query( trans.app.model.Sample ).get( kwd['sample_id'] )
+ request = trans.sa_session.query( trans.app.model.Request ).get( kwd['request_id'] )
except:
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='list',
status='error',
- message="Invalid sample ID" ) )
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ message="Invalid request ID" ) )
+ files_list = util.listify( params.get( 'files_list', '' ) )
folder_path = util.restore_text( params.get( 'folder_path',
- sample.request.type.datatx_info['data_dir'] ) )
- files_list = util.listify( params.get( 'files_list', '' ) )
- if params.get( 'start_transfer_button', False ) == 'True':
- return self.__start_datatx(trans, sample)
+ request.type.datatx_info['data_dir'] ) )
+ sbox = self.__samples_selectbox(trans, request, kwd.get('sample_id', None))
if not folder_path:
- return trans.fill_template( '/requests/common/get_data.mako',
- cntrller='requests_admin',
- sample=sample, files=[],
- dataset_files=sample.dataset_files,
+ return trans.fill_template( '/admin/requests/get_data.mako',
+ cntrller='requests_admin', request=request,
+ samples_selectbox=sbox, files=[],
folder_path=folder_path )
if folder_path[-1] != os.sep:
folder_path = folder_path+os.sep
- if params.get( 'browse_button', False ):
+ if params.get( 'show_page', False ):
+ if kwd.get('sample_id', None):
+ sample = trans.sa_session.query( trans.app.model.Sample ).get( kwd['sample_id'] )
+ if sample.datasets:
+ folder_path = os.path.dirname(sample.datasets[-1].file_path)
+ return trans.fill_template( '/admin/requests/get_data.mako',
+ cntrller='requests_admin', request=request,
+ samples_selectbox=sbox, files=[],
+ folder_path=folder_path,
+ status=status, message=message )
+ elif params.get( 'browse_button', False ):
# get the filenames from the remote host
- files = self.__get_files(trans, sample, folder_path)
+ files = self.__get_files(trans, request.type, folder_path)
if folder_path[-1] != os.sep:
folder_path += os.sep
- return trans.fill_template( '/requests/common/get_data.mako',
- cntrller='requests_admin',
- sample=sample, files=files,
- dataset_files=sample.dataset_files,
- folder_path=folder_path )
+ return trans.fill_template( '/admin/requests/get_data.mako',
+ cntrller='requests_admin', request=request,
+ samples_selectbox=sbox, files=files,
+ folder_path=folder_path,
+ status=status, message=message )
elif params.get( 'folder_up', False ):
if folder_path[-1] == os.sep:
folder_path = os.path.dirname(folder_path[:-1])
# get the filenames from the remote host
- files = self.__get_files(trans, sample, folder_path)
+ files = self.__get_files(trans, request.type, folder_path)
if folder_path[-1] != os.sep:
folder_path += os.sep
- return trans.fill_template( '/requests/common/get_data.mako',
- cntrller='requests_admin',
- sample=sample, files=files,
- dataset_files=sample.dataset_files,
- folder_path=folder_path )
+ return trans.fill_template( '/admin/requests/get_data.mako',
+ cntrller='requests_admin',request=request,
+ samples_selectbox=sbox, files=files,
+ folder_path=folder_path,
+ status=status, message=message )
elif params.get( 'open_folder', False ):
if len(files_list) == 1:
folder_path = os.path.join(folder_path, files_list[0])
# get the filenames from the remote host
- files = self.__get_files(trans, sample, folder_path)
+ files = self.__get_files(trans, request.type, folder_path)
if folder_path[-1] != os.sep:
folder_path += os.sep
- return trans.fill_template( '/requests/common/get_data.mako',
- cntrller='requests_admin',
- sample=sample, files=files,
- dataset_files=sample.dataset_files,
- folder_path=folder_path )
- elif params.get( 'remove_dataset_button', False ):
- # get the filenames from the remote host
- files = self.__get_files(trans, sample, folder_path)
- dataset_index = int(params.get( 'dataset_index', 0 ))
- del sample.dataset_files[dataset_index]
- trans.sa_session.add( sample )
- trans.sa_session.flush()
- return trans.fill_template( '/requests/common/get_data.mako',
- cntrller='requests_admin',
- sample=sample, files=files,
- dataset_files=sample.dataset_files,
- folder_path=folder_path)
- elif params.get( 'select_files_button', False ):
- folder_files = []
- if len(files_list):
- for f in files_list:
- filepath = os.path.join(folder_path, f)
- if f[-1] == os.sep:
- # the selected item is a folder so transfer all the
- # folder contents
- # FIXME
- #self.__get_files_in_dir(trans, sample, filepath)
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='get_data',
- sample_id=sample.id,
- folder_path=folder_path,
- open_folder=True))
- else:
- sample.dataset_files.append(dict(filepath=filepath,
- status=sample.transfer_status.NOT_STARTED,
- name=self.__dataset_name(sample, filepath.split('/')[-1]),
- error_msg='',
- size=sample.dataset_size(filepath)))
- trans.sa_session.add( sample )
- trans.sa_session.flush()
+ return trans.fill_template( '/admin/requests/get_data.mako',
+ cntrller='requests_admin', request=request,
+ samples_selectbox=sbox, files=files,
+ folder_path=folder_path,
+ status=status, message=message )
+ elif params.get( 'select_show_datasets_button', False ):
+ sample = trans.sa_session.query( trans.app.model.Sample ).get( kwd['sample_id'] )
+ retval = self.__save_sample_datasets(trans, sample, files_list, folder_path)
+ if retval: message='The dataset(s) %s have been selected for sample <b>%s</b>' %(str(retval)[1:-1].replace("'", ""), sample.name)
+ else: message = None
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='manage_datasets',
+ sample_id=sample.id,
+ status='done',
+ message=message) )
+ elif params.get( 'select_more_button', False ):
+ sample = trans.sa_session.query( trans.app.model.Sample ).get( kwd['sample_id'] )
+ retval = self.__save_sample_datasets(trans, sample, files_list, folder_path)
+ if retval: message='The dataset(s) %s have been selected for sample <b>%s</b>' %(str(retval)[1:-1].replace("'", ""), sample.name)
+ else: message = None
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='get_data',
+ request_id=sample.request.id,
+ folder_path=folder_path,
sample_id=sample.id,
- folder_path=folder_path,
- open_folder=True))
-
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller='requests_admin' ,
- action='show_datatx_page',
- sample_id=trans.security.encode_id(sample.id),
- folder_path=folder_path))
+ open_folder=True,
+ status='done',
+ message=message))
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='get_data',
+ request_id=sample.request.id,
+ folder_path=folder_path,
+ show_page=True))
+
+ def __save_sample_datasets(self, trans, sample, files_list, folder_path):
+ files = []
+ if len(files_list):
+ for f in files_list:
+ filepath = os.path.join(folder_path, f)
+ if f[-1] == os.sep:
+ # the selected item is a folder so transfer all the
+ # folder contents
+ # FIXME
+ #self.__get_files_in_dir(trans, sample, filepath)
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='get_data',
+ request=sample.request,
+ folder_path=folder_path,
+ open_folder=True))
+ else:
+ sample_dataset = trans.app.model.SampleDataset( sample=sample,
+ file_path=filepath,
+ status=sample.transfer_status.NOT_STARTED,
+ name=self.__dataset_name(sample, filepath.split('/')[-1]),
+ error_msg='',
+ size=sample.dataset_size(filepath))
+ trans.sa_session.add( sample_dataset )
+ trans.sa_session.flush()
+ files.append(str(sample_dataset.name))
+ return files
+
def __dataset_name(self, sample, filepath):
name = filepath.split('/')[-1]
@@ -735,7 +924,7 @@ class RequestsAdmin( BaseController ):
trans.sa_session.flush()
return datatx_user
- def __send_message(self, trans, datatx_info, sample):
+ def __send_message(self, trans, datatx_info, sample, id_list):
'''
This method creates the xml message and sends it to the rabbitmq server
'''
@@ -752,20 +941,20 @@ class RequestsAdmin( BaseController ):
</data_transfer>'''
dataset_xml = \
'''<dataset>
- <index>%(INDEX)s</index>
+ <dataset_id>%(ID)s</dataset_id><name>%(NAME)s</name><file>%(FILE)s</file></dataset>'''
datasets = ''
- for index, dataset in enumerate(sample.dataset_files):
- if dataset['status'] == sample.transfer_status.NOT_STARTED:
- datasets = datasets + dataset_xml % dict(INDEX=str(index),
- NAME=dataset['name'],
- FILE=dataset['filepath'])
- sample.dataset_files[index]['status'] = sample.transfer_status.IN_QUEUE
-
- trans.sa_session.add( sample )
- trans.sa_session.flush()
+ for id in id_list:
+ sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(id) )
+ if sample_dataset.status == sample.transfer_status.NOT_STARTED:
+ datasets = datasets + dataset_xml % dict(ID=str(sample_dataset.id),
+ NAME=sample_dataset.name,
+ FILE=sample_dataset.file_path)
+ sample_dataset.status = sample.transfer_status.IN_QUEUE
+ trans.sa_session.add( sample_dataset )
+ trans.sa_session.flush()
data = xml % dict(DATA_HOST=datatx_info['host'],
DATA_USER=datatx_info['username'],
DATA_PASSWORD=datatx_info['password'],
@@ -790,7 +979,7 @@ class RequestsAdmin( BaseController ):
chan.close()
conn.close()
- def __start_datatx(self, trans, sample):
+ def __start_datatx(self, trans, sample, id_list):
# data transfer user
datatx_user = self.__setup_datatx_user(trans, sample.library, sample.folder)
# validate sequecer information
@@ -799,46 +988,19 @@ class RequestsAdmin( BaseController ):
not datatx_info['username'] or \
not datatx_info['password']:
message = "Error in sequencer login information."
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller='requests_admin' ,
- action='show_datatx_page',
- sample_id=trans.security.encode_id(sample.id),
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='manage_datasets',
+ sample_id=sample.id,
status='error',
- message=message))
- self.__send_message(trans, datatx_info, sample)
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller='requests_admin' ,
- action='show_datatx_page',
- sample_id=trans.security.encode_id(sample.id),
- folder_path=datatx_info['data_dir']))
- @web.expose
- @web.require_admin
- def dataset_details( self, trans, **kwd ):
- try:
- sample = trans.sa_session.query( trans.app.model.Sample ).get( trans.security.decode_id(kwd['sample_id']) )
- except:
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='list',
- status='error',
- message="Invalid sample ID" ) )
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- dataset_index = int( params.get( 'dataset_index', '' ) )
- if params.get('save', '') == 'Save':
- sample.dataset_files[dataset_index]['name'] = util.restore_text( params.get( 'name',
- sample.dataset_files[dataset_index]['name'] ) )
- trans.sa_session.add( sample )
- trans.sa_session.flush()
- status = 'done'
- message = 'Saved the changes made to the dataset.'
- return trans.fill_template( '/admin/requests/dataset.mako',
- sample=sample,
- dataset_index=dataset_index,
- message=message,
- status=status)
+ message=message) )
+ self.__send_message(trans, datatx_info, sample, id_list)
+ message="%i dataset(s) have been queued for transfer from the sequencer. Click on <b>Refresh</b> button above to get the latest transfer status." % len(id_list)
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='manage_datasets',
+ sample_id=sample.id,
+ status='done',
+ message=message) )
-#
##
#### Request Type Stuff ###################################################
##
1
0

galaxy-dist commit af48a13e46b9: Fix a bug in the sge runner's stop_job and job finishing logic.
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1280411093 14400
# Node ID af48a13e46b9ab0136bcbf14508bd9bb044ad257
# Parent 9780508ee0c31b132158842afcae6c5ef90028a7
Fix a bug in the sge runner's stop_job and job finishing logic.
--- a/lib/galaxy/jobs/runners/sge.py
+++ b/lib/galaxy/jobs/runners/sge.py
@@ -69,6 +69,7 @@ class SGEJobRunner( object ):
if DRMAA is None:
raise Exception( "SGEJobRunner requires DRMAA_python which was not found" )
self.app = app
+ self.sa_session = app.model.context
# 'watched' and 'queue' are both used to keep track of jobs to watch.
# 'queue' is used to add new watched jobs, and can be called from
# any thread (usually by the 'queue_job' method). 'watched' must only
@@ -291,13 +292,9 @@ class SGEJobRunner( object ):
if state == DRMAA.Session.RUNNING and not sge_job_state.running:
sge_job_state.running = True
sge_job_state.job_wrapper.change_state( model.Job.states.RUNNING )
- if state == DRMAA.Session.DONE:
+ if state in ( DRMAA.Session.DONE, DRMAA.Session.FAILED ):
self.work_queue.put( ( 'finish', sge_job_state ) )
continue
- if state == DRMAA.Session.FAILED:
- sge_job_state.fail_message = "Cluster could not complete job"
- self.work_queue.put( ( 'fail', sge_job_state ) )
- continue
sge_job_state.old_state = state
new_watched.append( sge_job_state )
# Replace the watch list with the updated version
1
0

galaxy-dist commit 9780508ee0c3: Allow interval to bedstrict converter to work on bed files that may have e.g. a 'track' line.
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dan Blankenberg <dan(a)bx.psu.edu>
# Date 1280371288 14400
# Node ID 9780508ee0c31b132158842afcae6c5ef90028a7
# Parent 75e99661d24caaeda1381b14cd062ffdcf7c3ecd
Allow interval to bedstrict converter to work on bed files that may have e.g. a 'track' line.
--- a/lib/galaxy/datatypes/converters/interval_to_bedstrict_converter.py
+++ b/lib/galaxy/datatypes/converters/interval_to_bedstrict_converter.py
@@ -81,8 +81,8 @@ def __main__():
first_skipped_line = count + 1
continue
fields = line.split('\t')
- assert len( fields ) >= 3, 'A BED file requires at least 3 columns' #we can't fix this
try:
+ assert len( fields ) >= 3, 'A BED file requires at least 3 columns' #we can't fix this
if len(fields) > 12:
strict_bed = False
break
1
0

galaxy-dist commit fd9514c5349f: The DRMAA job runner, which should be compatible with all DRMs which support DRMAA (most notably, LSF and PBS Pro). Minor testing with LSF has been done, and this code is based on the SGE runner code which has been in service for a long time, but it should not be considered production.
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1280348685 14400
# Node ID fd9514c5349fadaf31abec04fd8725cc50a4213f
# Parent 34ee69d9ba931b99a935b4ba7b38288160d68d31
The DRMAA job runner, which should be compatible with all DRMs which support DRMAA (most notably, LSF and PBS Pro). Minor testing with LSF has been done, and this code is based on the SGE runner code which has been in service for a long time, but it should not be considered production.
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -737,6 +737,9 @@ class DefaultJobDispatcher( object ):
elif runner_name == "sge":
import runners.sge
self.job_runners[runner_name] = runners.sge.SGEJobRunner( app )
+ elif runner_name == "drmaa":
+ import runners.drmaa
+ self.job_runners[runner_name] = runners.drmaa.DRMAAJobRunner( app )
else:
log.error( "Unable to start unknown job runner: %s" %runner_name )
--- a/eggs.ini
+++ b/eggs.ini
@@ -31,6 +31,7 @@ amqplib = 0.6.1
Beaker = 1.4
decorator = 3.1.2
docutils = 0.4
+drmaa = 0.4b3
elementtree = 1.2.6_20050316
GeneTrack = 2.0.0_beta_1
lrucache = 0.2
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -151,7 +151,7 @@ class Configuration( object ):
raise ConfigurationError("File not found: %s" % path )
# Check job runners so the admin can scramble dependent egg.
if self.start_job_runners is not None:
- runner_to_egg = dict( pbs = 'pbs_python', sge = 'DRMAA_python' )
+ runner_to_egg = dict( pbs = 'pbs_python', sge = 'DRMAA_python', drmaa = 'drmaa' )
for runner in self.start_job_runners.split( ',' ):
try:
pkg_resources.require( runner_to_egg[runner] )
--- /dev/null
+++ b/lib/galaxy/jobs/runners/drmaa.py
@@ -0,0 +1,347 @@
+import os, logging, threading, time
+from Queue import Queue, Empty
+
+from galaxy import model
+from paste.deploy.converters import asbool
+
+import pkg_resources
+
+try:
+ pkg_resources.require( "drmaa" )
+ drmaa = __import__( "drmaa" )
+except Exception, e:
+ drmaa = str( e )
+
+log = logging.getLogger( __name__ )
+
+if type( drmaa ) != str:
+ drmaa_state = {
+ drmaa.JobState.UNDETERMINED: 'process status cannot be determined',
+ drmaa.JobState.QUEUED_ACTIVE: 'job is queued and active',
+ drmaa.JobState.SYSTEM_ON_HOLD: 'job is queued and in system hold',
+ drmaa.JobState.USER_ON_HOLD: 'job is queued and in user hold',
+ drmaa.JobState.USER_SYSTEM_ON_HOLD: 'job is queued and in user and system hold',
+ drmaa.JobState.RUNNING: 'job is running',
+ drmaa.JobState.SYSTEM_SUSPENDED: 'job is system suspended',
+ drmaa.JobState.USER_SUSPENDED: 'job is user suspended',
+ drmaa.JobState.DONE: 'job finished normally',
+ drmaa.JobState.FAILED: 'job finished, but failed',
+ }
+
+drm_template = """#!/bin/sh
+#$ -S /bin/sh
+GALAXY_LIB="%s"
+if [ "$GALAXY_LIB" != "None" ]; then
+ if [ -n "$PYTHONPATH" ]; then
+ PYTHONPATH="$GALAXY_LIB:$PYTHONPATH"
+ else
+ PYTHONPATH="$GALAXY_LIB"
+ fi
+ export PYTHONPATH
+fi
+cd %s
+%s
+"""
+
+class DRMAAJobState( object ):
+ def __init__( self ):
+ """
+ Encapsulates state related to a job that is being run via the DRM and
+ that we need to monitor.
+ """
+ self.job_wrapper = None
+ self.job_id = None
+ self.old_state = None
+ self.running = False
+ self.job_file = None
+ self.ofile = None
+ self.efile = None
+ self.runner_url = None
+
+class DRMAAJobRunner( object ):
+ """
+ Job runner backed by a finite pool of worker threads. FIFO scheduling
+ """
+ STOP_SIGNAL = object()
+ def __init__( self, app ):
+ """Initialize this job runner and start the monitor thread"""
+ # Check if drmaa was importable, fail if not
+ if type( drmaa ) == str:
+ raise Exception( "DRMAAJobRunner requires drmaa module which could not be loaded: %s" % drmaa )
+ self.app = app
+ self.sa_session = app.model.context
+ # 'watched' and 'queue' are both used to keep track of jobs to watch.
+ # 'queue' is used to add new watched jobs, and can be called from
+ # any thread (usually by the 'queue_job' method). 'watched' must only
+ # be modified by the monitor thread, which will move items from 'queue'
+ # to 'watched' and then manage the watched jobs.
+ self.watched = []
+ self.monitor_queue = Queue()
+ self.ds = drmaa.Session()
+ self.ds.initialize()
+ self.monitor_thread = threading.Thread( target=self.monitor )
+ self.monitor_thread.start()
+ self.work_queue = Queue()
+ self.work_threads = []
+ nworkers = app.config.cluster_job_queue_workers
+ for i in range( nworkers ):
+ worker = threading.Thread( target=self.run_next )
+ worker.start()
+ self.work_threads.append( worker )
+ log.debug( "%d workers ready" % nworkers )
+
+ def get_native_spec( self, url ):
+ """Get any native DRM arguments specified by the site configuration"""
+ try:
+ return url.split('/')[2] or None
+ except:
+ return None
+
+ def run_next( self ):
+ """
+ Run the next item in the queue (a job waiting to run or finish )
+ """
+ while 1:
+ ( op, obj ) = self.work_queue.get()
+ if op is self.STOP_SIGNAL:
+ return
+ try:
+ if op == 'queue':
+ self.queue_job( obj )
+ elif op == 'finish':
+ self.finish_job( obj )
+ elif op == 'fail':
+ self.fail_job( obj )
+ except:
+ log.exception( "Uncaught exception %sing job" % op )
+
+ def queue_job( self, job_wrapper ):
+ """Create job script and submit it to the DRM"""
+
+ try:
+ job_wrapper.prepare()
+ command_line = job_wrapper.get_command_line()
+ except:
+ job_wrapper.fail( "failure preparing job", exception=True )
+ log.exception("failure running job %d" % job_wrapper.job_id)
+ return
+
+ runner_url = job_wrapper.tool.job_runner
+
+ # This is silly, why would we queue a job with no command line?
+ if not command_line:
+ job_wrapper.finish( '', '' )
+ return
+
+ # Check for deletion before we change state
+ if job_wrapper.get_state() == model.Job.states.DELETED:
+ log.debug( "Job %s deleted by user before it entered the queue" % job_wrapper.job_id )
+ job_wrapper.cleanup()
+ return
+
+ # Change to queued state immediately
+ job_wrapper.change_state( model.Job.states.QUEUED )
+
+ # define job attributes
+ ofile = "%s/database/pbs/%s.o" % (os.getcwd(), job_wrapper.job_id)
+ efile = "%s/database/pbs/%s.e" % (os.getcwd(), job_wrapper.job_id)
+ jt = self.ds.createJobTemplate()
+ jt.remoteCommand = "%s/database/pbs/galaxy_%s.sh" % (os.getcwd(), job_wrapper.job_id)
+ jt.outputPath = ":%s" % ofile
+ jt.errorPath = ":%s" % efile
+ native_spec = self.get_native_spec( runner_url )
+ if native_spec is not None:
+ jt.nativeSpecification = native_spec
+
+ script = drm_template % (job_wrapper.galaxy_lib_dir, os.path.abspath( job_wrapper.working_directory ), command_line)
+ if self.app.config.set_metadata_externally:
+ script += "cd %s\n" % os.path.abspath( os.getcwd() )
+ script += "%s\n" % job_wrapper.setup_external_metadata( exec_dir = os.path.abspath( os.getcwd() ),
+ tmp_dir = self.app.config.new_file_path,
+ dataset_files_path = self.app.model.Dataset.file_path,
+ output_fnames = job_wrapper.get_output_fnames(),
+ set_extension = False,
+ kwds = { 'overwrite' : False } ) #we don't want to overwrite metadata that was copied over in init_meta(), as per established behavior
+ fh = file( jt.remoteCommand, "w" )
+ fh.write( script )
+ fh.close()
+ os.chmod( jt.remoteCommand, 0750 )
+
+ # job was deleted while we were preparing it
+ if job_wrapper.get_state() == model.Job.states.DELETED:
+ log.debug( "Job %s deleted by user before it entered the queue" % job_wrapper.job_id )
+ self.cleanup( ( ofile, efile, jt.remoteCommand ) )
+ job_wrapper.cleanup()
+ return
+
+ galaxy_job_id = job_wrapper.job_id
+ log.debug("(%s) submitting file %s" % ( galaxy_job_id, jt.remoteCommand ) )
+ log.debug("(%s) command is: %s" % ( galaxy_job_id, command_line ) )
+ # runJob will raise if there's a submit problem
+ job_id = self.ds.runJob(jt)
+ log.info("(%s) queued as %s" % ( galaxy_job_id, job_id ) )
+
+ # store runner information for tracking if Galaxy restarts
+ job_wrapper.set_runner( runner_url, job_id )
+
+ # Store DRM related state information for job
+ drm_job_state = DRMAAJobState()
+ drm_job_state.job_wrapper = job_wrapper
+ drm_job_state.job_id = job_id
+ drm_job_state.ofile = ofile
+ drm_job_state.efile = efile
+ drm_job_state.job_file = jt.remoteCommand
+ drm_job_state.old_state = 'new'
+ drm_job_state.running = False
+ drm_job_state.runner_url = runner_url
+
+ # delete the job template
+ self.ds.deleteJobTemplate( jt )
+
+ # Add to our 'queue' of jobs to monitor
+ self.monitor_queue.put( drm_job_state )
+
+ def monitor( self ):
+ """
+ Watches jobs currently in the PBS queue and deals with state changes
+ (queued to running) and job completion
+ """
+ while 1:
+ # Take any new watched jobs and put them on the monitor list
+ try:
+ while 1:
+ drm_job_state = self.monitor_queue.get_nowait()
+ if drm_job_state is self.STOP_SIGNAL:
+ # TODO: This is where any cleanup would occur
+ self.ds.exit()
+ return
+ self.watched.append( drm_job_state )
+ except Empty:
+ pass
+ # Iterate over the list of watched jobs and check state
+ self.check_watched_items()
+ # Sleep a bit before the next state check
+ time.sleep( 1 )
+
+ def check_watched_items( self ):
+ """
+ Called by the monitor thread to look at each watched job and deal
+ with state changes.
+ """
+ new_watched = []
+ for drm_job_state in self.watched:
+ job_id = drm_job_state.job_id
+ galaxy_job_id = drm_job_state.job_wrapper.job_id
+ old_state = drm_job_state.old_state
+ try:
+ state = self.ds.jobStatus( job_id )
+ except drmaa.InvalidJobException:
+ # we should only get here if an orphaned job was put into the queue at app startup
+ log.debug("(%s/%s) job left DRM queue" % ( galaxy_job_id, job_id ) )
+ self.work_queue.put( ( 'finish', drm_job_state ) )
+ continue
+ except Exception, e:
+ # so we don't kill the monitor thread
+ log.exception("(%s/%s) Unable to check job status" % ( galaxy_job_id, job_id ) )
+ log.warning("(%s/%s) job will now be errored" % ( galaxy_job_id, job_id ) )
+ drm_job_state.fail_message = "Cluster could not complete job"
+ self.work_queue.put( ( 'fail', drm_job_state ) )
+ continue
+ if state != old_state:
+ log.debug("(%s/%s) state change: %s" % ( galaxy_job_id, job_id, drmaa_state[state] ) )
+ if state == drmaa.JobState.RUNNING and not drm_job_state.running:
+ drm_job_state.running = True
+ drm_job_state.job_wrapper.change_state( model.Job.states.RUNNING )
+ if state in ( drmaa.JobState.DONE, drmaa.JobState.FAILED ):
+ self.work_queue.put( ( 'finish', drm_job_state ) )
+ continue
+ drm_job_state.old_state = state
+ new_watched.append( drm_job_state )
+ # Replace the watch list with the updated version
+ self.watched = new_watched
+
+ def finish_job( self, drm_job_state ):
+ """
+ Get the output/error for a finished job, pass to `job_wrapper.finish`
+ and cleanup all the DRM temporary files.
+ """
+ ofile = drm_job_state.ofile
+ efile = drm_job_state.efile
+ job_file = drm_job_state.job_file
+ # collect the output
+ try:
+ ofh = file(ofile, "r")
+ efh = file(efile, "r")
+ stdout = ofh.read()
+ stderr = efh.read()
+ except:
+ stdout = ''
+ stderr = 'Job output not returned from cluster'
+ log.debug(stderr)
+
+ try:
+ drm_job_state.job_wrapper.finish( stdout, stderr )
+ except:
+ log.exception("Job wrapper finish method failed")
+
+ # clean up the drm files
+ self.cleanup( ( ofile, efile, job_file ) )
+
+ def fail_job( self, drm_job_state ):
+ """
+ Seperated out so we can use the worker threads for it.
+ """
+ self.stop_job( self.sa_session.query( self.app.model.Job ).get( drm_job_state.job_wrapper.job_id ) )
+ drm_job_state.job_wrapper.fail( drm_job_state.fail_message )
+ self.cleanup( ( drm_job_state.ofile, drm_job_state.efile, drm_job_state.job_file ) )
+
+ def cleanup( self, files ):
+ if not asbool( self.app.config.get( 'debug', False ) ):
+ for file in files:
+ if os.access( file, os.R_OK ):
+ os.unlink( file )
+
+ def put( self, job_wrapper ):
+ """Add a job to the queue (by job identifier)"""
+ # Change to queued state before handing to worker thread so the runner won't pick it up again
+ job_wrapper.change_state( model.Job.states.QUEUED )
+ self.work_queue.put( ( 'queue', job_wrapper ) )
+
+ def shutdown( self ):
+ """Attempts to gracefully shut down the monitor thread"""
+ log.info( "sending stop signal to worker threads" )
+ self.monitor_queue.put( self.STOP_SIGNAL )
+ for i in range( len( self.work_threads ) ):
+ self.work_queue.put( ( self.STOP_SIGNAL, None ) )
+ log.info( "drmaa job runner stopped" )
+
+ def stop_job( self, job ):
+ """Attempts to delete a job from the DRM queue"""
+ try:
+ self.ds.control( job.job_runner_external_id, drmaa.JobControlAction.TERMINATE )
+ log.debug( "(%s/%s) Removed from DRM queue at user's request" % ( job.id, job.job_runner_external_id ) )
+ except drmaa.InvalidJobException:
+ log.debug( "(%s/%s) User killed running job, but it was already dead" % ( job.id, job.job_runner_external_id ) )
+ except Exception, e:
+ log.debug( "(%s/%s) User killed running job, but error encountered removing from DRM queue: %s" % ( job.id, job.job_runner_external_id, e ) )
+
+ def recover( self, job, job_wrapper ):
+ """Recovers jobs stuck in the queued/running state when Galaxy started"""
+ drm_job_state = DRMAAJobState()
+ drm_job_state.ofile = "%s/database/pbs/%s.o" % (os.getcwd(), job.id)
+ drm_job_state.efile = "%s/database/pbs/%s.e" % (os.getcwd(), job.id)
+ drm_job_state.job_file = "%s/database/pbs/galaxy_%s.sh" % (os.getcwd(), job.id)
+ drm_job_state.job_id = str( job.job_runner_external_id )
+ drm_job_state.runner_url = job_wrapper.tool.job_runner
+ job_wrapper.command_line = job.command_line
+ drm_job_state.job_wrapper = job_wrapper
+ if job.state == model.Job.states.RUNNING:
+ log.debug( "(%s/%s) is still in running state, adding to the DRM queue" % ( job.id, job.job_runner_external_id ) )
+ drm_job_state.old_state = drmaa.JobState.RUNNING
+ drm_job_state.running = True
+ self.monitor_queue.put( drm_job_state )
+ elif job.state == model.Job.states.QUEUED:
+ log.debug( "(%s/%s) is still in DRM queued state, adding to the DRM queue" % ( job.id, job.job_runner_external_id ) )
+ drm_job_state.old_state = drmaa.JobState.QUEUED_ACTIVE
+ drm_job_state.running = False
+ self.monitor_queue.put( drm_job_state )
--- a/lib/galaxy/eggs/__init__.py
+++ b/lib/galaxy/eggs/__init__.py
@@ -314,6 +314,7 @@ class GalaxyConfig( object ):
return { "psycopg2": lambda: self.config.get( "app:main", "database_connection" ).startswith( "postgres://" ),
"MySQL_python": lambda: self.config.get( "app:main", "database_connection" ).startswith( "mysql://" ),
"DRMAA_python": lambda: "sge" in self.config.get( "app:main", "start_job_runners" ).split(","),
+ "drmaa": lambda: ( "drmaa" in self.config.get( "app:main", "start_job_runners" ).split(",") ) and sys.version_info[:2] >= ( 2, 5 ),
"pbs_python": lambda: "pbs" in self.config.get( "app:main", "start_job_runners" ).split(","),
"threadframe": lambda: self.config.get( "app:main", "use_heartbeat" ),
"guppy": lambda: self.config.get( "app:main", "use_memdump" ),
1
0

galaxy-dist commit 34ee69d9ba93: Fixes and code cleanup for the Galaxy tool shed. Fixes include improved mappers and more optimal methods for defining the latest version of a tool and the latest version of tools by state.
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Greg Von Kuster <greg(a)bx.psu.edu>
# Date 1280346733 14400
# Node ID 34ee69d9ba931b99a935b4ba7b38288160d68d31
# Parent 00672de098618096b9a11f7ad4983eb1c4faa32a
Fixes and code cleanup for the Galaxy tool shed. Fixes include improved mappers and more optimal methods for defining the latest version of a tool and the latest version of tools by state.
--- a/lib/galaxy/webapps/community/controllers/common.py
+++ b/lib/galaxy/webapps/community/controllers/common.py
@@ -96,7 +96,7 @@ class ToolListGrid( grids.Grid ):
ToolCategoryColumn( "Category",
key="category",
model_class=model.Category,
- visible=False ),
+ visible=False )
]
columns.append( grids.MulticolFilterColumn( "Search",
cols_to_filter=[ columns[0], columns[1], columns[2] ],
@@ -123,6 +123,14 @@ class CategoryListGrid( grids.Grid ):
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:
+ viewable_tools += 1
+ return viewable_tools
+ return 0
# Grid definition
webapp = "community"
@@ -148,13 +156,17 @@ class CategoryListGrid( grids.Grid ):
grids.DeletedColumn( "Deleted",
key="deleted",
visible=False,
- filterable="advanced" )
+ filterable="advanced" ),
+ ToolsColumn( "Tools",
+ model_class=model.Tool,
+ attach_popup=False )
]
columns.append( grids.MulticolFilterColumn( "Search",
cols_to_filter=[ columns[0], columns[1] ],
key="free-text-search",
visible=False,
filterable="standard" ) )
+
# Override these
global_actions = []
operations = []
@@ -240,7 +252,7 @@ class CommonController( BaseController )
out_categories.append( ( category.id, category.name ) )
if tool.is_rejected:
# Include the comments regarding the reason for rejection
- reason_for_rejection = get_most_recent_event( tool ).comment
+ reason_for_rejection = tool.latest_event.comment
else:
reason_for_rejection = ''
can_approve_or_reject = trans.app.security_agent.can_approve_or_reject( trans.user, trans.user_is_admin(), cntrller, tool )
@@ -294,7 +306,7 @@ class CommonController( BaseController )
tool_file_contents = tarfile.open( tool.file_name, 'r' ).getnames()
if tool.is_rejected:
# Include the comments regarding the reason for rejection
- reason_for_rejection = get_most_recent_event( tool ).comment
+ reason_for_rejection = tool.latest_event.comment
else:
reason_for_rejection = ''
return trans.fill_template( '/webapps/community/tool/view_tool.mako',
@@ -429,15 +441,15 @@ class CommonController( BaseController )
def get_versions( item ):
"""Get all versions of item"""
- versions = [item]
+ versions = [ item ]
this_item = item
while item.newer_version:
versions.insert( 0, item.newer_version )
item = item.newer_version
item = this_item
while item.older_version:
- versions.append( item.older_version[0] )
- item = item.older_version[0]
+ versions.append( item.older_version[ 0 ] )
+ item = item.older_version[ 0 ]
return versions
def get_categories( trans ):
"""Get all categories from the database"""
@@ -450,22 +462,21 @@ def get_category( trans, id ):
def get_tool( trans, id ):
"""Get a tool from the database"""
return trans.sa_session.query( trans.model.Tool ).get( trans.app.security.decode_id( id ) )
-def get_tools( trans ):
+def get_latest_versions_of_tools( trans ):
"""Get only the latest version of each tool from the database"""
return trans.sa_session.query( trans.model.Tool ) \
.filter( trans.model.Tool.newer_version_id == None ) \
.order_by( trans.model.Tool.name )
+def get_approved_tools( trans ):
+ """Get the tools from the database whose state is APPROVED"""
+ approved_tools = []
+ for tool in get_latest_versions_of_tools( trans ):
+ if tool.state == trans.model.Tool.states.APPROVED:
+ approved_tools.append( tool )
+ return approved_tools
def get_event( trans, id ):
"""Get an event from the databse"""
return trans.sa_session.query( trans.model.Event ).get( trans.security.decode_id( id ) )
-def get_most_recent_event( item ):
- """Get the most recent event for item"""
- if item.events:
- # Sort the events in ascending order by update_time
- events = model.sort_by_attr( [ item_event_assoc.event for item_event_assoc in item.events ], 'update_time' )
- # Get the last event that occurred
- return events[-1]
- return None
def get_user( trans, id ):
"""Get a user from the database"""
return trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( id ) )
--- a/templates/webapps/community/index.mako
+++ b/templates/webapps/community/index.mako
@@ -81,7 +81,7 @@
<div class="toolSectionBody"><div class="toolSectionBg"><div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_categories', webapp='community' )}">Browse by category</a></div>
- <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_tools', webapp='community' )}">Browse all tools</a></div>
+ <div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_tools', operation='approved_tools', webapp='community' )}">Browse all tools</a></div>
%if trans.user:
<div class="toolTitle"><a target="galaxy_main" href="${h.url_for( controller='tool', action='browse_tools', operation='my_tools', webapp='community' )}">Browse your tools</a></div>
%endif
--- a/lib/galaxy/webapps/community/model/__init__.py
+++ b/lib/galaxy/webapps/community/model/__init__.py
@@ -10,7 +10,6 @@ from galaxy import util
from galaxy.util.hash_util import *
from galaxy.web.form_builder import *
log = logging.getLogger( __name__ )
-from sqlalchemy.orm import object_session
class User( object ):
def __init__( self, email=None, password=None ):
@@ -137,22 +136,17 @@ class Tool( object ):
self.suite = datatype_bunch.suite
@property
def state( self ):
+ latest_event = self.latest_event
+ if latest_event:
+ return latest_event.state
+ return None
+ @property
+ def latest_event( self ):
if self.events:
- # Sort the events in ascending order by update_time
- events = sort_by_attr( [ tca.event for tca in self.events ], 'update_time' )
- # Get the last event that occurred
- return events[-1].state
+ events = [ tea.event for tea in self.events ]
+ # Get the last event that occurred ( events mapper is sorted descending )
+ return events[0]
return None
- def last_comment( self ):
- if self.events:
- # Sort the events in ascending order by update_time
- events = sort_by_attr( [ tca.event for tca in self.events ], 'update_time' )
- # Get the last event that occurred
- if events[-1].comment:
- return events[-1].comment
- else:
- return ''
- return 'No comment'
# Tool states
@property
def is_new( self ):
--- a/lib/galaxy/webapps/community/model/mapping.py
+++ b/lib/galaxy/webapps/community/model/mapping.py
@@ -14,8 +14,6 @@ from galaxy.model.orm.ext.assignmapper i
from galaxy.model.custom_types import *
from galaxy.util.bunch import Bunch
from galaxy.webapps.community.security import CommunityRBACAgent
-from sqlalchemy.orm.collections import attribute_mapped_collection
-from sqlalchemy.ext.associationproxy import association_proxy
metadata = MetaData()
context = Session = scoped_session( sessionmaker( autoflush=False, autocommit=True ) )
@@ -216,7 +214,12 @@ assign_mapper( context, ToolAnnotationAs
assign_mapper( context, Tool, Tool.table,
properties = dict(
categories=relation( ToolCategoryAssociation ),
- events=relation( ToolEventAssociation ),
+ events=relation( ToolEventAssociation, secondary=Event.table,
+ primaryjoin=( Tool.table.c.id==ToolEventAssociation.table.c.tool_id ),
+ secondaryjoin=( ToolEventAssociation.table.c.event_id==Event.table.c.id ),
+ order_by=desc( Event.table.c.update_time ),
+ viewonly=True,
+ uselist=True ),
user=relation( User.mapper ),
older_version=relation(
Tool,
--- a/lib/galaxy/webapps/community/controllers/tool.py
+++ b/lib/galaxy/webapps/community/controllers/tool.py
@@ -8,45 +8,55 @@ from common import *
log = logging.getLogger( __name__ )
+class StateColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, tool ):
+ state = tool.state
+ if state == trans.model.Tool.states.APPROVED:
+ state_color = 'ok'
+ elif state == trans.model.Tool.states.REJECTED:
+ state_color = 'error'
+ elif state == trans.model.Tool.states.ARCHIVED:
+ state_color = 'upload'
+ else:
+ state_color = state
+ return '<div class="count-box state-color-%s">%s</div>' % ( state_color, state )
+class ToolStateColumn( grids.StateColumn ):
+ def filter( self, trans, user, query, column_filter ):
+ """Modify query to filter self.model_class by state."""
+ if column_filter == "All":
+ pass
+ elif column_filter in [ v for k, v in self.model_class.states.items() ]:
+ # Get all of the latest Events associated with the current version of each tool
+ latest_event_id_for_current_versions_of_tools = [ tool.latest_event.id for tool in get_latest_versions_of_tools( trans ) ]
+ # Filter query by the latest state for the current version of each tool
+ return query.filter( and_( model.Event.table.c.state == column_filter,
+ model.Event.table.c.id.in_( latest_event_id_for_current_versions_of_tools ) ) )
+ return query
+
class ApprovedToolListGrid( ToolListGrid ):
- def apply_query_filter( self, trans, query, **kwargs ):
- return query.filter( model.Event.table.c.state == 'approved' )
-
-class MyToolsListGrid( ToolListGrid ):
- class StateColumn( grids.TextColumn ):
- def get_value( self, trans, grid, tool ):
- state = tool.state
- if state == 'approved':
- state_color = 'ok'
- elif state == 'rejected':
- state_color = 'error'
- elif state == 'archived':
- state_color = 'upload'
- else:
- state_color = state
- return '<div class="count-box state-color-%s">%s</div>' % ( state_color, state )
- class ToolStateColumn( grids.StateColumn ):
- def filter( self, trans, user, query, column_filter ):
- """Modify query to filter self.model_class by state."""
- if column_filter == "All":
- pass
- elif column_filter in [ v for k, v in self.model_class.states.items() ]:
- # Get all of the latest ToolEventAssociation ids
- tea_ids = [ tea_id_tup[0] for tea_id_tup in trans.sa_session.query( func.max( model.ToolEventAssociation.table.c.id ) ) \
- .group_by( model.ToolEventAssociation.table.c.tool_id ) ]
- # Get all of the Event ids associated with the latest ToolEventAssociation ids
- event_ids = [ event_id_tup[0] for event_id_tup in trans.sa_session.query( model.ToolEventAssociation.table.c.event_id ) \
- .filter( model.ToolEventAssociation.table.c.id.in_( tea_ids ) ) ]
- # Filter our query by state and event ids
- return query.filter( and_( model.Event.table.c.state == column_filter,
- model.Event.table.c.id.in_( event_ids ) ) )
- return query
-
columns = [ col for col in ToolListGrid.columns ]
columns.append(
StateColumn( "Status",
model_class=model.Tool,
link=( lambda item: dict( operation="tools_by_state", id=item.id, webapp="community" ) ),
+ visible=False,
+ attach_popup=False )
+ )
+ columns.append(
+ ToolStateColumn( "State",
+ key="state",
+ model_class=model.Tool,
+ visible=False,
+ filterable="advanced" )
+ )
+
+class MyToolsListGrid( ApprovedToolListGrid ):
+ columns = [ col for col in ToolListGrid.columns ]
+ columns.append(
+ StateColumn( "Status",
+ model_class=model.Tool,
+ link=( lambda item: dict( operation="tools_by_state", id=item.id, webapp="community" ) ),
+ visible=True,
attach_popup=False )
)
columns.append(
@@ -58,6 +68,10 @@ class MyToolsListGrid( ToolListGrid ):
)
class ToolCategoryListGrid( CategoryListGrid ):
+ """
+ Replaces the tools column in the Category grid with a similar column,
+ but displaying the number of APPROVED tools in the category.
+ """
class ToolsColumn( grids.TextColumn ):
def get_value( self, trans, grid, category ):
if category.tools:
@@ -69,7 +83,10 @@ class ToolCategoryListGrid( CategoryList
return viewable_tools
return 0
- columns = [ col for col in CategoryListGrid.columns ]
+ columns = []
+ for col in CategoryListGrid.columns:
+ if not isinstance( col, CategoryListGrid.ToolsColumn ):
+ columns.append( col )
columns.append(
ToolsColumn( "Tools",
model_class=model.Tool,
@@ -146,6 +163,14 @@ class ToolController( BaseController ):
del kwd[ k ]
kwd[ 'f-email' ] = trans.user.email
return self.my_tools_list_grid( trans, **kwd )
+ elif operation == "approved_tools":
+ # Eliminate the current filters if any exist.
+ for k, v in kwd.items():
+ if k.startswith( 'f-' ):
+ del kwd[ k ]
+ # Make sure only the latest version of a tool whose state is APPROVED are displayed.
+ kwd[ 'f-state' ] = trans.model.Tool.states.APPROVED
+ return self.tool_list_grid( trans, **kwd )
elif operation == "tools_by_category":
# Eliminate the current filters if any exist.
for k, v in kwd.items():
@@ -154,6 +179,8 @@ class ToolController( BaseController ):
category_id = kwd.get( 'id', None )
category = get_category( trans, category_id )
kwd[ 'f-category' ] = category.name
+ # Make sure only the latest version of a tool whose state is APPROVED are displayed.
+ kwd[ 'f-state' ] = trans.model.Tool.states.APPROVED
# Render the list view
return self.tool_list_grid( trans, **kwd )
@web.expose
--- a/lib/galaxy/webapps/community/datatypes/__init__.py
+++ b/lib/galaxy/webapps/community/datatypes/__init__.py
@@ -69,7 +69,7 @@ class Tool( object ):
raise DatatypeVerificationError( 'The archive is not a readable tar file.' )
if not xml_files:
# Make sure we're not uploading a tool suite
- if filter( lambda x: x.lower() == 'suite_config.xml', tar.getnames() ):
+ if filter( lambda x: x.lower().find( 'suite_config.xml' ) >= 0, tar.getnames() ):
raise DatatypeVerificationError( 'The archive includes a suite_config.xml file, so set the upload type to "Tool Suite".' )
xml_files = filter( lambda x: x.lower().endswith( '.xml' ), tar.getnames() )
if not xml_files:
--- a/lib/galaxy/webapps/community/controllers/admin.py
+++ b/lib/galaxy/webapps/community/controllers/admin.py
@@ -2,7 +2,7 @@ from galaxy.web.base.controller import *
from galaxy.webapps.community import model
from galaxy.model.orm import *
from galaxy.web.framework.helpers import time_ago, iff, grids
-from common import ToolListGrid, CategoryListGrid, get_category, get_tools, get_event, get_tool, get_versions
+from common import ToolListGrid, CategoryListGrid, get_category, get_event, get_tool, get_versions
import logging
log = logging.getLogger( __name__ )
@@ -791,38 +791,3 @@ class AdminController( BaseController, A
action='manage_categories',
message=util.sanitize_text( message ),
status='done' ) )
-
-## ---- Utility methods -------------------------------------------------------
-
-def get_tools_by_state( trans, state ):
- # TODO: write this as a query using eagerload - will be much faster.
- ids = []
- if state == trans.model.Tool.states.NEW:
- for tool in get_tools( trans ):
- if tool.is_new:
- ids.append( tool.id )
- elif state == trans.model.Tool.states.ERROR:
- for tool in get_tools( trans ):
- if tool.is_error:
- ids.append( tool.id )
- elif state == trans.model.Tool.states.DELETED:
- for tool in get_tools( trans ):
- if tool.is_deleted:
- ids.append( tool.id )
- elif state == trans.model.Tool.states.WAITING:
- for tool in get_tools( trans ):
- if tool.is_waiting:
- ids.append( tool.id )
- elif state == trans.model.Tool.states.APPROVED:
- for tool in get_tools( trans ):
- if tool.is_approved:
- ids.append( tool.id )
- elif state == trans.model.Tool.states.REJECTED:
- for tool in get_tools( trans ):
- if tool.is_rejected:
- ids.append( tool.id )
- elif state == trans.model.Tool.states.ARCHIVED:
- for tool in get_tools( trans ):
- if tool.is_archived:
- ids.append( tool.id )
- return ids
1
0

galaxy-dist commit 00672de09861: Bug fix for calculating viewport when displaying interval files at Ensembl browser.
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dan Blankenberg <dan(a)bx.psu.edu>
# Date 1280343810 14400
# Node ID 00672de098618096b9a11f7ad4983eb1c4faa32a
# Parent 453e4edfab90db13efd08af64eeb3e7c64c22880
Bug fix for calculating viewport when displaying interval files at Ensembl browser.
--- a/display_applications/ensembl/ensembl_interval_as_bed.xml
+++ b/display_applications/ensembl/ensembl_interval_as_bed.xml
@@ -31,7 +31,7 @@
#if $chrom.startswith( 'chr' ):
#set $chrom = $chrom[3:]
#end if
-${chrom}:${start + 1}-${end}
+${chrom}:${int( start ) + 1}-${end}
#else:
##default view is of '1'
1
@@ -71,7 +71,7 @@ 1
#if $chrom.startswith( 'chr' ):
#set $chrom = $chrom[3:]
#end if
- &chr=${chrom}&start=${start + 1}&end=${end}
+ &chr=${chrom}&start=${int( start ) + 1}&end=${end}
#end if
</param></dynamic_links>
1
0

galaxy-dist commit 453e4edfab90: Have check_galaxy.py script get twill from galaxy.eggs instead of directly from the (defunct) eggs/py-(version) directory
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1280336236 14400
# Node ID 453e4edfab90db13efd08af64eeb3e7c64c22880
# Parent 04dd5e51cf19b8c85e824457b045cc6af945dd4e
Have check_galaxy.py script get twill from galaxy.eggs instead of directly from the (defunct) eggs/py-(version) directory
--- a/scripts/check_galaxy.py
+++ b/scripts/check_galaxy.py
@@ -90,9 +90,8 @@ except:
# find/import twill
lib_dir = os.path.join( scripts_dir, "..", "lib" )
-eggs_dir = os.path.join( scripts_dir, "..", "eggs", "py%s-noplatform" %sys.version[:3] )
sys.path.append( lib_dir )
-sys.path.append( eggs_dir )
+from galaxy import eggs
import pkg_resources
pkg_resources.require( "twill" )
import twill
1
0

galaxy-dist commit 04dd5e51cf19: Display rerun button for history items when dataset is empty.
by commits-noreply@bitbucket.org 30 Jul '10
by commits-noreply@bitbucket.org 30 Jul '10
30 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dan Blankenberg <dan(a)bx.psu.edu>
# Date 1280327687 14400
# Node ID 04dd5e51cf19b8c85e824457b045cc6af945dd4e
# Parent 5401c7d2e13bbd214e1a424e71c4fbcbac7cf919
Display rerun button for history items when dataset is empty.
--- a/templates/root/history_common.mako
+++ b/templates/root/history_common.mako
@@ -158,6 +158,8 @@
<a target="${link_app.url.get( 'target_frame', '_blank' )}" href="${link_app.get_display_url( data, trans )}">${_(link_app.name)}</a>
%endfor
%endfor
+ %elif for_editing:
+ <a href="${h.url_for( controller='tool_runner', action='rerun', id=data.id )}" target="galaxy_main" title="Run this job again" class="icon-button arrow-circle tooltip"></a>
%endif
</div>
1
0