galaxy-commits
Threads by month
- ----- 2025 -----
- 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 122aaabe2c7e: Workaround Issue 397 using 'if' in Cheetah template to avoid hidden parameters
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User peterjc <p.j.a.cock(a)googlemail.com>
# Date 1286965628 -3600
# Node ID 122aaabe2c7eb48726f65233370926b838b6b444
# Parent 10d0ffe5b7e22b2d122945ab5b7fa951a18c6e4a
Workaround Issue 397 using 'if' in Cheetah template to avoid hidden parameters
--- a/tools/ncbi_blast_plus/ncbi_blastn_wrapper.xml
+++ b/tools/ncbi_blast_plus/ncbi_blastn_wrapper.xml
@@ -12,11 +12,12 @@ blastn
#end if
-task $blast_type
-evalue $evalue_cutoff
-$adv_opts.filter_query
-$adv_opts.strand
-out $output1
$out_format
-num_threads 8
+#if $adv_opts.adv_opts_selector=="advanced":
+$adv_opts.filter_query
+$adv_opts.strand
## Need int(str(...)) because $adv_opts.max_hits is an InputValueWrapper object not a string
## Note -max_target_seqs overrides -num_descriptions and -num_alignments
#if (str($adv_opts.max_hits) and int(str($adv_opts.max_hits)) > 0):
@@ -26,6 +27,8 @@ blastn
-word_size $adv_opts.word_size
#end if
$adv_opts.ungapped
+## End of advanced options:
+#end if
</command><inputs><param name="query" type="data" format="fasta" label="Nucleotide query sequence(s)"/>
@@ -78,13 +81,7 @@ blastn
<option value="basic" selected="True">Hide Advanced Options</option><option value="advanced">Show Advanced Options</option></param>
- <when value="basic">
- <param name="filter_query" type="hidden" value="" />
- <param name="strand" type="hidden" value="" />
- <param name="max_hits" type="hidden" value="" />
- <param name="word_size" type="hidden" value="" />
- <param name="ungapped" type="hidden" value="" />
- </when>
+ <when value="basic" /><when value="advanced"><!-- Could use a select (yes, no, other) where other allows setting 'level window linker' --><param name="filter_query" type="boolean" label="Filter out low complexity regions (with DUST)" truevalue="-dust yes" falsevalue="-dust no" checked="true" />
--- a/tools/ncbi_blast_plus/ncbi_blastp_wrapper.xml
+++ b/tools/ncbi_blast_plus/ncbi_blastp_wrapper.xml
@@ -12,11 +12,12 @@ blastp
#end if
-task $blast_type
-evalue $evalue_cutoff
-$adv_opts.filter_query
-$adv_opts.matrix
-out $output1
$out_format
-num_threads 8
+#if $adv_opts.adv_opts_selector=="advanced":
+$adv_opts.filter_query
+$adv_opts.matrix
## Need int(str(...)) because $adv_opts.max_hits is an InputValueWrapper object not a string
## Note -max_target_seqs overrides -num_descriptions and -num_alignments
#if (str($adv_opts.max_hits) and int(str($adv_opts.max_hits)) > 0):
@@ -27,6 +28,8 @@ blastp
#end if
##Ungapped disabled for now - see comments below
##$adv_opts.ungapped
+## End of advanced options:
+#end if
</command><inputs><param name="query" type="data" format="fasta" label="Protein query sequence(s)"/>
@@ -73,16 +76,7 @@ blastp
<option value="basic" selected="True">Hide Advanced Options</option><option value="advanced">Show Advanced Options</option></param>
- <when value="basic">
- <param name="filter_query" type="hidden" value="" />
- <param name="matrix" type="hidden" value="" />
- <param name="max_hits" type="hidden" value="" />
- <param name="word_size" type="hidden" value="" />
- <!--
- Ungapped disabled for now, see comments below
- <param name="ungapped" type="hidden" value="" />
- -->
- </when>
+ <when value="basic" /><when value="advanced"><!-- Could use a select (yes, no, other) where other allows setting 'window locut hicut' --><param name="filter_query" type="boolean" label="Filter out low complexity regions (with SEG)" truevalue="-seg yes" falsevalue="-seg no" checked="true" />
--- a/tools/ncbi_blast_plus/ncbi_tblastx_wrapper.xml
+++ b/tools/ncbi_blast_plus/ncbi_tblastx_wrapper.xml
@@ -11,12 +11,13 @@ tblastx
-subject "$db_opts.subject"
#end if
-evalue $evalue_cutoff
+-out $output1
+$out_format
+-num_threads 8
+#if $adv_opts.adv_opts_selector=="advanced":
$adv_opts.filter_query
$adv_opts.strand
$adv_opts.matrix
--out $output1
-$out_format
--num_threads 8
## Need int(str(...)) because $adv_opts.max_hits is an InputValueWrapper object not a string
## Note -max_target_seqs overrides -num_descriptions and -num_alignments
#if (str($adv_opts.max_hits) and int(str($adv_opts.max_hits)) > 0):
@@ -25,6 +26,8 @@ tblastx
#if (str($adv_opts.word_size) and int(str($adv_opts.word_size)) > 0):
-word_size $adv_opts.word_size
#end if
+## End of advanced options:
+#end if
</command><inputs><param name="query" type="data" format="fasta" label="Nucleotide query sequence(s)"/>
@@ -67,13 +70,7 @@ tblastx
<option value="basic" selected="True">Hide Advanced Options</option><option value="advanced">Show Advanced Options</option></param>
- <when value="basic">
- <param name="filter_query" type="hidden" value="" />
- <param name="strand" type="hidden" value="" />
- <param name="matrix" type="hidden" value="" />
- <param name="max_hits" type="hidden" value="" />
- <param name="word_size" type="hidden" value="" />
- </when>
+ <when value="basic" /><when value="advanced"><!-- Could use a select (yes, no, other) where other allows setting 'window locut hicut' --><param name="filter_query" type="boolean" label="Filter out low complexity regions (with SEG)" truevalue="-seg yes" falsevalue="-seg no" checked="true" />
--- a/tools/ncbi_blast_plus/ncbi_blastx_wrapper.xml
+++ b/tools/ncbi_blast_plus/ncbi_blastx_wrapper.xml
@@ -11,12 +11,13 @@ blastx
-subject "$db_opts.subject"
#end if
-evalue $evalue_cutoff
+-out $output1
+$out_format
+-num_threads 8
+#if $adv_opts.adv_opts_selector=="advanced":
$adv_opts.filter_query
$adv_opts.strand
$adv_opts.matrix
--out $output1
-$out_format
--num_threads 8
## Need int(str(...)) because $adv_opts.max_hits is an InputValueWrapper object not a string
## Note -max_target_seqs overrides -num_descriptions and -num_alignments
#if (str($adv_opts.max_hits) and int(str($adv_opts.max_hits)) > 0):
@@ -26,6 +27,8 @@ blastx
-word_size $adv_opts.word_size
#end if
$adv_opts.ungapped
+## End of advanced options:
+#end if
</command><inputs><param name="query" type="data" format="fasta" label="Nucleotide query sequence(s)"/>
@@ -68,14 +71,7 @@ blastx
<option value="basic" selected="True">Hide Advanced Options</option><option value="advanced">Show Advanced Options</option></param>
- <when value="basic">
- <param name="filter_query" type="hidden" value="" />
- <param name="strand" type="hidden" value="" />
- <param name="matrix" type="hidden" value="" />
- <param name="max_hits" type="hidden" value="" />
- <param name="word_size" type="hidden" value="" />
- <param name="ungapped" type="hidden" value="" />
- </when>
+ <when value="basic" /><when value="advanced"><!-- Could use a select (yes, no, other) where other allows setting 'window locut hicut' --><param name="filter_query" type="boolean" label="Filter out low complexity regions (with SEG)" truevalue="-seg yes" falsevalue="-seg no" checked="true" />
--- a/tools/ncbi_blast_plus/ncbi_tblastn_wrapper.xml
+++ b/tools/ncbi_blast_plus/ncbi_tblastn_wrapper.xml
@@ -11,11 +11,12 @@ tblastn
-subject "$db_opts.subject"
#end if
-evalue $evalue_cutoff
-$adv_opts.filter_query
-$adv_opts.matrix
-out $output1
$out_format
-num_threads 8
+#if $adv_opts.adv_opts_selector=="advanced":
+$adv_opts.filter_query
+$adv_opts.matrix
## Need int(str(...)) because $adv_opts.max_hits is an InputValueWrapper object not a string
## Note -max_target_seqs overrides -num_descriptions and -num_alignments
#if (str($adv_opts.max_hits) and int(str($adv_opts.max_hits)) > 0):
@@ -26,6 +27,8 @@ tblastn
#end if
##Ungapped disabled for now - see comments below
##$adv_opts.ungapped
+## End of advanced options:
+#end if
</command><inputs><param name="query" type="data" format="fasta" label="Protein query sequence(s)"/>
@@ -68,16 +71,7 @@ tblastn
<option value="basic" selected="True">Hide Advanced Options</option><option value="advanced">Show Advanced Options</option></param>
- <when value="basic">
- <param name="filter_query" type="hidden" value="" />
- <param name="matrix" type="hidden" value="" />
- <param name="max_hits" type="hidden" value="" />
- <param name="word_size" type="hidden" value="" />
- <!--
- Ungapped disabled for now, see comments below
- <param name="ungapped" type="hidden" value="" />
- -->
- </when>
+ <when value="basic" /><when value="advanced"><!-- Could use a select (yes, no, other) where other allows setting 'window locut hicut' --><param name="filter_query" type="boolean" label="Filter out low complexity regions (with SEG)" truevalue="-seg yes" falsevalue="-seg no" checked="true" />
1
0
galaxy-dist commit dfc848840870: Various sample tracking bug fixes: samples no longer require bar codes, change all references to 'barcode' to be 'bar_code' since that is what the model uses, and mixing the 2 is not maintainable, enhance sample popup menu options, fix broken logic that handles bulk sample library and folderr changes.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '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 1289503442 18000
# Node ID dfc848840870fa9936a69e62322446fcb940fb40
# Parent ca23ea683d26df004e666ede82950c712e3ac637
Various sample tracking bug fixes: samples no longer require bar codes, change all references to 'barcode' to be 'bar_code' since that is what the model uses, and mixing the 2 is not maintainable, enhance sample popup menu options, fix broken logic that handles bulk sample library and folderr changes.
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -725,7 +725,7 @@ class RequestsCommon( BaseController, Us
search_type = params.get( 'search_type', '' )
request_states = util.listify( params.get( 'request_states', '' ) )
samples = []
- if search_type == 'barcode':
+ if search_type == 'bar_code':
samples = trans.sa_session.query( trans.model.Sample ) \
.filter( and_( trans.model.Sample.table.c.deleted==False,
func.lower( trans.model.Sample.table.c.bar_code ).like( "%" + search_string.lower() + "%" ) ) ) \
@@ -763,7 +763,7 @@ class RequestsCommon( BaseController, Us
display='checkboxes' )
# Build the search_type SelectField
selected_value = kwd.get( 'search_type', 'sample name' )
- types = [ 'sample name', 'barcode', 'dataset' ]
+ types = [ 'sample name', 'bar_code', 'dataset' ]
search_type = build_select_field( trans, types, 'self', 'search_type', selected_value=selected_value, refresh_on_change=False )
# Build the search_box TextField
search_box = TextField( 'search_box', 50, kwd.get('search_box', '' ) )
@@ -846,7 +846,7 @@ class RequestsCommon( BaseController, Us
# Append the new sample to the current list of samples for the request
displayable_sample_widgets.append( dict( id=None,
name=name,
- barcode='',
+ bar_code='',
library=None,
library_id=library_id,
folder=None,
@@ -999,7 +999,7 @@ class RequestsCommon( BaseController, Us
**kwd )
displayable_sample_widgets.append( dict( id=None,
name=row[0],
- barcode='',
+ bar_code='',
library=None,
folder=None,
library_select_field=library_select_field,
@@ -1029,7 +1029,10 @@ class RequestsCommon( BaseController, Us
editing_samples=False )
def __save_samples( self, trans, cntrller, request, samples, **kwd ):
# Here we handle saving all new samples added by the user as well as saving
- # changes to any subset of the request's samples.
+ # changes to any subset of the request's samples. A sample will not have an
+ # associated SampleState until the request is submitted, at which time the
+ # sample is automatically associated with the first SampleState configured by
+ # the admin for the request's RequestType.
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
@@ -1065,7 +1068,7 @@ class RequestsCommon( BaseController, Us
# Send the encoded sample_ids to update_sample_state.
# TODO: make changes necessary to just send the samples...
encoded_selected_sample_ids = self.__get_encoded_selected_sample_ids( trans, request, **kwd )
- # Make sure all samples have a unique barcode if the state is changing
+ # Make sure all samples have a unique bar_code if the state is changing
for sample_index in range( len( samples ) ):
current_sample = samples[ sample_index ]
if current_sample is None:
@@ -1073,13 +1076,15 @@ class RequestsCommon( BaseController, Us
# on which to perform the action.
continue
request_sample = request.samples[ sample_index ]
- bc_message = self.__validate_barcode( trans, request_sample, current_sample[ 'barcode' ] )
- if bc_message:
- #status = 'error'
- message += bc_message
- kwd[ 'message' ] = message
- del kwd[ 'save_samples_button' ]
- handle_error( **kwd )
+ bar_code = current_sample[ 'bar_code' ]
+ if bar_code:
+ # If the sample has a new bar_code, make sure it is unique.
+ bc_message = self.__validate_bar_code( trans, request_sample, bar_code )
+ if bc_message:
+ message += bc_message
+ kwd[ 'message' ] = message
+ del kwd[ 'save_samples_button' ]
+ handle_error( **kwd )
self.update_sample_state( trans, cntrller, encoded_selected_sample_ids, new_state, comment=sample_event_comment )
return trans.response.send_redirect( web.url_for( controller='requests_common',
cntrller=cntrller,
@@ -1088,18 +1093,22 @@ class RequestsCommon( BaseController, Us
elif sample_operation == 'Select data library and folder':
# TODO: fix the code so that the sample_operation_select_field does not use
# sample_0_library_id as it's name. it should use something like sample_operation_library_id
- # and sample_operation-folder_id because the name sample_0_library_id should belong to the
+ # and sample_operation_folder_id because the name sample_0_library_id should belong to the
# first sample since all other form field values are named like this. The library and folder
# are skewed to be named +1 resulting in the forced use of id_index everywhere...
library_id = params.get( 'sample_0_library_id', 'none' )
folder_id = params.get( 'sample_0_folder_id', 'none' )
library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ for sample_index in range( len( samples ) ):
+ current_sample = samples[ sample_index ]
+ current_sample[ 'library' ] = library
+ current_sample[ 'folder' ] = folder
self.__update_samples( trans, cntrller, request, samples, **kwd )
# Samples will not have an associated SampleState until the request is submitted, at which
# time all samples of the request will be set to the first SampleState configured for the
# request's RequestType defined by the admin.
if request.is_submitted:
- # See if all the samples' barcodes are in the same state, and if so send email if configured to.
+ # See if all the samples' bar_codes are in the same state, and if so send email if configured to.
common_state = request.samples_have_common_state
if common_state and common_state.id == request.type.states[1].id:
comment = "All samples of this request are in the (%s) sample state. " % common_state.name
@@ -1136,10 +1145,12 @@ class RequestsCommon( BaseController, Us
status=status,
message=message ) )
def __update_samples( self, trans, cntrller, request, sample_widgets, **kwd ):
- # Determine if the values in kwd require updating the request's samples. The list of
- # sample_widgets must have the same number of objects as request.samples, but some of
- # the objects can be None. Those that are not None correspond to samples selected by
- # the user for performing an action on multiple samples simultaneously.
+ # The list of sample_widgets must have the same number of objects as request.samples,
+ # but some of the objects can be None. Those that are not None correspond to samples
+ # selected by the user for performing an action on multiple samples simultaneously.
+ # The items in the sample_widgets list have already been populated with any changed
+ # param values (changed implies the value in kwd is different from the attribute value
+ # in the database) in kwd before this method is reached.
def handle_error( **kwd ):
kwd[ 'status' ] = 'error'
return trans.response.send_redirect( web.url_for( controller='requests_common',
@@ -1147,12 +1158,6 @@ class RequestsCommon( BaseController, Us
cntrller=cntrller,
**kwd ) )
params = util.Params( kwd )
- sample_operation = params.get( 'sample_operation', 'none' )
- if sample_operation != 'none':
- # These values will be in kwd if the user checked 1 or more checkboxes for performing this action
- # on a set of samples.
- library_id = params.get( 'sample_0_library_id', 'none' )
- folder_id = params.get( 'sample_0_folder_id', 'none' )
for index, sample_widget in enumerate( sample_widgets ):
if sample_widget is not None:
# sample_widget will be None if the user checked sample check boxes and selected an action
@@ -1161,21 +1166,16 @@ class RequestsCommon( BaseController, Us
# Get the sample's form values to see if they have changed.
form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
if sample.name != sample_widget[ 'name' ] or \
- sample.bar_code != sample_widget[ 'barcode' ] or \
+ sample.bar_code != sample_widget[ 'bar_code' ] or \
sample.library != sample_widget[ 'library' ] or \
sample.folder != sample_widget[ 'folder' ] or \
form_values.content != sample_widget[ 'field_values' ]:
# Information about this sample has been changed.
sample.name = sample_widget[ 'name' ]
- barcode = sample_widget[ 'barcode' ]
- # The bar_code field requires special handling because after a request is submitted, the
- # state of a sample cannot be changed without a bar_code associated with the sample. Bar
- # codes can only be added to a sample after the request is submitted. Also, a samples will
- # not have an associated SampleState until the request is submitted, at which time the sample
- # is automatically associated with the first SamplesState configured by the admin for the
- # request's RequestType.
- if barcode:
- bc_message = self.__validate_barcode( trans, sample, bar_code )
+ bar_code = sample_widget[ 'bar_code' ]
+ # If the sample has a new bar_code, make sure it is unique.
+ if bar_code:
+ bc_message = self.__validate_bar_code( trans, sample, bar_code )
if bc_message:
kwd[ 'message' ] = bc_message
del kwd[ 'save_samples_button' ]
@@ -1191,7 +1191,7 @@ class RequestsCommon( BaseController, Us
'Bar code associated with the sample' )
trans.sa_session.add( event )
trans.sa_session.flush()
- sample.bar_code = barcode
+ sample.bar_code = bar_code
sample.library = sample_widget[ 'library' ]
sample.folder = sample_widget[ 'folder' ]
form_values.content = sample_widget[ 'field_values' ]
@@ -1281,7 +1281,7 @@ class RequestsCommon( BaseController, Us
# Update the sample attributes from kwd
sample_id = None
name = util.restore_text( params.get( 'sample_%i_name' % index, sample.name ) )
- bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, sample.bar_code ) )
+ bar_code = util.restore_text( params.get( 'sample_%i_bar_code' % index, sample.bar_code ) )
library_id = util.restore_text( params.get( 'sample_%i_library_id' % id_index, '' ) )
if not library_id and sample.library:
library_id = trans.security.encode_id( sample.library.id )
@@ -1303,7 +1303,7 @@ class RequestsCommon( BaseController, Us
**kwd )
sample_widgets.append( dict( id=sample_id,
name=name,
- barcode=bar_code,
+ bar_code=bar_code,
library=library,
folder=folder,
field_values=field_values,
@@ -1317,7 +1317,7 @@ class RequestsCommon( BaseController, Us
if not name:
break
id_index = index + 1
- bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
+ bar_code = util.restore_text( params.get( 'sample_%i_bar_code' % index, '' ) )
library_id = util.restore_text( params.get( 'sample_%i_library_id' % id_index, '' ) )
folder_id = util.restore_text( params.get( 'sample_%i_folder_id' % id_index, '' ) )
library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
@@ -1334,7 +1334,7 @@ class RequestsCommon( BaseController, Us
**kwd )
sample_widgets.append( dict( id=None,
name=name,
- barcode=bar_code,
+ bar_code=bar_code,
library=library,
folder=folder,
field_values=field_values,
@@ -1492,31 +1492,24 @@ class RequestsCommon( BaseController, Us
action='edit_samples',
cntrller=cntrller,
**kwd ) )
- def __validate_barcode( self, trans, sample, barcode ):
+ def __validate_bar_code( self, trans, sample, bar_code ):
"""
- Makes sure that the barcode about to be assigned to a sample is globally unique.
- That is, barcodes must be unique across requests in Galaxy sample tracking.
+ Make sure that the bar_code about to be assigned to a sample is globally unique.
+ That is, bar_codes must be unique across requests in Galaxy sample tracking.
+ Bar codes are not required, but if used, they can only be added to a sample after
+ the request is submitted.
"""
message = ''
unique = True
for index in range( len( sample.request.samples ) ):
- # Check for empty bar code
- if not barcode.strip():
- if sample.state.id == sample.request.type.states[0].id:
- # The user has not yet filled in the barcode value, but the sample is
- # 'new', so all is well.
- break
- else:
- message = "Fill in the barcode for sample (%s) before changing it's state." % sample.name
- break
# TODO: Add a unique constraint to sample.bar_code table column
# Make sure bar code is unique
- for sample_with_barcode in trans.sa_session.query( trans.model.Sample ) \
- .filter( trans.model.Sample.table.c.bar_code == barcode ):
- if sample_with_barcode and sample_with_barcode.id != sample.id:
+ for sample_with_bar_code in trans.sa_session.query( trans.model.Sample ) \
+ .filter( trans.model.Sample.table.c.bar_code == bar_code ):
+ if sample_with_bar_code and sample_with_bar_code.id != sample.id:
message = '''The bar code (%s) associated with the sample (%s) belongs to another sample.
Bar codes must be unique across all samples, so use a different bar code
- for this sample.''' % ( barcode, sample.name )
+ for this sample.''' % ( bar_code, sample.name )
unique = False
break
if not unique:
--- a/templates/requests/common/common.mako
+++ b/templates/requests/common/common.mako
@@ -122,7 +122,7 @@
is_rejected = request.is_rejected
is_submitted = sample.request.is_submitted
is_unsubmitted = sample.request.is_unsubmitted
- can_delete_samples = not is_complete
+ can_delete_samples = editing_samples and request.samples and ( ( is_admin and not is_complete ) or is_unsubmitted )
display_checkboxes = editing_samples and ( is_complete or is_rejected or is_submitted )
display_bar_code = request.samples and ( is_complete or is_rejected or is_submitted )
display_datasets = request.samples and ( is_complete or is_submitted )
@@ -153,10 +153,10 @@
%if display_bar_code:
<td valign="top">
%if is_admin:
- <input type="text" name="sample_${sample_widget_index}_barcode" value="${sample_widget['barcode']}" size="10"/>
+ <input type="text" name="sample_${sample_widget_index}_bar_code" value="${sample_widget['bar_code']}" size="10"/>
%else:
- ${sample_widget['barcode']}
- <input type="hidden" name="sample_${sample_widget_index}_barcode" value="${sample_widget['barcode']}"/>
+ ${sample_widget['bar_code']}
+ <input type="hidden" name="sample_${sample_widget_index}_bar_code" value="${sample_widget['bar_code']}"/>
%endif
</td>
%endif
@@ -234,7 +234,7 @@
is_submitted = request.is_submitted
is_unsubmitted = request.is_unsubmitted
can_add_samples = request.is_unsubmitted
- can_delete_samples = editing_samples and request.samples and not is_complete
+ can_delete_samples = editing_samples and request.samples and ( ( is_admin and not is_complete ) or is_unsubmitted )
can_edit_samples = request.samples and ( is_admin or not is_complete )
can_select_datasets = is_admin and displayable_sample_widgets and ( is_submitted or is_complete )
can_transfer_datasets = is_admin and request.samples and not request.is_rejected
@@ -280,14 +280,14 @@
<tbody><% trans.sa_session.refresh( request ) %>
## displayable_sample_widgets is a dictionary whose keys are:
- ## id, name, barcode, library, folder, field_values, library_select_field, folder_select_field
+ ## id, name, bar_code, library, folder, field_values, library_select_field, folder_select_field
## A displayable_sample_widget will have an id == None if the widget's associated sample has not
## yet been saved (i.e., the use clicked the "Add sample" button but has not yet clicked the
## "Save" button.
%for sample_widget_index, sample_widget in enumerate( displayable_sample_widgets ):
<%
sample_widget_name = sample_widget[ 'name' ]
- sample_widget_barcode = sample_widget[ 'barcode' ]
+ sample_widget_bar_code = sample_widget[ 'bar_code' ]
sample_widget_library = sample_widget[ 'library' ]
if sample_widget_library:
if cntrller == 'requests':
@@ -309,7 +309,12 @@
<td>
%if sample.state and ( can_select_datasets or can_transfer_datasets ):
## A sample will have a state only after the request has been submitted.
- <% encoded_id = trans.security.encode_id( sample.id ) %>
+ <%
+ encoded_id = trans.security.encode_id( sample.id )
+ transferred_dataset_files = sample.transferred_dataset_files
+ if not transferred_dataset_files:
+ transferred_dataset_files = []
+ %><div style="float: left; margin-left: 2px;" class="menubutton split popup" id="sample-${sample.id}-popup">
${sample.name}
</div>
@@ -317,8 +322,10 @@
%if can_select_datasets:
<li><a class="action-button" href="${h.url_for( controller='requests_admin', action='select_datasets_to_transfer', request_id=trans.security.encode_id( request.id ), sample_id=trans.security.encode_id( sample.id ) )}">Select datasets to transfer</a></li>
%endif
- %if sample.untransferred_dataset_files:
- <li><a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_datasets', sample_id=trans.security.encode_id( sample.id ) )}">Transfer datasets</a></li>
+ %if sample.datasets and len( sample.datasets ) > len( transferred_dataset_files ):
+ <li><a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_datasets', sample_id=trans.security.encode_id( sample.id ) )}">Manage selected datasets</a></li>
+ %elif sample.datasets and len(sample.datasets ) == len( transferred_dataset_files ):
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ), transfer_status=trans.model.SampleDataset.transfer_status.COMPLETE )}">View transferred datasets</a></li>
%endif
</div>
%else:
@@ -326,7 +333,7 @@
%endif
</td>
%if display_bar_code:
- <td>${sample_widget_barcode}</td>
+ <td>${sample_widget_bar_code}</td>
%endif
%if is_unsubmitted:
<td>Unsubmitted</td>
@@ -361,9 +368,8 @@
<td>
%if is_admin:
<%
- if sample.transferred_dataset_files:
- transferred_dataset_files = sample.transferred_dataset_files
- else:
+ transferred_dataset_files = sample.transferred_dataset_files
+ if not transferred_dataset_files:
transferred_dataset_files = []
%>
%if not sample.datasets:
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -1575,7 +1575,7 @@ class TwillTestCase( unittest.TestCase )
for index, field_value in enumerate( bar_codes ):
sample_field_name = "sample_%i_name" % index
sample_field_value = samples[ index ].name.replace( ' ', '+' )
- field_name = "sample_%i_barcode" % index
+ field_name = "sample_%i_bar_code" % index
url += "&%s=%s" % ( field_name, field_value )
url += "&%s=%s" % ( sample_field_name, sample_field_value )
url += "&save_samples_button=Save"
1
0
galaxy-dist commit 0e5144e49c14: Trackster: packed scripts, support LEN files that are space separated
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '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 1289414596 18000
# Node ID 0e5144e49c14eb2a77e1d3182a33b4f9c8de98fc
# Parent d56f296aaa610a537d35cb7d235eac9d8f596464
Trackster: packed scripts, support LEN files that are space separated
--- a/lib/galaxy/web/controllers/tracks.py
+++ b/lib/galaxy/web/controllers/tracks.py
@@ -220,9 +220,10 @@ class TracksController( BaseController,
if not os.path.exists( len_file ):
return None
for line in open( len_file ):
- if line.startswith("#"): continue
- line = line.rstrip("\r\n")
- fields = line.split("\t")
+ if line.startswith("#"):
+ continue
+ # LEN files are just whitespace separated
+ fields = line.split()
manifest[fields[0]] = int(fields[1])
return manifest
--- a/static/scripts/packed/trackster.js
+++ b/static/scripts/packed/trackster.js
@@ -1,1 +1,1 @@
-var DENSITY=200,FEATURE_LEVELS=10,MAX_FEATURE_DEPTH=50,CONNECTOR_COLOR="#ccc",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...",FILTERABLE_CLASS="filterable",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=5,CACHED_DATA=5,DUMMY_CANVAS=document.createElement("canvas"),RIGHT_STRAND,LEFT_STRAND;if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(DUMMY_CANVAS)}CONTEXT=DUMMY_CANVAS.getContext("2d");PX_PER_CHAR=CONTEXT.measureText("A").width;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.createPa
ttern(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=CONTEXT.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")};function round_1000(a){return Math.round(a*1000)/1000}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,d,c,b){this.container=a;this.vis_id=c;this.dbkey=b;this.ti
tle=d;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 c=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").appendTo(c);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide();this.nav_container=$("<div/>").addClass("nav-container").appendTo(c);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/>").addClas
s("overview-viewport").appendTo(this.overview);this.overview_close=$("<a href='javascript:void(0);'>Close Overview</a>").addClass("overview-close").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div />").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_form=$("<form/>").attr("action",function(){}).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);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.wh
ich)!==27){a.go_to($(this).val())}$(this).hide();a.location_span.show();a.chrom_select.show();return false}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keypress focusout",b).appendTo(this.chrom_form);this.location_span=$("<span/>").addClass("location").appendTo(this.chrom_form);this.location_span.bind("click",function(){a.location_span.hide();a.chrom_select.hide();a.nav_input.css("display","inline-block");a.nav_input.select();a.nav_input.focus()});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).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(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);$.ajax({url:chrom_url,data:(this.vis_id!==undefined?{vis_id:this.vis_id}:{dbkey:
this.dbkey}),dataType:"json",success:function(e){if(e.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=e.chrom_info;var h='<option value="">Select Chrom/Contig</option>';for(var g=0,d=a.chrom_data.length;g<d;g++){var f=a.chrom_data[g].chrom;h+='<option value="'+f+'">'+f+"</option>"}a.chrom_select.html(h);a.intro_div.show();a.chrom_select.bind("change",function(){a.change_chrom(a.chrom_select.val())})},error:function(){alert("Could not load chroms for this dbkey:",a.dbkey)}});this.content_div.bind("dblclick",function(d){a.zoom_in(d.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(d){this.current_x=d.offsetX}).bind("drag",function(d){var g=d.offsetX-this.current_x;this.current_x=d.offsetX;var f=Math.round(g/a.viewport_container.width()*(a.max_high-a.max_low));a.move_delta(-f)});this.overview_close.bind("click",function(){for(var e=0,d=a.tracks.length;e<d;e++){a.tracks[e].is_overview=false}$(this).siblings().filter("canvas").remove();
$(this).parent().css("height",a.overview_box.height());a.overview_highlight.hide();$(this).hide()});this.viewport_container.bind("dragstart",function(d){this.original_low=a.low;this.current_height=d.clientY;this.current_x=d.offsetX;this.enable_pan=(d.clientX<a.viewport_container.width()-16)?true:false}).bind("drag",function(g){if(!this.enable_pan||this.in_reordering){return}var d=$(this);var i=g.offsetX-this.current_x;var f=d.scrollTop()-(g.clientY-this.current_height);d.scrollTop(f);this.current_height=g.clientY;this.current_x=g.offsetX;var h=Math.round(i/a.viewport_container.width()*(a.high-a.low));a.move_delta(h)});this.top_labeltrack.bind("dragstart",function(d){this.drag_origin_x=d.clientX;this.drag_origin_pos=d.clientX/a.viewport_container.width()*(a.high-a.low)+a.low;this.drag_div=$("<div />").css({height:a.content_div.height()+30,top:"0px",position:"absolute","background-color":"#cfc",border:"1px solid #6a6",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag
",function(i){var f=Math.min(i.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(i.clientX,this.drag_origin_x)-a.container.offset().left,h=(a.high-a.low),g=a.viewport_container.width();a.update_location(Math.round(f/g*h)+a.low,Math.round(d/g*h)+a.low);this.drag_div.css({left:f+"px",width:(d-f)+"px"})}).bind("dragend",function(j){var f=Math.min(j.clientX,this.drag_origin_x),d=Math.max(j.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),i=a.low;a.low=Math.round(f/g*h)+i;a.high=Math.round(d/g*h)+i;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));$(window).bind("resize",function(){a.resize_window()});$(document).bind("redraw",function(){a.redraw()});this.reset();$(window).trigger("resize")},update_location:function(a,b){this.location_span.text(commatize(a)+" - "+commatize(b));this.nav_input.val(this.chrom+":"+commatize(a)+"-"+c
ommatize(b))},change_chrom:function(e,b,g){var d=this;var f=$.grep(d.chrom_data,function(j,k){return j.chrom===e})[0];if(f===undefined){return}if(e!==d.chrom){d.chrom=e;if(!d.chrom){d.intro_div.show()}else{d.intro_div.hide()}d.chrom_select.val(d.chrom);d.max_high=f.len;d.reset();d.redraw(true);for(var h=0,a=d.tracks.length;h<a;h++){var c=d.tracks[h];if(c.init){c.init()}}}if(b!==undefined&&g!==undefined){d.low=Math.max(b,0);d.high=Math.min(g,d.max_high)}d.reset_overview();d.redraw()},go_to:function(f){var j=this,a,d,b=f.split(":"),h=b[0],i=b[1];if(i!==undefined){try{var g=i.split("-");a=parseInt(g[0].replace(/,/g,""),10);d=parseInt(g[1].replace(/,/g,""),10)}catch(c){return false}}j.change_chrom(h,a,d)},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.view=this;a.track_id=this.track_id_counte
r;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},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},redraw:function(h){var g=this.high-this.low,f=this.low,b=this.high;if(f<this.max_low){f=this.max_low}if(b>this.max_high){b=this.max_high}if(this.high!==0&&g<this.min_separation){b=f+this.min_separation}this.low=Math.floor(f);this.high=Math.ceil(b);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))));var a=(this.low/(this.max_high-this.max
_low)*this.overview_viewport.width())||0;var e=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var j=13;this.overview_box.css({left:a,width:Math.max(j,e)}).show();if(e<j){this.overview_box.css("left",a-(j-e)/2)}if(this.overview_highlight){this.overview_highlight.css({left:a,width:e})}this.update_location(this.low,this.high);if(!h){for(var c=0,d=this.tracks.length;c<d;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(c=0,d=this.label_tracks.length;c<d;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);th
is.high=Math.round(c+a);this.redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.nav_container.height()-45);this.nav_container.width(this.container.width());this.redraw()},reset_overview:function(){this.overview_viewport.find("canvas").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide()}});var Filter=function(b,a,c){this.name=b;this.index=a;this.value=c};var NumberFilter=function(b,a){this.name=b;this.index=a;this.low=-Number.MAX_VALUE;this.high=Number.MAX_VALUE;this.slider_min=Number.MAX_VALUE;this.slider_max=-Number.MAX_VALUE;this.slider=null;this.slider_label=null};$.extend(NumberFilter.prototype,{applies_to:function(a){if(a.length>this.index){return true}return false},keep:function(a){if(!this.applies_to(a)){return true}return(a[this.index]>=this.low&&a[this.index]<=this.high)},update_attrs:function(b
){var a=false;if(!this.applies_to(b)){return a}if(b[this.index]<this.slider_min){this.slider_min=b[this.index];a=true}if(b[this.index]>this.slider_max){this.slider_max=b[this.index];a=false}return a},update_ui_elt:function(){var b=this.slider.slider("option","min"),a=this.slider.slider("option","max");if(this.slider_min<b||this.slider_max>a){this.slider.slider("option","min",this.slider_min);this.slider.slider("option","max",this.slider_max);this.slider.slider("option","values",[this.slider_min,this.slider_max])}}});var get_filters=function(a){var g=[];for(var d=0;d<a.length;d++){var f=a[d];var c=f.name,e=f.type,b=f.index;if(e=="int"||e=="float"){g[d]=new NumberFilter(c,b)}else{g[d]=new Filter(c,b,e)}}return g};var Track=function(b,a,d,c){this.name=b;this.view=a;this.parent_element=d;this.filters=(c!==undefined?get_filters(c):[]);this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track").css("position","relative");i
f(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);if(this.view.editor){this.drag_div=$("<div class='draghandle' />").appendTo(this.header_div)}this.name_div=$("<div class='menubutton popup' />").appendTo(this.header_div);this.name_div.text(this.name);this.name_div.attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase())}this.filtering_div=$("<div class='track-filters'>").appendTo(this.container_div);this.filtering_div.hide();this.filtering_div.bind("drag",function(i){i.stopPropagation()});var b=$("<table class='filters'>").appendTo(this.filtering_div);var c=this;for(var e=0;e<this.filters.length;e++){var a=this.filters[e];var f=$("<tr>").appendTo(b);var g=$("<th class='filter-info'>").appendTo(f);var j=$("<span class='name'>").appendTo(g);j.text(a.name+" ");var d=$("<span class='values'>").appendTo(g);var h=$("<td>").appendTo(f);a.control_element=$("<div id='"+a.name+"-filter-control' style='width: 200
px; position: relative'>").appendTo(h);a.control_element.slider({range:true,min:Number.MAX_VALUE,max:-Number.MIN_VALUE,values:[0,0],slide:function(k,l){var i=l.values;d.text("["+i[0]+"-"+i[1]+"]");a.low=i[0];a.high=i[1];c.draw(true)},change:function(i,k){a.control_element.slider("option","slide").call(a.control_element,i,k)}});a.slider=a.control_element;a.slider_label=d}this.content_div=$("<div class='track-content'>").appendTo(this.container_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.initial_canvas=undefined;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(){var b=this,j=b.view;if(b.hidden){return}if(b.display_modes!==undefined){if(b.mode_div===undefined){b.mode_div=$("<div class='right-float menubutton popup' />").appendTo(b.header_div);var e=
b.display_modes[0];b.mode=e;b.mode_div.text(e);var c=function(i){b.mode_div.text(i);b.mode=i;b.tile_cache.clear();b.draw()};var a={};for(var f=0,h=b.display_modes.length;f<h;f++){var g=b.display_modes[f];a[g]=function(i){return function(){c(i)}}(g)}make_popupmenu(b.mode_div,a)}else{b.mode_div.hide()}}var d={};d["Set as overview"]=function(){j.overview_viewport.find("canvas").remove();b.is_overview=true;b.set_overview();for(var i in j.tracks){if(j.tracks[i]!==b){j.tracks[i].is_overview=false}}};d["Edit configuration"]=function(){var l=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},i=function(){b.update_options(b.track_id);hide_modal();$(window).unbind("keypress.check_enter_esc")},k=function(m){if((m.keyCode||m.which)===27){l()}else{if((m.keyCode||m.which)===13){i()}}};$(window).bind("keypress.check_enter_esc",k);show_modal("Configure Track",b.gen_options(b.track_id),{Cancel:l,OK:i})};if(b.filters.length>0){d["Show filters"]=function(){var i;if(!b.filter
ing_div.is(":visible")){i="Hide filters";b.filters_visible=true}else{i="Show filters";b.filters_visible=false}$("#"+b.name_div.attr("id")+"-menu").find("li").eq(2).text(i);b.filtering_div.toggle()}}d.Remove=function(){j.remove_track(b);if(j.num_tracks===0){$("#no-tracks").show()}};b.popup_menu=make_popupmenu(b.name_div,d);show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters",false)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(a){var k=this.view.low,g=this.view.high,h=g-k,f=this.view.resolution;var n=$("<div style='position: relative;'></div>"),o=this.content_div.width()/h;this.content_div.append(n);this.max_height=0;var b=Math.floor(k/f/DENSITY);var j={};while((b*DENSITY*f)<g){var l=this.content_div.width()+"_"+o+"_"+b;var e=this.tile_cache.get(l);if(!a&&e){var i=b*DENSITY*f;var d=(i-k)*o;if(this.left_offset){d-=this.left_offset}e.css({left:d});this.show_tile(e,n)}else{this.delayed_draw(this,l,k,g,b,f,n,o,j)}b+=1}var c=this;var m=setInterval(functio
n(){if(obj_length(j)===0){if(c.content_div.children().length>1){c.content_div.children(":first").remove()}for(var p=0;p<c.filters.length;p++){c.filters[p].update_ui_elt()}clearInterval(m)}},50)},delayed_draw:function(c,h,g,e,b,d,i,j,f){var a=setTimeout(function(){if(g<=c.view.high&&e>=c.view.low){var k=c.draw_tile(d,b,i,j);if(k){if(!c.initial_canvas&&!window.G_vmlCanvasManager){c.initial_canvas=$(k).clone();var n=k.get(0).getContext("2d");var l=c.initial_canvas.get(0).getContext("2d");var m=n.getImageData(0,0,n.canvas.width,n.canvas.height);l.putImageData(m,0,0);c.set_overview()}c.tile_cache.set(h,k);c.show_tile(k,i)}}delete f[a]},50);f[a]=true},show_tile:function(a,c){var b=this;c.append(a);b.max_height=Math.max(b.max_height,a.height());b.content_div.css("height",b.max_height+"px");if(a.hasClass(FILTERABLE_CLASS)){show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters");if(b.filters_visible){b.filtering_div.show()}}else{show_hide_popupmenu_options(b.popup_menu,"(Show
|Hide) filters",false);b.filtering_div.hide()}},set_overview:function(){var a=this.view;if(this.initial_canvas&&this.is_overview){a.overview_close.show();a.overview_viewport.append(this.initial_canvas);a.overview_highlight.show().height(this.initial_canvas.height());a.overview_viewport.height(this.initial_canvas.height()+a.overview_box.height())}$(window).trigger("resize")}});var LabelTrack=function(a,b){this.track_type="LabelTrack";this.hidden=true;Track.call(this,null,a,b);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_di
v.append(b)}});var ReferenceTrack=function(a){this.track_type="ReferenceTrack";this.hidden=true;Track.call(this,null,a,a.top_labeltrack);TiledTrack.call(this);this.left_offset=200;this.height_px=12;this.container_div.addClass("reference-track");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,i){console.log(h,g,i)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,j=f+"_"+b;var e=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(e)}e=$(e);var n=e.get(0).getContext("2d");if(o>PX_PER_CHAR){if(this.dat
a_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),i=Math.round(o/2);n.fillText(m[h],a+this.left_offset+i,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b,a,c){this.track_type="LineTrack";this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=80;this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={color:"black",min_value:undefined,max_value:undefined,mode:this.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.ver
tical_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");var e=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=e.min;a.prefs.max_value=e.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=e.total_frequency;a.container_div.find(".yaxislabel").remove();var f=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(round_1000(a.prefs.min_value));var d=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(round_1000(a.prefs.max_value));d.css({position:"absolute",top:"22px",left:"10px"});d.prependTo(a.container_div);f.css({position:"absolute",top:a.height_px+11+"px",left:"10px"});f.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){var h=g.data;c.data_cache.set(e,h);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,r,c,e){if(this.vertical_range===undefined){return}var s=r*DENSITY*o,a=DENSITY*o,w=o+"_"+r;var b=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(b)}b=$(b);if(this.data_cache.get(w)===undefined){this.get_data(o,r);return}var v=this.data_cache.get(w);if(!v){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),j=false,k=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,t=this.total_frequency,d=this.height_px,l=this.mode;n.beginPath();n.fillStyle=this.prefs.co
lor;var u,h,f;if(v.length>1){f=Math.ceil((v[1][0]-v[0][0])*e)}else{f=10}for(var p=0,q=v.length;p<q;p++){u=Math.round((v[p][0]-s)*e);h=v[p][1];if(h===null){if(j&&l==="Filled"){n.lineTo(u,d)}j=false;continue}if(h<k){h=k}else{if(h>g){h=g}}if(l==="Histogram"){h=Math.round(d-(h-k)/m*d);n.fillRect(u,h,f,d-h)}else{if(l==="Intensity"){h=255-Math.floor((h-k)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(u,0,f,d)}else{h=Math.round(d-(h-k)/m*d);if(j){n.lineTo(u,h)}else{j=true;if(l==="Filled"){n.moveTo(u,d);n.lineTo(u,h)}else{n.moveTo(u,h)}}}}}if(l==="Filled"){if(j){n.lineTo(u,d)}n.fill()}else{n.stroke()}c.append(b);return b},gen_options:function(m){var a=$("<div />").addClass("form-row");var e="track_"+m+"_color",b=$("<label />").attr("for",e).text("Color:"),c=$("<input />").attr("id",e).attr("name",e).val(this.prefs.color),h="track_"+m+"_minval",l=$("<label></label>").attr("for",h).text("Min value:"),d=(this.prefs.min_value===undefined?"":this.prefs.min_value),k=$("<input></i
nput>").attr("id",h).val(d),j="track_"+m+"_maxval",g=$("<label></label>").attr("for",j).text("Max value:"),i=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",j).val(i);return a.append(l).append(k).append(g).append(f).append(b).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_color").val();if(a!==this.prefs.min_value||c!==this.prefs.max_value||b!==this.prefs.color){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.color=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,e,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container,e);TiledTrack.call(this);this.height_p
x=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=2;this.summary_draw_height=30;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.left_offset=200;this.prefs={block_color:"#444",label_color:"black",show_counts:true}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b="initial";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,mode:a.mode},function(c){a.mode_div.show();a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.vie
w.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,b,q){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=q;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,y=[],h=0,n=this.view.max_low;var A=[];if(this.inc_slots[a].mode!==q){delete this.inc_slots[a];this.inc_slots[a]={mode:q,w_scale:m};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var v=0,w=g.length;v<w;v++){var f=g[v],l=f[0];if(this.inc_slots[a][l]!==undefined){h=Math.max(h,this.inc_slots[a][l]);A.push(this.inc_slots[a][l])}else{y.push(v)}}for(var v=0,w=y.length;v<w;v++){var f=g[y[v]],l=f[0],r=f[1],c=f[2],p=f[3],d=Math.floor((r-n)*m),e=Math.ceil((c-n)*m);if(p!==undefined&&!b){var s=CONTEXT.measureText(p).width;if(d-s<0){e+=s}else{d-=s}}var u=0;while(u<=MAX_FEATURE_DEPTH){var o=true;if(this.s_e_by_tile[a][u]!==undefined){for(var t=0,z=this.s_e_by_tile[a][u].length;t<z;t++){var x=this.s_e_
by_tile[a][u][t];if(e>x[0]&&d<x[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][u]===undefined){this.s_e_by_tile[a][u]=[]}this.s_e_by_tile[a][u].push([d,e]);this.inc_slots[a][l]=u;h=Math.max(h,u);break}u++}}return h},rect_or_text:function(r,l,t,b,q,f,i,e){r.textAlign="center";var k=0,p=Math.round(l/2);for(var m=0,s=i.length;m<s;m++){var j=i[m],d="MIDNSHP"[j[0]],n=j[1];if(d==="H"||d==="S"){k-=n}var g=q+k,w=Math.floor(Math.max(0,(g-t)*l)),h=Math.floor(Math.max(0,(g+n-t)*l));switch(d){case"S":case"H":case"M":var o=f.slice(k,n);if((this.mode==="Pack"||this.mode==="Auto")&&f!==undefined&&l>PX_PER_CHAR){r.fillStyle=this.prefs.block_color;r.fillRect(w+this.left_offset,e+1,h-w,9);r.fillStyle=CONNECTOR_COLOR;for(var u=0,a=o.length;u<a;u++){if(g+u>=t&&g+u<=b){var v=Math.floor(Math.max(0,(g+u-t)*l));r.fillText(o[u],v+this.left_offset+p,e+9)}}}else{r.fillStyle=this.prefs.block_color;r.fillRect(w+this.left_offset,e+4,h-w,3)}break;case"N":r.fillStyle=CONNECTOR_COLOR;r.fillRect(w+this.lef
t_offset,e+5,h-w,1);break;case"D":r.fillStyle="red";r.fillRect(w+this.left_offset,e+4,h-w,3);break;case"P":case"I":break}k+=n}},draw_tile:function(ag,o,s,av){var N=o*DENSITY*ag,al=(o+1)*DENSITY*ag,M=al-N;var an=(!this.initial_canvas?"initial":N+"_"+al);var I=this.data_cache.get(an);var e;if(I===undefined||(this.mode!=="Auto"&&I.dataset_type==="summary_tree")){this.data_queue[[N,al]]=true;this.get_data(N,al);return}var a=Math.ceil(M*av),ai=this.prefs.label_color,l=this.prefs.block_color,r=this.mode,z=25,ae=(r==="Squish")||(r==="Dense")&&(r!=="Pack")||(r==="Auto"&&(I.extra_info==="no_detail")),W=this.left_offset,au,D,aw;var q=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(q)}q=$(q);if(I.dataset_type==="summary_tree"){D=this.summary_draw_height}else{if(r==="Dense"){D=z;aw=10}else{aw=(ae?this.vertical_nodetail_px:this.vertical_detail_px);var A=(av<0.0001?1/this.view.zoom_res:av);D=this.incremental_slots(A,I.data,ae,r)*aw+z;au=this.i
nc_slots[A]}}q.css({position:"absolute",top:0,left:(N-this.view.low)*av-W});q.get(0).width=a+W;q.get(0).height=D;s.parent().css("height",Math.max(this.height_px,D)+"px");var J=q.get(0).getContext("2d");J.fillStyle=l;J.font=this.default_font;J.textAlign="right";this.container_div.find(".yaxislabel").remove();if(I.dataset_type=="summary_tree"){var Y=I.data,L=I.max,b=Math.ceil(I.delta*av);var p=$("<div />").addClass("yaxislabel").text(L);p.css({position:"absolute",top:"22px",left:"10px"});p.prependTo(this.container_div);for(var ap=0,H=Y.length;ap<H;ap++){var aa=Math.floor((Y[ap][0]-N)*av);var Z=Y[ap][1];if(!Z){continue}var am=Z/L*this.summary_draw_height;J.fillStyle="black";J.fillRect(aa+W,this.summary_draw_height-am,b,am);if(this.prefs.show_counts&&J.measureText(Z).width<b){J.fillStyle="#bbb";J.textAlign="center";J.fillText(Z,aa+W+(b/2),this.summary_draw_height-5)}}e="Summary";s.append(q);return q}if(I.message){q.css({border:"solid red","border-width":"2px 2px 2px 0px"});J.fil
lStyle="red";J.textAlign="left";J.fillText(I.message,100+W,aw)}var ad=false;if(I.data){ad=true;for(var ar=0;ar<this.filters.length;ar++){if(!this.filters[ar].applies_to(I.data[0])){ad=false}}}if(ad){q.addClass(FILTERABLE_CLASS)}var at=I.data;var ao=0;for(var ap=0,H=at.length;ap<H;ap++){var S=at[ap],R=S[0],aq=S[1],ac=S[2],O=S[3];if(au[R]===undefined){continue}var ab=false;var U;for(var ar=0;ar<this.filters.length;ar++){U=this.filters[ar];U.update_attrs(S);if(!U.keep(S)){ab=true;break}}if(ab){continue}if(aq<=al&&ac>=N){var af=Math.floor(Math.max(0,(aq-N)*av)),K=Math.ceil(Math.min(a,Math.max(0,(ac-N)*av))),X=(r==="Dense"?1:(1+au[R]))*aw;var G,aj,P=null,ax=null;if(I.dataset_type==="bai"){var v=S[4];J.fillStyle=l;if(S[5] instanceof Array){var E=Math.floor(Math.max(0,(S[5][0]-N)*av)),Q=Math.ceil(Math.min(a,Math.max(0,(S[5][1]-N)*av))),C=Math.floor(Math.max(0,(S[6][0]-N)*av)),w=Math.ceil(Math.min(a,Math.max(0,(S[6][1]-N)*av)));if(S[5][1]>=N&&S[5][0]<=al){this.rect_or_text(J,av,N,al
,S[5][0],S[5][2],v,X)}if(S[6][1]>=N&&S[6][0]<=al){this.rect_or_text(J,av,N,al,S[6][0],S[6][2],v,X)}if(C>Q){J.fillStyle=CONNECTOR_COLOR;J.fillRect(Q+W,X+5,C-Q,1)}}else{J.fillStyle=l;this.rect_or_text(J,av,N,al,aq,O,v,X)}if(r!=="Dense"&&!ae&&aq>N){J.fillStyle=this.prefs.label_color;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(R,K+2+W,X+8)}else{J.textAlign="right";J.fillText(R,af-2+W,X+8)}J.fillStyle=l}}else{if(I.dataset_type==="interval_index"){if(ae){J.fillStyle=l;J.fillRect(af+W,X+5,K-af,1)}else{var F=S[4],V=S[5],ah=S[6],h=S[7];if(V&&ah){P=Math.floor(Math.max(0,(V-N)*av));ax=Math.ceil(Math.min(a,Math.max(0,(ah-N)*av)))}if(r!=="Dense"&&O!==undefined&&aq>N){J.fillStyle=ai;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(O,K+2+W,X+8)}else{J.textAlign="right";J.fillText(O,af-2+W,X+8)}J.fillStyle=l}if(h){if(F){if(F=="+"){J.fillStyle=RIGHT_STRAND}else{if(F=="-"){J.fillStyle=LEFT_STRAND}}J.fillRect(af+W,X,K-af,10);J.fillStyle=l}for(var an
=0,g=h.length;an<g;an++){var u=h[an],d=Math.floor(Math.max(0,(u[0]-N)*av)),T=Math.ceil(Math.min(a,Math.max((u[1]-N)*av)));if(d>T){continue}G=5;aj=3;J.fillRect(d+W,X+aj,T-d,G);if(P!==undefined&&!(d>ax||T<P)){G=9;aj=1;var ak=Math.max(d,P),B=Math.min(T,ax);J.fillRect(ak+W,X+aj,B-ak,G)}}}else{G=9;aj=1;J.fillRect(af+W,X+aj,K-af,G);if(S.strand){if(S.strand=="+"){J.fillStyle=RIGHT_STRAND_INV}else{if(S.strand=="-"){J.fillStyle=LEFT_STRAND_INV}}J.fillRect(af+W,X,K-af,10);J.fillStyle=l}}}}else{if(I.dataset_type==="vcf"){if(ae){J.fillStyle=l;J.fillRect(af+W,X+5,K-af,1)}else{var t=S[4],n=S[5],c=S[6];G=9;aj=1;J.fillRect(af+W,X,K-af,G);if(r!=="Dense"&&O!==undefined&&aq>N){J.fillStyle=ai;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(O,K+2+W,X+8)}else{J.textAlign="right";J.fillText(O,af-2+W,X+8)}J.fillStyle=l}var m=t+" / "+n;if(aq>N&&J.measureText(m).width<(K-af)){J.fillStyle="white";J.textAlign="center";J.fillText(m,W+af+(K-af)/2,X+8);J.fillStyle=l}}}}}ao++}}return q
},gen_options:function(i){var a=$("<div />").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label />").attr("for",e).text("Block color:"),l=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label />").attr("for",j).text("Text color:"),h=$("<input />").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(k).append(l).append(g).append(h).append(d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_
counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,e,c){FeatureTrack.call(this,d,b,a,e,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,MAX_FEATURE_DEPTH=50,CONNECTOR_COLOR="#ccc",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...",FILTERABLE_CLASS="filterable",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=5,CACHED_DATA=5,DUMMY_CANVAS=document.createElement("canvas"),RIGHT_STRAND,LEFT_STRAND;if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(DUMMY_CANVAS)}CONTEXT=DUMMY_CANVAS.getContext("2d");PX_PER_CHAR=CONTEXT.measureText("A").width;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.createPa
ttern(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=CONTEXT.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")};function round_1000(a){return Math.round(a*1000)/1000}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,d,c,b,e){this.container=a;this.vis_id=c;this.dbkey=b;this.
title=d;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(e);this.reset()};$.extend(View.prototype,{init:function(d){var c=this.container,a=this;this.top_container=$("<div/>").addClass("top-container").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").appendTo(c);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(c);this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(this.top_container);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide();this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("nav-container").prependTo(this.top
_container);this.nav=$("<div/>").addClass("nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.bottom_container);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_close=$("<a href='javascript:void(0);'>Close Overview</a>").addClass("overview-close").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div />").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_form=$("<form/>").attr("action",function(){}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-a
utocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(f){if(f.type==="focusout"||(f.keyCode||f.which)===13||(f.keyCode||f.which)===27){if((f.keyCode||f.which)!==27){a.go_to($(this).val())}$(this).hide();a.location_span.show();a.chrom_select.show();return false}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keypress focusout",b).appendTo(this.chrom_form);this.location_span=$("<span/>").addClass("location").appendTo(this.chrom_form);this.location_span.bind("click",function(){a.location_span.hide();a.chrom_select.hide();a.nav_input.css("display","inline-block");a.nav_input.select();a.nav_input.focus()});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).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(this.chrom_form);this.zi_link=$("<a/>").click(funct
ion(){a.zoom_in();a.redraw()}).html('<img src="'+image_path+'/fugue/magnifier-zoom.png" />').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(f){if(f.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=f.chrom_info;var j='<option value="">Select Chrom/Contig</option>';for(var h=0,e=a.chrom_data.length;h<e;h++){var g=a.chrom_data[h].chrom;j+='<option value="'+g+'">'+g+"</option>"}a.chrom_select.html(j);a.intro_div.show();a.chrom_select.bind("change",function(){a.change_chrom(a.chrom_select.val())});if(d){d()}},error:function(){alert("Could not load chroms for this dbkey:",a.dbkey)}});this.content_div.bind("dblclick",function(f){a.zoom_in(f.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(f){this.current_x=f.offsetX}).bind("drag",function(f){var h=f.offsetX-this.current_x;this.current_x=f.offsetX;var g=Math.round(h/a.viewport_container
.width()*(a.max_high-a.max_low));a.move_delta(-g)});this.overview_close.bind("click",function(){for(var f=0,e=a.tracks.length;f<e;f++){a.tracks[f].is_overview=false}$(this).siblings().filter("canvas").remove();$(this).parent().css("height",a.overview_box.height());a.overview_highlight.hide();$(this).hide()});this.viewport_container.bind("dragstart",function(f){this.original_low=a.low;this.current_height=f.clientY;this.current_x=f.offsetX;this.enable_pan=(f.clientX<a.viewport_container.width()-16)?true:false}).bind("drag",function(h){if(!this.enable_pan||this.in_reordering){return}var f=$(this);var j=h.offsetX-this.current_x;var g=f.scrollTop()-(h.clientY-this.current_height);f.scrollTop(g);this.current_height=h.clientY;this.current_x=h.offsetX;var i=Math.round(j/a.viewport_container.width()*(a.high-a.low));a.move_delta(i)});this.top_labeltrack.bind("dragstart",function(f){this.drag_origin_x=f.clientX;this.drag_origin_pos=f.clientX/a.viewport_container.width()*(a.high-a.low)+
a.low;this.drag_div=$("<div />").css({height:a.content_div.height()+a.top_labeltrack.height()+a.nav_labeltrack.height(),top:"0px",position:"absolute","background-color":"#ccf",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag",function(j){var g=Math.min(j.clientX,this.drag_origin_x)-a.container.offset().left,f=Math.max(j.clientX,this.drag_origin_x)-a.container.offset().left,i=(a.high-a.low),h=a.viewport_container.width();a.update_location(Math.round(g/h*i)+a.low,Math.round(f/h*i)+a.low);this.drag_div.css({left:g+"px",width:(f-g)+"px"})}).bind("dragend",function(k){var g=Math.min(k.clientX,this.drag_origin_x),f=Math.max(k.clientX,this.drag_origin_x),i=(a.high-a.low),h=a.viewport_container.width(),j=a.low;a.low=Math.round(g/h*i)+j;a.high=Math.round(f/h*i)+j;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));$(window).bind("resize",function(){a.resize_window(
)});$(document).bind("redraw",function(){a.redraw()});this.reset();$(window).trigger("resize")},update_location:function(a,b){this.location_span.text(commatize(a)+" - "+commatize(b));this.nav_input.val(this.chrom+":"+commatize(a)+"-"+commatize(b))},change_chrom:function(e,b,g){var d=this;var f=$.grep(d.chrom_data,function(j,k){return j.chrom===e})[0];if(f===undefined){return}if(e!==d.chrom){d.chrom=e;if(!d.chrom){d.intro_div.show()}else{d.intro_div.hide()}d.chrom_select.val(d.chrom);d.max_high=f.len;d.reset();d.redraw(true);for(var h=0,a=d.tracks.length;h<a;h++){var c=d.tracks[h];if(c.init){c.init()}}}if(b!==undefined&&g!==undefined){d.low=Math.max(b,0);d.high=Math.min(g,d.max_high)}d.reset_overview();d.redraw()},go_to:function(f){var j=this,a,d,b=f.split(":"),h=b[0],i=b[1];if(i!==undefined){try{var g=i.split("-");a=parseInt(g[0].replace(/,/g,""),10);d=parseInt(g[1].replace(/,/g,""),10)}catch(c){return false}}j.change_chrom(h,a,d)},move_fraction:function(c){var a=this;var b=
a.high-a.low;this.move_delta(c*b)},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.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},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},redraw:function(h){var g=this.high-this.low,f=this.low,b=this.high;if(f<this.max_low){f=this.max_low}if(b>this.max_high){b=this.max_high}if(this.high!==0&&g<this.min_separation){b=f+this.
min_separation}this.low=Math.floor(f);this.high=Math.ceil(b);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))));var a=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var e=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var j=13;this.overview_box.css({left:a,width:Math.max(j,e)}).show();if(e<j){this.overview_box.css("left",a-(j-e)/2)}if(this.overview_highlight){this.overview_highlight.css({left:a,width:e})}this.update_location(this.low,this.high);if(!h){for(var c=0,d=this.tracks.length;c<d;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(c=0,d=this.label_tracks.length;c<d;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()},resize_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());this.nav_container.width(this.container.width());this.redraw()},reset_overview:function(){this.overview_viewport.find("canvas").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide()}});var Filter=function(b,a,c){this.name=b;this.index=a;this.value=c};var NumberFilter=function(b,a){this.name=b;this.index=a;this.low=-Number.MAX_VALUE;this.high=Number.MAX_VALUE;this.sli
der_min=Number.MAX_VALUE;this.slider_max=-Number.MAX_VALUE;this.slider=null;this.slider_label=null};$.extend(NumberFilter.prototype,{applies_to:function(a){if(a.length>this.index){return true}return false},keep:function(a){if(!this.applies_to(a)){return true}return(a[this.index]>=this.low&&a[this.index]<=this.high)},update_attrs:function(b){var a=false;if(!this.applies_to(b)){return a}if(b[this.index]<this.slider_min){this.slider_min=b[this.index];a=true}if(b[this.index]>this.slider_max){this.slider_max=b[this.index];a=false}return a},update_ui_elt:function(){var b=this.slider.slider("option","min"),a=this.slider.slider("option","max");if(this.slider_min<b||this.slider_max>a){this.slider.slider("option","min",this.slider_min);this.slider.slider("option","max",this.slider_max);this.slider.slider("option","values",[this.slider_min,this.slider_max])}}});var get_filters=function(a){var g=[];for(var d=0;d<a.length;d++){var f=a[d];var c=f.name,e=f.type,b=f.index;if(e=="int"||e=="f
loat"){g[d]=new NumberFilter(c,b)}else{g[d]=new Filter(c,b,e)}}return g};var Track=function(b,a,d,c){this.name=b;this.view=a;this.parent_element=d;this.filters=(c!==undefined?get_filters(c):[]);this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track").css("position","relative");if(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);if(this.view.editor){this.drag_div=$("<div class='draghandle' />").appendTo(this.header_div)}this.name_div=$("<div class='menubutton popup' />").appendTo(this.header_div);this.name_div.text(this.name);this.name_div.attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase())}this.filtering_div=$("<div class='track-filters'>").appendTo(this.container_div);this.filtering_div.hide();this.filtering_div.bind("drag",function(i){i.stopPropagation()});var b=$("<table class='filters'>").appendTo(this.filtering_div);var c=this;for(va
r e=0;e<this.filters.length;e++){var a=this.filters[e];var f=$("<tr>").appendTo(b);var g=$("<th class='filter-info'>").appendTo(f);var j=$("<span class='name'>").appendTo(g);j.text(a.name+" ");var d=$("<span class='values'>").appendTo(g);var h=$("<td>").appendTo(f);a.control_element=$("<div id='"+a.name+"-filter-control' style='width: 200px; position: relative'>").appendTo(h);a.control_element.slider({range:true,min:Number.MAX_VALUE,max:-Number.MIN_VALUE,values:[0,0],slide:function(k,l){var i=l.values;d.text("["+i[0]+"-"+i[1]+"]");a.low=i[0];a.high=i[1];c.draw(true)},change:function(i,k){a.control_element.slider("option","slide").call(a.control_element,i,k)}});a.slider=a.control_element;a.slider_label=d}this.content_div=$("<div class='track-content'>").appendTo(this.container_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.initial_canvas=undefined;a.content_di
v.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(){var b=this,j=b.view;if(b.hidden){return}if(b.display_modes!==undefined){if(b.mode_div===undefined){b.mode_div=$("<div class='right-float menubutton popup' />").appendTo(b.header_div);var e=b.display_modes[0];b.mode=e;b.mode_div.text(e);var c=function(i){b.mode_div.text(i);b.mode=i;b.tile_cache.clear();b.draw()};var a={};for(var f=0,h=b.display_modes.length;f<h;f++){var g=b.display_modes[f];a[g]=function(i){return function(){c(i)}}(g)}make_popupmenu(b.mode_div,a)}else{b.mode_div.hide()}}var d={};d["Set as overview"]=function(){j.overview_viewport.find("canvas").remove();b.is_overview=true;b.set_overview();for(var i in j.tracks){if(j.tracks[i]!==b){j.tracks[i].is_overview=false}}};d["Edit configuration"]=function(){var l=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},i=function(){b.update_options(b.track_
id);hide_modal();$(window).unbind("keypress.check_enter_esc")},k=function(m){if((m.keyCode||m.which)===27){l()}else{if((m.keyCode||m.which)===13){i()}}};$(window).bind("keypress.check_enter_esc",k);show_modal("Configure Track",b.gen_options(b.track_id),{Cancel:l,OK:i})};if(b.filters.length>0){d["Show filters"]=function(){var i;if(!b.filtering_div.is(":visible")){i="Hide filters";b.filters_visible=true}else{i="Show filters";b.filters_visible=false}$("#"+b.name_div.attr("id")+"-menu").find("li").eq(2).text(i);b.filtering_div.toggle()}}d.Remove=function(){j.remove_track(b);if(j.num_tracks===0){$("#no-tracks").show()}};b.popup_menu=make_popupmenu(b.name_div,d);show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters",false)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(a){var k=this.view.low,g=this.view.high,h=g-k,f=this.view.resolution;var n=$("<div style='position: relative;'></div>"),o=this.content_div.width()/h;this.content_div.append(n);this.max_height=
0;var b=Math.floor(k/f/DENSITY);var j={};while((b*DENSITY*f)<g){var l=this.content_div.width()+"_"+o+"_"+b;var e=this.tile_cache.get(l);if(!a&&e){var i=b*DENSITY*f;var d=(i-k)*o;if(this.left_offset){d-=this.left_offset}e.css({left:d});this.show_tile(e,n)}else{this.delayed_draw(this,l,k,g,b,f,n,o,j)}b+=1}var c=this;var m=setInterval(function(){if(obj_length(j)===0){if(c.content_div.children().length>1){c.content_div.children(":first").remove()}for(var p=0;p<c.filters.length;p++){c.filters[p].update_ui_elt()}clearInterval(m)}},50)},delayed_draw:function(c,h,g,e,b,d,i,j,f){var a=setTimeout(function(){if(g<=c.view.high&&e>=c.view.low){var k=c.draw_tile(d,b,i,j);if(k){if(!c.initial_canvas&&!window.G_vmlCanvasManager){c.initial_canvas=$(k).clone();var n=k.get(0).getContext("2d");var l=c.initial_canvas.get(0).getContext("2d");var m=n.getImageData(0,0,n.canvas.width,n.canvas.height);l.putImageData(m,0,0);c.set_overview()}c.tile_cache.set(h,k);c.show_tile(k,i)}}delete f[a]},50);f[a]=
true},show_tile:function(a,c){var b=this;c.append(a);b.max_height=Math.max(b.max_height,a.height());b.content_div.css("height",b.max_height+"px");if(a.hasClass(FILTERABLE_CLASS)){show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters");if(b.filters_visible){b.filtering_div.show()}}else{show_hide_popupmenu_options(b.popup_menu,"(Show|Hide) filters",false);b.filtering_div.hide()}},set_overview:function(){var a=this.view;if(this.initial_canvas&&this.is_overview){a.overview_close.show();a.overview_viewport.append(this.initial_canvas);a.overview_highlight.show().height(this.initial_canvas.height());a.overview_viewport.height(this.initial_canvas.height()+a.overview_box.height())}$(window).trigger("resize")}});var LabelTrack=function(a,b){this.track_type="LabelTrack";this.hidden=true;Track.call(this,null,a,b);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";this.hidden=true;Track.call(this,null,a,a.top_labeltrack);TiledTrack.call(this);this.left_offset=200;this.height_px=12;this.container_div.addClass("reference-track");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,i){console.log(h,g,i)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,j=f+"_"+b;var e=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(e)}e=$(e);var n=e.get(0).getContext("2d");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),i=Math.round(o/2);n.fillText(m[h],a+this.left_offset+i,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b,a,c){this.track_type="LineTrack";this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";Track.call(this,d,b,b.viewport_conta
iner);TiledTrack.call(this);this.height_px=80;this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={color:"black",min_value:undefined,max_value:undefined,mode:this.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");var e=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=e.min;a.prefs.max_value=e.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=e.total_frequency;a.container_div.find(".yaxislabel").remove();var f=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(round_1000(a.prefs.min_value));v
ar d=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(round_1000(a.prefs.max_value));d.css({position:"absolute",top:"22px",left:"10px"});d.prependTo(a.container_div);f.css({position:"absolute",top:a.height_px+11+"px",left:"10px"});f.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){var h=g.data;c.data_cache.set(e,h);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,r,c,e){if(this.vertical_range===undefined){return}var s=r*DENSITY*o,a=DENSITY*o,w=o+"_"+r;var b=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(b)}b=$(b);if(this.data_cache.get(w)===undefined){this.get_data(o,r);return}var v=this.data_cache.
get(w);if(!v){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),j=false,k=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,t=this.total_frequency,d=this.height_px,l=this.mode;n.beginPath();n.fillStyle=this.prefs.color;var u,h,f;if(v.length>1){f=Math.ceil((v[1][0]-v[0][0])*e)}else{f=10}for(var p=0,q=v.length;p<q;p++){u=Math.round((v[p][0]-s)*e);h=v[p][1];if(h===null){if(j&&l==="Filled"){n.lineTo(u,d)}j=false;continue}if(h<k){h=k}else{if(h>g){h=g}}if(l==="Histogram"){h=Math.round(d-(h-k)/m*d);n.fillRect(u,h,f,d-h)}else{if(l==="Intensity"){h=255-Math.floor((h-k)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(u,0,f,d)}else{h=Math.round(d-(h-k)/m*d);if(j){n.lineTo(u,h)}else{j=true;if(l==="Filled"){n.moveTo(u,d);n.lineTo(u,h)}else{n.moveTo(u,h)}}}}}if(l==="Filled"){if(j){n.lineTo(u,d)}n.fill()}else{n.stroke()}c.append(b);return b},gen_options:funct
ion(m){var a=$("<div />").addClass("form-row");var e="track_"+m+"_color",b=$("<label />").attr("for",e).text("Color:"),c=$("<input />").attr("id",e).attr("name",e).val(this.prefs.color),h="track_"+m+"_minval",l=$("<label></label>").attr("for",h).text("Min value:"),d=(this.prefs.min_value===undefined?"":this.prefs.min_value),k=$("<input></input>").attr("id",h).val(d),j="track_"+m+"_maxval",g=$("<label></label>").attr("for",j).text("Max value:"),i=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",j).val(i);return a.append(l).append(k).append(g).append(f).append(b).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_color").val();if(a!==this.prefs.min_value||c!==this.prefs.max_value||b!==this.prefs.color){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.color=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetr
ack_"+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,e,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container,e);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.summary_draw_height=30;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.left_offset=200;this.prefs={block_color:"#444",label_color:"black",show_counts:true}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b="initial";this.init_each({low:a.view.max_low,high:a.view.m
ax_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution,mode:a.mode},function(c){a.mode_div.show();a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,b,q){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=q;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,y=[],h=0,n=this.view.max_low;var A=[];if(this.inc_slots[a].mode!==q){delete this.inc_slots[a];this.inc_slots[a]={mode:q,w_scale:m};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var v=0,w=g.length;v<w;v++){var f=g[v],l=f[0];if(this.inc_slots[a][l]!==undefined){h=Math.max(h,this.inc_slots[a][l]);A.push(this.inc_slots[a][l])}else{y.push(v)}}for(var v=0,w
=y.length;v<w;v++){var f=g[y[v]],l=f[0],r=f[1],c=f[2],p=f[3],d=Math.floor((r-n)*m),e=Math.ceil((c-n)*m);if(p!==undefined&&!b){var s=CONTEXT.measureText(p).width;if(d-s<0){e+=s}else{d-=s}}var u=0;while(u<=MAX_FEATURE_DEPTH){var o=true;if(this.s_e_by_tile[a][u]!==undefined){for(var t=0,z=this.s_e_by_tile[a][u].length;t<z;t++){var x=this.s_e_by_tile[a][u][t];if(e>x[0]&&d<x[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][u]===undefined){this.s_e_by_tile[a][u]=[]}this.s_e_by_tile[a][u].push([d,e]);this.inc_slots[a][l]=u;h=Math.max(h,u);break}u++}}return h},rect_or_text:function(r,l,t,b,q,f,i,e){r.textAlign="center";var k=0,p=Math.round(l/2);for(var m=0,s=i.length;m<s;m++){var j=i[m],d="MIDNSHP"[j[0]],n=j[1];if(d==="H"||d==="S"){k-=n}var g=q+k,w=Math.floor(Math.max(0,(g-t)*l)),h=Math.floor(Math.max(0,(g+n-t)*l));switch(d){case"S":case"H":case"M":var o=f.slice(k,n);if((this.mode==="Pack"||this.mode==="Auto")&&f!==undefined&&l>PX_PER_CHAR){r.fillStyle=this.prefs.block_color;r.fillR
ect(w+this.left_offset,e+1,h-w,9);r.fillStyle=CONNECTOR_COLOR;for(var u=0,a=o.length;u<a;u++){if(g+u>=t&&g+u<=b){var v=Math.floor(Math.max(0,(g+u-t)*l));r.fillText(o[u],v+this.left_offset+p,e+9)}}}else{r.fillStyle=this.prefs.block_color;r.fillRect(w+this.left_offset,e+4,h-w,3)}break;case"N":r.fillStyle=CONNECTOR_COLOR;r.fillRect(w+this.left_offset,e+5,h-w,1);break;case"D":r.fillStyle="red";r.fillRect(w+this.left_offset,e+4,h-w,3);break;case"P":case"I":break}k+=n}},draw_tile:function(ag,o,s,av){var N=o*DENSITY*ag,al=(o+1)*DENSITY*ag,M=al-N;var an=(!this.initial_canvas?"initial":N+"_"+al);var I=this.data_cache.get(an);var e;if(I===undefined||(this.mode!=="Auto"&&I.dataset_type==="summary_tree")){this.data_queue[[N,al]]=true;this.get_data(N,al);return}var a=Math.ceil(M*av),ai=this.prefs.label_color,l=this.prefs.block_color,r=this.mode,z=25,ae=(r==="Squish")||(r==="Dense")&&(r!=="Pack")||(r==="Auto"&&(I.extra_info==="no_detail")),W=this.left_offset,au,D,aw;var q=document.createE
lement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(q)}q=$(q);if(I.dataset_type==="summary_tree"){D=this.summary_draw_height}else{if(r==="Dense"){D=z;aw=10}else{aw=(ae?this.vertical_nodetail_px:this.vertical_detail_px);var A=(av<0.0001?1/this.view.zoom_res:av);D=this.incremental_slots(A,I.data,ae,r)*aw+z;au=this.inc_slots[A]}}q.css({position:"absolute",top:0,left:(N-this.view.low)*av-W});q.get(0).width=a+W;q.get(0).height=D;s.parent().css("height",Math.max(this.height_px,D)+"px");var J=q.get(0).getContext("2d");J.fillStyle=l;J.font=this.default_font;J.textAlign="right";this.container_div.find(".yaxislabel").remove();if(I.dataset_type=="summary_tree"){var Y=I.data,L=I.max,b=Math.ceil(I.delta*av);var p=$("<div />").addClass("yaxislabel").text(L);p.css({position:"absolute",top:"22px",left:"10px"});p.prependTo(this.container_div);for(var ap=0,H=Y.length;ap<H;ap++){var aa=Math.floor((Y[ap][0]-N)*av);var Z=Y[ap][1];if(!Z){continue}var am=Z/L*this.summary_
draw_height;J.fillStyle="black";J.fillRect(aa+W,this.summary_draw_height-am,b,am);if(this.prefs.show_counts&&J.measureText(Z).width<b){J.fillStyle="#bbb";J.textAlign="center";J.fillText(Z,aa+W+(b/2),this.summary_draw_height-5)}}e="Summary";s.append(q);return q}if(I.message){q.css({border:"solid red","border-width":"2px 2px 2px 0px"});J.fillStyle="red";J.textAlign="left";J.fillText(I.message,100+W,aw)}var ad=false;if(I.data){ad=true;for(var ar=0;ar<this.filters.length;ar++){if(!this.filters[ar].applies_to(I.data[0])){ad=false}}}if(ad){q.addClass(FILTERABLE_CLASS)}var at=I.data;var ao=0;for(var ap=0,H=at.length;ap<H;ap++){var S=at[ap],R=S[0],aq=S[1],ac=S[2],O=S[3];if(au[R]===undefined){continue}var ab=false;var U;for(var ar=0;ar<this.filters.length;ar++){U=this.filters[ar];U.update_attrs(S);if(!U.keep(S)){ab=true;break}}if(ab){continue}if(aq<=al&&ac>=N){var af=Math.floor(Math.max(0,(aq-N)*av)),K=Math.ceil(Math.min(a,Math.max(0,(ac-N)*av))),X=(r==="Dense"?1:(1+au[R]))*aw;var G,
aj,P=null,ax=null;if(I.dataset_type==="bai"){var v=S[4];J.fillStyle=l;if(S[5] instanceof Array){var E=Math.floor(Math.max(0,(S[5][0]-N)*av)),Q=Math.ceil(Math.min(a,Math.max(0,(S[5][1]-N)*av))),C=Math.floor(Math.max(0,(S[6][0]-N)*av)),w=Math.ceil(Math.min(a,Math.max(0,(S[6][1]-N)*av)));if(S[5][1]>=N&&S[5][0]<=al){this.rect_or_text(J,av,N,al,S[5][0],S[5][2],v,X)}if(S[6][1]>=N&&S[6][0]<=al){this.rect_or_text(J,av,N,al,S[6][0],S[6][2],v,X)}if(C>Q){J.fillStyle=CONNECTOR_COLOR;J.fillRect(Q+W,X+5,C-Q,1)}}else{J.fillStyle=l;this.rect_or_text(J,av,N,al,aq,O,v,X)}if(r!=="Dense"&&!ae&&aq>N){J.fillStyle=this.prefs.label_color;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(R,K+2+W,X+8)}else{J.textAlign="right";J.fillText(R,af-2+W,X+8)}J.fillStyle=l}}else{if(I.dataset_type==="interval_index"){if(ae){J.fillStyle=l;J.fillRect(af+W,X+5,K-af,1)}else{var F=S[4],V=S[5],ah=S[6],h=S[7];if(V&&ah){P=Math.floor(Math.max(0,(V-N)*av));ax=Math.ceil(Math.min(a,Math.max(0,(ah-N)*av)
))}if(r!=="Dense"&&O!==undefined&&aq>N){J.fillStyle=ai;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(O,K+2+W,X+8)}else{J.textAlign="right";J.fillText(O,af-2+W,X+8)}J.fillStyle=l}if(h){if(F){if(F=="+"){J.fillStyle=RIGHT_STRAND}else{if(F=="-"){J.fillStyle=LEFT_STRAND}}J.fillRect(af+W,X,K-af,10);J.fillStyle=l}for(var an=0,g=h.length;an<g;an++){var u=h[an],d=Math.floor(Math.max(0,(u[0]-N)*av)),T=Math.ceil(Math.min(a,Math.max((u[1]-N)*av)));if(d>T){continue}G=5;aj=3;J.fillRect(d+W,X+aj,T-d,G);if(P!==undefined&&!(d>ax||T<P)){G=9;aj=1;var ak=Math.max(d,P),B=Math.min(T,ax);J.fillRect(ak+W,X+aj,B-ak,G)}}}else{G=9;aj=1;J.fillRect(af+W,X+aj,K-af,G);if(S.strand){if(S.strand=="+"){J.fillStyle=RIGHT_STRAND_INV}else{if(S.strand=="-"){J.fillStyle=LEFT_STRAND_INV}}J.fillRect(af+W,X,K-af,10);J.fillStyle=l}}}}else{if(I.dataset_type==="vcf"){if(ae){J.fillStyle=l;J.fillRect(af+W,X+5,K-af,1)}else{var t=S[4],n=S[5],c=S[6];G=9;aj=1;J.fillRect(af+W,X,K-af,G);if(r!=="Dense"&&O!
==undefined&&aq>N){J.fillStyle=ai;if(o===0&&af-J.measureText(O).width<0){J.textAlign="left";J.fillText(O,K+2+W,X+8)}else{J.textAlign="right";J.fillText(O,af-2+W,X+8)}J.fillStyle=l}var m=t+" / "+n;if(aq>N&&J.measureText(m).width<(K-af)){J.fillStyle="white";J.textAlign="center";J.fillText(m,W+af+(K-af)/2,X+8);J.fillStyle=l}}}}}ao++}}return q},gen_options:function(i){var a=$("<div />").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label />").attr("for",e).text("Block color:"),l=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label />").attr("for",j).text("Text color:"),h=$("<input />").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(k).append(
l).append(g).append(h).append(d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,e,c){FeatureTrack.call(this,d,b,a,e,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
1
0
galaxy-dist commit 591c53b44ae4: Fixes for links that direct admin and regular users to different views of selected or transferred sample dataset files.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '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 1289487850 18000
# Node ID 591c53b44ae4a6237a841adaa3abfe59829beebe
# Parent bbc0f56e3e16f93bf3c81d09b0fa12876a3f2579
Fixes for links that direct admin and regular users to different views of selected or transferred sample dataset files.
--- a/templates/requests/common/common.mako
+++ b/templates/requests/common/common.mako
@@ -125,7 +125,7 @@
can_delete_samples = not is_complete
display_checkboxes = editing_samples and ( is_complete or is_rejected or is_submitted )
display_bar_code = request.samples and ( is_complete or is_rejected or is_submitted )
- display_datasets = request.samples and ( is_complete or is_rejected or is_submitted )
+ display_datasets = request.samples and ( is_complete or is_submitted )
else:
is_complete = False
is_submitted = False
@@ -175,33 +175,44 @@
<td valign="top">
## An admin can select the datasets to transfer, while a non-admin can only view what has been selected
%if is_admin:
- <a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_admin', action='select_datasets_to_transfer', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
+ ## This link will direct the admin to a page allowing them to select datasets.
+ <a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_admin', action='select_datasets_to_transfer', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id= trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
%elif sample.datasets:
- ## Only display a link if there is at least 1 selected dataset for the sample
- <a href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a></td>
+ ## Since this is a regular user, only display a link if there is at least 1
+ ## selected dataset for the sample.
+ <a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
%else:
+ ## Since this is a regular user, do not display a link if there are no datasets.
${len( sample.datasets )}
%endif
</td><td valign="top">
%if is_admin:
- %if sample.untransferred_dataset_files:
+ <%
+ if sample.transferred_dataset_files:
+ transferred_dataset_files = sample.transferred_dataset_files
+ else:
+ transferred_dataset_files = []
+ %>
+ %if not sample.datasets:
+ ## No datasets have been selected for this sample, so don't include a link
+ ${len( sample.transferred_dataset_files )}
+ %elif len( sample.datasets ) > len( transferred_dataset_files ):
## At least 1 selected dataset is not yet transferred, so this link
## will direct the admin to a page allowing them to transfer datasets.
- <a href="${h.url_for( controller='requests_common', action='manage_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.transferred_dataset_files )}</a>
- %else:
+ <a href="${h.url_for( controller='requests_admin', action='manage_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.transferred_dataset_files )}</a>
+ %elif len( sample.datasets ) == len( transferred_dataset_files ):
## All selected datasets have successfully transferred, so this link
## will direct the admin to a page displaying all transferred datasets.
<a href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ), transfer_status=trans.model.SampleDataset.transfer_status.COMPLETE )}">${len( sample.transferred_dataset_files )}</a>
%endif
%else:
%if sample.transferred_dataset_files:
- ## The cuurent user is not an admin, so this link will direct the
- ## user to the target data library containing those datasets that
- ## were successfully transferred.
+ ## Since this is a regular user, this link will direct them to the target
+ ## data library containing those datasets that were successfully transferred.
<a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library', id=trans.security.encode_id( sample.library.id ) )}">${len( sample.transferred_dataset_files )}</a>
%else:
- ## Display a 0 with no link.
+ ## Since this is a regular user, do not display a link.
${len( sample.transferred_dataset_files )}
%endif
%endif
@@ -228,7 +239,7 @@
can_select_datasets = is_admin and current_samples and ( is_submitted or is_complete )
display_checkboxes = editing_samples and ( is_complete or is_rejected or is_submitted )
display_bar_code = request.samples and ( is_complete or is_rejected or is_submitted )
- display_datasets = request.samples and ( is_complete or is_rejected or is_submitted )
+ display_datasets = request.samples and ( is_complete or is_submitted )
%>
${grid_header}
%if render_buttons and ( can_add_samples or can_edit_samples ):
@@ -314,39 +325,44 @@
<td>
## An admin can select the datasets to transfer, while a non-admin can only view what has been selected
%if is_admin:
- %if not sample.datasets:
- ## If there are no selected datasets, display a page alowing the admin to select some.
- <a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_admin', action='select_datasets_to_transfer', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id= trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
- %else:
- ## If there are selected datasets, display them
- <a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
- %endif
+ ## This link will direct the admin to a page allowing them to select datasets.
+ <a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_admin', action='select_datasets_to_transfer', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id= trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
%elif sample.datasets:
- ## Only display a link if there is at least 1 selected dataset for the sample
+ ## Since this is a regular user, only display a link if there is at least 1
+ ## selected dataset for the sample.
<a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
%else:
+ ## Since this is a regular user, do not display a link if there are no datasets.
${len( sample.datasets )}
%endif
</td><td>
%if is_admin:
- %if sample.untransferred_dataset_files:
+ <%
+ if sample.transferred_dataset_files:
+ transferred_dataset_files = sample.transferred_dataset_files
+ else:
+ transferred_dataset_files = []
+ %>
+ %if not sample.datasets:
+ ## No datasets have been selected for this sample, so don't include a link
+ ${len( sample.transferred_dataset_files )}
+ %elif len( sample.datasets ) > len( transferred_dataset_files ):
## At least 1 selected dataset is not yet transferred, so this link
## will direct the admin to a page allowing them to transfer datasets.
- <a href="${h.url_for( controller='requests_common', action='manage_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.transferred_dataset_files )}</a>
- %else:
+ <a href="${h.url_for( controller='requests_admin', action='manage_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.transferred_dataset_files )}</a>
+ %elif len( sample.datasets ) == len( transferred_dataset_files ):
## All selected datasets have successfully transferred, so this link
## will direct the admin to a page displaying all transferred datasets.
<a href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ), transfer_status=trans.model.SampleDataset.transfer_status.COMPLETE )}">${len( sample.transferred_dataset_files )}</a>
%endif
%else:
%if sample.transferred_dataset_files:
- ## The cuurent user is not an admin, so this link will direct the
- ## user to the target data library containing those datasets that
- ## were successfully transferred.
+ ## Since this is a regular user, this link will direct them to the target
+ ## data library containing those datasets that were successfully transferred.
<a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library', id=trans.security.encode_id( sample.library.id ) )}">${len( sample.transferred_dataset_files )}</a>
%else:
- ## Display a 0 with no link.
+ ## Since this is a regular user, do not display a link.
${len( sample.transferred_dataset_files )}
%endif
%endif
--- a/templates/requests/common/view_sample_datasets.mako
+++ b/templates/requests/common/view_sample_datasets.mako
@@ -37,7 +37,7 @@
${render_sample_datasets( cntrller, sample, sample_datasets, title )}
%else:
%if transfer_status:
- No datasets with status ${transfer_status}" belong to this sample
+ No datasets with status "${transfer_status}" belong to this sample
%else:
No datasets have been selected for this sample.
%endif
1
0
galaxy-dist commit b1813ff5bb4e: Bug fixes and more UI streamlining for sample tracking.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '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 1289426620 18000
# Node ID b1813ff5bb4ef20d0b5d5a1fdcfc16cf6ec3ba53
# Parent 0e5144e49c14eb2a77e1d3182a33b4f9c8de98fc
Bug fixes and more UI streamlining for sample tracking.
--- a/templates/requests/common/sample_datasets.mako
+++ /dev/null
@@ -1,5 +0,0 @@
-<%def name="render_sample_datasets( sample )">
- ${len( sample.datasets )} / ${len( sample.transferred_dataset_files )}
-</%def>
-
-${render_sample_datasets( sample )}
--- /dev/null
+++ b/templates/requests/common/view_sample_datasets.mako
@@ -0,0 +1,43 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+<%namespace file="/requests/common/common.mako" import="render_sample_datasets" />
+
+<%
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ is_complete = sample.request.is_complete
+ is_submitted = sample.request.is_submitted
+ can_select_datasets = is_admin and ( is_complete or is_submitted )
+ can_transfer_datasets = is_admin and sample.untransferred_dataset_files
+%>
+
+<br/><br/>
+
+<ul class="manage-table-actions">
+ %if can_transfer_datasets:
+ <li><a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">Transfer datasets</a></li>
+ %endif
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ), transfer_status=transfer_status )}">Refresh page</a></li>
+ <li><a class="action-button" id="sample-${sample.id}-popup" class="menubutton">Dataset Actions</a></li>
+ <div popupmenu="sample-${sample.id}-popup">
+ %if can_select_datasets:
+ <li><a class="action-button" href="${h.url_for( controller='requests_admin', action='select_datasets_to_transfer', cntrller=cntrller, request_id=trans.security.encode_id( sample.request.id ), sample_id=trans.security.encode_id( sample.id ) )}">Select more datasets</a></li>
+ %endif
+ <li><a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( sample.library.id ) )}">View target Data Library</a></li>
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a></li>
+ </div>
+</ul>
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+%if sample and sample_datasets:
+ ## The list of sample_datasets may not be the same as sample.datasets because it may be
+ ## filtered by a transfer_status value. The value of title changes based on this filter.
+ ${render_sample_datasets( cntrller, sample, sample_datasets, title )}
+%else:
+ %if transfer_status:
+ No datasets with status ${transfer_status}" belong to this sample
+ %else:
+ No datasets have been selected for this sample.
+%endif
--- a/templates/requests/common/edit_samples.mako
+++ b/templates/requests/common/edit_samples.mako
@@ -41,15 +41,15 @@
%if can_submit:
<li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
%endif
- <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request Actions</a></li><div popupmenu="request-${request.id}-popup"><a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
%if can_edit_request:
- <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit this request</a>
%endif
<a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">View history</a>
%if can_reject:
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject this request</a>
%endif
</div></ul>
@@ -118,7 +118,7 @@
## Render the other grids
<% trans.sa_session.refresh( request.type.sample_form ) %>
%for grid_index, grid_name in enumerate( request.type.sample_form.layout ):
- ${render_request_type_sample_form_grids( grid_index, grid_name, request.type.sample_form.grid_fields( grid_index ), editing_samples=editing_samples )}
+ ${render_request_type_sample_form_grids( grid_index, grid_name, request.type.sample_form.grid_fields( grid_index ), current_samples=current_samples, editing_samples=editing_samples )}
%endfor
%else:
<label>There are no samples.</label>
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1831,12 +1831,12 @@ class Sample( object ):
return self.events[0]
return None
@property
- def untransferred_dataset_files( self ):
- untransferred_datasets = []
+ def adding_to_library_dataset_files( self ):
+ adding_to_library_datasets = []
for dataset in self.datasets:
- if dataset.status == SampleDataset.transfer_status.NOT_STARTED:
- untransferred_datasets.append( dataset )
- return untransferred_datasets
+ if dataset.status == SampleDataset.transfer_status.ADD_TO_LIBRARY:
+ adding_to_library_datasets.append( dataset )
+ return adding_to_library_datasets
@property
def inprogress_dataset_files( self ):
inprogress_datasets = []
@@ -1845,12 +1845,40 @@ class Sample( object ):
inprogress_datasets.append( dataset )
return inprogress_datasets
@property
+ def queued_dataset_files( self ):
+ queued_datasets = []
+ for dataset in self.datasets:
+ if dataset.status == SampleDataset.transfer_status.IN_QUEUE:
+ queued_datasets.append( dataset )
+ return queued_datasets
+ @property
+ def transfer_error_dataset_files( self ):
+ transfer_error_datasets = []
+ for dataset in self.datasets:
+ if dataset.status == SampleDataset.transfer_status.ERROR:
+ transfer_error_datasets.append( dataset )
+ return transfer_error_datasets
+ @property
def transferred_dataset_files( self ):
transferred_datasets = []
for dataset in self.datasets:
if dataset.status == SampleDataset.transfer_status.COMPLETE:
transferred_datasets.append( dataset )
return transferred_datasets
+ @property
+ def transferring_dataset_files( self ):
+ transferring_datasets = []
+ for dataset in self.datasets:
+ if dataset.status == SampleDataset.transfer_status.TRANSFERRING:
+ transferring_datasets.append( dataset )
+ return transferring_datasets
+ @property
+ def untransferred_dataset_files( self ):
+ untransferred_datasets = []
+ for dataset in self.datasets:
+ if dataset.status != SampleDataset.transfer_status.COMPLETE:
+ untransferred_datasets.append( dataset )
+ return untransferred_datasets
def get_untransferred_dataset_size( self, filepath ):
# TODO: RC: If rsh keys are not set, this method will return something like the following:
# greg(a)scofield.bx.psu.edu's password: 46M /afs/bx.psu.edu/home/greg/chr22/chr21.fa
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -109,10 +109,7 @@ class RequestsCommon( BaseController, Us
"datasets": len( sample.datasets ),
"html_state": unicode( trans.fill_template( "requests/common/sample_state.mako",
sample=sample),
- 'utf-8' ),
- "html_datasets": unicode( trans.fill_template( "requests/common/sample_datasets.mako",
- sample=sample ),
- 'utf-8' ) }
+ 'utf-8' ) }
return rval
@web.expose
@web.require_login( "create sequencing requests" )
@@ -455,7 +452,7 @@ class RequestsCommon( BaseController, Us
samples.append( sample )
else:
samples.append( None )
- # The __save_samples method requires samples widgets, not sample objects
+ # The __save_samples method requires sample_widgets, not sample objects
samples = self.__get_sample_widgets( trans, request, samples, **kwd )
else:
samples = current_samples
@@ -699,9 +696,9 @@ class RequestsCommon( BaseController, Us
# are in this state.
retval = request.send_email_notification( trans, common_state, final_state )
if retval:
- message = comments + retval
+ message = comment + retval
else:
- message = comments
+ message = comment
if cntrller == 'api':
return 200, message
return trans.response.send_redirect( web.url_for( controller='requests_common',
@@ -897,7 +894,7 @@ class RequestsCommon( BaseController, Us
message=message ) )
@web.expose
@web.require_login( "view data transfer page" )
- def view_selected_datasets( self, trans, cntrller, **kwd ):
+ def view_sample_datasets( self, trans, cntrller, **kwd ):
# The link on the number of selected datasets will only appear if there is at least 1 selected dataset.
# If there are 0 selected datasets, there is no link, so this method will only be reached from the requests
# controller if there are selected datasets.
@@ -930,13 +927,34 @@ class RequestsCommon( BaseController, Us
if folder_path and folder_path[-1] != os.sep:
folder_path += os.sep
if not sample.request.type.datatx_info['host'] \
- or not sample.request.type.datatx_info['username'] \
- or not sample.request.type.datatx_info['password']:
+ or not sample.request.type.datatx_info[ 'username' ] \
+ or not sample.request.type.datatx_info[ 'password' ]:
status = 'error'
message = 'The sequencer login information is incomplete. Click sequencer information to add login details.'
- return trans.fill_template( '/requests/common/view_selected_datasets.mako',
+ transfer_status = params.get( 'transfer_status', None )
+ if transfer_status in [ None, 'None' ]:
+ title = 'All selected datasets for "%s"' % sample.name
+ sample_datasets = sample.datasets
+ elif transfer_status == trans.model.SampleDataset.transfer_status.IN_QUEUE:
+ title = 'Datasets of "%s" that are in the transfer queue' % sample.name
+ sample_datasets = sample.queued_dataset_files
+ elif transfer_status == trans.model.SampleDataset.transfer_status.TRANSFERRING:
+ title = 'Datasets of "%s" that are being transferred' % sample.name
+ sample_datasets = sample.transferring_dataset_files
+ elif transfer_status == trans.model.SampleDataset.transfer_status.ADD_TO_LIBRARY:
+ title = 'Datasets of "%s" that are being added to the target data library' % sample.name
+ sample_datasets = sample.adding_to_library_dataset_files
+ elif transfer_status == trans.model.SampleDataset.transfer_status.COMPLETE:
+ title = 'Datasets of "%s" that are available in the target data library' % sample.name
+ sample_datasets = sample.transferred_dataset_files
+ elif transfer_status == trans.model.SampleDataset.transfer_status.ERROR:
+ title = 'Datasets of "%s" that resulted in a transfer error' % sample.name
+ sample_datasets = sample.transfer_error_dataset_files
+ return trans.fill_template( '/requests/common/view_sample_datasets.mako',
cntrller=cntrller,
sample=sample,
+ sample_datasets=sample_datasets,
+ transfer_status=transferr_status,
message=message,
status=status,
files=[],
@@ -1129,60 +1147,48 @@ class RequestsCommon( BaseController, Us
# on a set of samples.
library_id = params.get( 'sample_0_library_id', 'none' )
folder_id = params.get( 'sample_0_folder_id', 'none' )
- for index, obj in enumerate( sample_widgets ):
- if obj is not None:
- # obj will be None if the user checked sample check boxes and selected an action
+ for index, sample_widget in enumerate( sample_widgets ):
+ if sample_widget is not None:
+ # sample_widget will be None if the user checked sample check boxes and selected an action
# to perform on multiple samples, but did not select certain samples.
sample = request.samples[ index ]
- # See if any values in kwd are different from the values already associated with this sample.
- id_index = index + 1
- if sample_operation == 'none':
- # We are handling changes to a single sample.
- library_id = params.get( 'sample_%i_library_id' % id_index, 'none' )
- folder_id = params.get( 'sample_%i_folder_id' % id_index, 'none' )
- # Update the corresponding sample's values as well as the sample_widget.
- name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
- # The bar_code field requires special handling because after a request is submitted, the
- # state of a sample cannot be changed without a bar_code associated with the sample. Bar
- # codes can only be added to a sample after the request is submitted. Also, a samples will
- # not have an associated SampleState until the request is submitted, at which time the sample
- # is automatically associated with the first SamplesState configured by the admin for the
- # request's RequestType.
- bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
- if bar_code:
- bc_message = self.__validate_barcode( trans, sample, bar_code )
- if bc_message:
- kwd[ 'message' ] = bc_message
- del kwd[ 'save_samples_button' ]
- handle_error( **kwd )
- if not sample.bar_code:
- # If the sample's associated SampleState is still the initial state
- # configured by the admin for the request's RequestType, this must be
- # the first time a bar code was added to the sample, so change it's state
- # to the next associated SampleState.
- if sample.state.id == request.type.states[0].id:
- event = trans.app.model.SampleEvent(sample,
- request.type.states[1],
- 'Bar code associated with the sample' )
- trans.sa_session.add( event )
- trans.sa_session.flush()
- library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
- field_values = []
- for field_index in range( len( request.type.sample_form.fields ) ):
- field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ # Get the sample's form values to see if they have changed.
form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
- form_values.content = field_values
- if sample.name != name or \
- sample.bar_code != bar_code or \
- sample.library != library or \
- sample.folder != folder or \
- form_values.content != field_values:
+ if sample.name != sample_widget[ 'name' ] or \
+ sample.bar_code != sample_widget[ 'barcode' ] or \
+ sample.library != sample_widget[ 'library' ] or \
+ sample.folder != sample_widget[ 'folder' ] or \
+ form_values.content != sample_widget[ 'field_values' ]:
# Information about this sample has been changed.
- sample.name = name
- sample.bar_code = bar_code
- sample.library = library
- sample.folder = folder
- form_values.content = field_values
+ sample.name = sample_widget[ 'name' ]
+ barcode = sample_widget[ 'barcode' ]
+ # The bar_code field requires special handling because after a request is submitted, the
+ # state of a sample cannot be changed without a bar_code associated with the sample. Bar
+ # codes can only be added to a sample after the request is submitted. Also, a samples will
+ # not have an associated SampleState until the request is submitted, at which time the sample
+ # is automatically associated with the first SamplesState configured by the admin for the
+ # request's RequestType.
+ if barcode:
+ bc_message = self.__validate_barcode( trans, sample, bar_code )
+ if bc_message:
+ kwd[ 'message' ] = bc_message
+ del kwd[ 'save_samples_button' ]
+ handle_error( **kwd )
+ if not sample.bar_code:
+ # If the sample's associated SampleState is still the initial state
+ # configured by the admin for the request's RequestType, this must be
+ # the first time a bar code was added to the sample, so change it's state
+ # to the next associated SampleState.
+ if sample.state.id == request.type.states[0].id:
+ event = trans.app.model.SampleEvent(sample,
+ request.type.states[1],
+ 'Bar code associated with the sample' )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ sample.bar_code = barcode
+ sample.library = sample_widget[ 'library' ]
+ sample.folder = sample_widget[ 'folder' ]
+ form_values.content = sample_widget[ 'field_values' ]
trans.sa_session.add_all( ( sample, form_values ) )
trans.sa_session.flush()
def __get_library_and_folder( self, trans, library_id, folder_id ):
@@ -1232,8 +1238,8 @@ class RequestsCommon( BaseController, Us
"""
Returns a list of dictionaries, each representing the widgets that define a sample on a form.
The widgets are populated from kwd based on the set of samples received. The set of samples
- corresponds to a reques.samples list, but if the user checked specific check boxes on the form,
- those samples that were not check will have None objects in the list of samples. In this case,
+ corresponds to a request.samples list, but if the user checked specific check boxes on the form,
+ those samples that were not checked will have None objects in the list of samples. In this case,
the corresponding sample_widget is populated from the db rather than kwd.
"""
params = util.Params( kwd )
@@ -1264,7 +1270,7 @@ class RequestsCommon( BaseController, Us
bar_code = sample.bar_code
library = sample.library
folder = sample.folder
- field_values = sample.values.content,
+ field_values = sample.values.content
else:
# Update the sample attributes from kwd
name = util.restore_text( params.get( 'sample_%i_name' % index, sample.name ) )
@@ -1278,7 +1284,8 @@ class RequestsCommon( BaseController, Us
library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
field_values = []
for field_index in range( len( request.type.sample_form.fields ) ):
- field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ field_value = util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), sample.values.content[ field_index ] ) )
+ field_values.append( field_value )
library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans=trans,
user=request.user,
sample_index=id_index,
--- a/templates/requests/common/find_samples.mako
+++ b/templates/requests/common/find_samples.mako
@@ -71,7 +71,7 @@
%else:
State: ${sample.state.name}<br/>
%endif
- Datasets: <a href="${h.url_for( controller='requests_common', action='view_selected_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.transferred_dataset_files )}/${len( sample.datasets )}</a><br/>
+ Datasets: <a href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a><br/>
%if is_admin:
<i>User: ${sample.request.user.email}</i>
%endif
--- a/templates/requests/common/view_selected_datasets.mako
+++ /dev/null
@@ -1,37 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-<%namespace file="/requests/common/common.mako" import="render_sample_datasets" />
-
-<%
- is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
- is_complete = sample.request.is_complete
- is_submitted = sample.request.is_submitted
- can_select_datasets = is_admin and ( is_complete or is_submitted )
- can_transfer_datasets = is_admin and sample.untransferred_dataset_files
-%>
-
-<br/><br/>
-
-<ul class="manage-table-actions">
- %if can_transfer_datasets:
- <li><a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">Transfer datasets</a></li>
- %endif
- <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_selected_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">Refresh page</a></li>
- <li><a class="action-button" id="sample-${sample.id}-popup" class="menubutton">Dataset Actions</a></li>
- <div popupmenu="sample-${sample.id}-popup">
- %if can_select_datasets:
- <li><a class="action-button" href="${h.url_for( controller='requests_admin', action='select_datasets_to_transfer', cntrller=cntrller, request_id=trans.security.encode_id( sample.request.id ), sample_id=trans.security.encode_id( sample.id ) )}">Select more datasets</a></li>
- %endif
- <li><a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( sample.library.id ) )}">View target Data Library</a></li>
- <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a></li>
- </div>
-</ul>
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-%if sample and sample.datasets:
- <% title = 'Datasets currently selected for "sample.name"' %>
- ${render_sample_datasets( cntrller, sample, sample.datasets, title )}
-%endif
--- a/templates/admin/requests/select_datasets_to_transfer.mako
+++ b/templates/admin/requests/select_datasets_to_transfer.mako
@@ -127,7 +127,7 @@
</div>
%if sample and sample.datasets:
- <% title = 'Datasets currently selected for "sample.name"' %>
+ <% title = 'Datasets currently selected for "%s"' % sample.name %><p/>
${render_sample_datasets( 'requests_admin', sample, sample.datasets, title )}
%endif
--- a/templates/requests/common/common.mako
+++ b/templates/requests/common/common.mako
@@ -100,8 +100,6 @@
// Replace HTML
var cell1 = $("#sampleState-" + id);
cell1.html( val.html_state );
- var cell2 = $("#sampleDatasets-" + id);
- cell2.html( val.html_datasets );
sample_states[ parseInt( id ) ] = val.state;
});
updater( sample_states );
@@ -115,14 +113,16 @@
</script></%def>
-<%def name="render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )">
+<%def name="render_editable_sample_row( cntrller, sample, current_sample_index, current_sample, encoded_selected_sample_ids )"><%
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
if sample:
trans.sa_session.refresh( sample.request )
is_complete = sample.request.is_complete
is_rejected = request.is_rejected
is_submitted = sample.request.is_submitted
is_unsubmitted = sample.request.is_unsubmitted
+ can_delete_samples = not is_complete
display_checkboxes = editing_samples and ( is_complete or is_rejected or is_submitted )
display_bar_code = request.samples and ( is_complete or is_rejected or is_submitted )
display_datasets = request.samples and ( is_complete or is_rejected or is_submitted )
@@ -130,6 +130,7 @@
is_complete = False
is_submitted = False
is_unsubmitted = False
+ can_delete_samples = False
display_checkboxes = False
display_bar_code = False
display_datasets = False
@@ -177,20 +178,36 @@
<a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_admin', action='select_datasets_to_transfer', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
%elif sample.datasets:
## Only display a link if there is at least 1 selected dataset for the sample
- <a href="${h.url_for( controller='requests_common', action='view_selected_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a></td>
+ <a href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a></td>
%else:
${len( sample.datasets )}
%endif
</td><td valign="top">
- %if is_admin and sample.untransferred_dataset_files:
- <a href="${h.url_for( controller='requests_common', action='manage_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.transferred_dataset_files )}</a>
+ %if is_admin:
+ %if sample.untransferred_dataset_files:
+ ## At least 1 selected dataset is not yet transferred, so this link
+ ## will direct the admin to a page allowing them to transfer datasets.
+ <a href="${h.url_for( controller='requests_common', action='manage_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.transferred_dataset_files )}</a>
+ %else:
+ ## All selected datasets have successfully transferred, so this link
+ ## will direct the admin to a page displaying all transferred datasets.
+ <a href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ), transfer_status=trans.model.SampleDataset.transfer_status.COMPLETE )}">${len( sample.transferred_dataset_files )}</a>
+ %endif
%else:
- ${len( sample.transferred_dataset_files )}
+ %if sample.transferred_dataset_files:
+ ## The cuurent user is not an admin, so this link will direct the
+ ## user to the target data library containing those datasets that
+ ## were successfully transferred.
+ <a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library', id=trans.security.encode_id( sample.library.id ) )}">${len( sample.transferred_dataset_files )}</a>
+ %else:
+ ## Display a 0 with no link.
+ ${len( sample.transferred_dataset_files )}
+ %endif
%endif
</td>
%endif
- %if sample and ( is_admin or is_unsubmitted ) and not is_complete:
+ %if can_delete_samples:
## Delete button
<td valign="top"><a class="action-button" href="${h.url_for( controller='requests_common', action='delete_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id=current_sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a></td>
%endif
@@ -206,7 +223,7 @@
is_submitted = request.is_submitted
is_unsubmitted = request.is_unsubmitted
can_add_samples = request.is_unsubmitted
- can_delete_samples = request.samples and not is_complete
+ can_delete_samples = editing_samples and request.samples and not is_complete
can_edit_samples = request.samples and ( is_admin or not is_complete )
can_select_datasets = is_admin and current_samples and ( is_submitted or is_complete )
display_checkboxes = editing_samples and ( is_complete or is_rejected or is_submitted )
@@ -271,7 +288,7 @@
sample = None
%>
%if editing_samples:
- <tr>${render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
+ <tr>${render_editable_sample_row( cntrller, sample, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
%elif sample:
<tr><td>${current_sample_name}</td>
@@ -302,11 +319,11 @@
<a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_admin', action='select_datasets_to_transfer', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id= trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
%else:
## If there are selected datasets, display them
- <a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_selected_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
+ <a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
%endif
%elif sample.datasets:
## Only display a link if there is at least 1 selected dataset for the sample
- <a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_selected_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
+ <a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a>
%else:
${len( sample.datasets )}
%endif
@@ -322,7 +339,7 @@
</tr>
%else:
## The Add sample button was clicked for this sample_widget
- <tr>${render_editable_sample_row( is_admin, None, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
+ <tr>${render_editable_sample_row( cntrller, None, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
%endif
%endfor
</tbody>
@@ -390,7 +407,7 @@
</tr></%def>
-<%def name="render_request_type_sample_form_grids( grid_index, grid_name, fields_dict, editing_samples )">
+<%def name="render_request_type_sample_form_grids( grid_index, grid_name, fields_dict, current_samples, editing_samples )"><%
if not grid_name:
grid_name = "Sample form layout " + grid_index
@@ -432,6 +449,8 @@
</%def><%def name="render_sample_datasets( cntrller, sample, sample_datasets, title )">
+ ## The list of sample_datasets may not be the same as sample.datasets because it may be
+ ## filtered by a transfer_status value. The value of title changes based on this filter.
%if sample_datasets:
<%
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
--- a/templates/requests/common/events.mako
+++ b/templates/requests/common/events.mako
@@ -4,6 +4,7 @@
<%
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
+ can_reject_request = is_admin and request.is_submitted
can_add_samples = request.is_unsubmitted
%>
@@ -13,14 +14,13 @@
<div popupmenu="request-${request.id}-popup"><a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
%if can_edit_request:
- <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit this request</a>
%endif
%if can_add_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='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit</a>
+ <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='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit this request</a>
%endif
- %if is_admin and request.is_submitted:
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='select_datasets_to_transfer', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
+ %if can_reject_request:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject this request</a>
%endif
</div></ul>
--- a/templates/requests/common/edit_basic_request_info.mako
+++ b/templates/requests/common/edit_basic_request_info.mako
@@ -12,11 +12,11 @@
<div popupmenu="request-${request.id}-popup"><a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
%if can_add_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='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit</a>
+ <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='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit this request</a>
%endif
<a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">View history</a>
%if is_admin and request.is_submitted:
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject this request</a><a class="action-button" href="${h.url_for( controller='requests_admin', action='select_datasets_to_transfer', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
%endif
</div>
--- a/templates/requests/common/view_request.mako
+++ b/templates/requests/common/view_request.mako
@@ -27,6 +27,7 @@
can_edit_samples = request.samples and ( is_admin or not is_complete )
can_reject = is_admin and is_submitted
can_submit = request.samples and is_unsubmitted
+ can_undelete = request.deleted
%><br/><br/>
@@ -35,17 +36,17 @@
%if can_submit:
<li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
%endif
- <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request Actions</a></li><div popupmenu="request-${request.id}-popup">
- %if request.deleted:
- <a class="action-button" href="${h.url_for( controller='requests_common', action='undelete_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Undelete</a>
+ %if can_undelete:
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='undelete_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Undelete this request</a>
%endif
%if can_edit_request:
- <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit this request</a>
%endif
<a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">View history</a>
%if can_reject:
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject this request</a>
%endif
</div></ul>
@@ -164,5 +165,5 @@
## Render the other grids
<% trans.sa_session.refresh( request.type.sample_form ) %>
%for grid_index, grid_name in enumerate( request.type.sample_form.layout ):
- ${render_request_type_sample_form_grids( grid_index, grid_name, request.type.sample_form.grid_fields( grid_index ), editing_samples=False )}
+ ${render_request_type_sample_form_grids( grid_index, grid_name, request.type.sample_form.grid_fields( grid_index ), current_samples=current_samples, editing_samples=False )}
%endfor
1
0
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Kelly Vincent <kpvincent(a)bx.psu.edu>
# Date 1289493322 18000
# Node ID ef3e52251722c1086160643a5f700c5db75a71d4
# Parent 865d7175847ad322959f6b85532357d0f69afa46
# Parent 7ea4c156ae32c4757378229bfc49584df91a024a
Merge
1
0
galaxy-dist commit d56f296aaa61: Minimally functional splitting.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1289408765 18000
# Node ID d56f296aaa610a537d35cb7d235eac9d8f596464
# Parent d6a0dd8e55e2f7d2077bc282bca3e42d98fbb7f2
Minimally functional splitting.
Disabled by default, not advisable to enable on a production server (or main!) until scheduling issues are looked at and performance analysis has been done.
Tested 'basic' splitting with many basic tools (bowtie, bwa, filter sam, others). This style of splitting should work for most 'embarassingly parallel' one input, one output tools (no dependence between parallel tasks).
--- a/tools/sr_mapping/bowtie_wrapper.xml
+++ b/tools/sr_mapping/bowtie_wrapper.xml
@@ -1,6 +1,7 @@
<tool id="bowtie_wrapper" name="Map with Bowtie for Illumina" version="1.1.0"><requirements><requirement type='package'>bowtie</requirement></requirements><description></description>
+ <parallelism method="basic"></parallelism><command interpreter="python">
bowtie_wrapper.py
--threads="4"
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -1,10 +1,11 @@
-import logging, threading, sys, os, time, subprocess, string, tempfile, re, traceback, shutil
+import logging, threading, sys, os, time, traceback, shutil
import galaxy
from galaxy import util, model
from galaxy.model.orm import lazyload
from galaxy.datatypes.tabular import *
from galaxy.datatypes.interval import *
+# tabular/interval imports appear to be unused. Clean up?
from galaxy.datatypes import metadata
from galaxy.util.json import from_json_string
from galaxy.util.expressions import ExpressionContext
@@ -13,8 +14,6 @@ from galaxy.jobs.actions.post import Act
import pkg_resources
pkg_resources.require( "PasteDeploy" )
-from paste.deploy.converters import asbool
-
from Queue import Queue, Empty
log = logging.getLogger( __name__ )
@@ -83,11 +82,9 @@ class JobQueue( object ):
self.parent_pid = os.getpid()
# Contains new jobs. Note this is not used if track_jobs_in_database is True
self.queue = Queue()
-
# Contains jobs that are waiting (only use from monitor thread)
## This and jobs_to_check[] are closest to a "Job Queue"
self.waiting_jobs = []
-
# Helper for interruptable sleep
self.sleeper = Sleeper()
self.running = True
@@ -104,7 +101,7 @@ class JobQueue( object ):
the database and requeues or cleans up as necessary. Only run as the
job manager starts.
"""
- model = self.app.model
+ model = self.app.model # DBTODO Why?
for job in self.sa_session.query( model.Job ).filter( model.Job.state == model.Job.states.NEW ):
if job.tool_id not in self.app.toolbox.tools_by_id:
log.warning( "Tool '%s' removed from tool config, unable to recover job: %s" % ( job.tool_id, job.id ) )
@@ -294,7 +291,10 @@ class JobWrapper( object ):
self.tool_provided_job_metadata = None
# Wrapper holding the info required to restore and clean up from files used for setting metadata externally
self.external_output_metadata = metadata.JobExternalOutputMetadataWrapper( job )
-
+
+ def get_job( self ):
+ return self.sa_session.query( model.Job ).get( self.job_id )
+
def get_param_dict( self ):
"""
Restore the dictionary of parameters from the database.
@@ -479,9 +479,9 @@ class JobWrapper( object ):
self.fail( job.info )
return
if stderr:
- job.state = "error"
+ job.state = job.states.ERROR
else:
- job.state = 'ok'
+ job.state = job.states.OK
if self.app.config.outputs_to_working_directory:
for dataset_path in self.get_output_fnames():
try:
@@ -761,6 +761,215 @@ class JobWrapper( object ):
else:
return 'anonymous@unknown'
+class TaskWrapper(JobWrapper):
+ """
+ Extension of JobWrapper intended for running tasks.
+ Should be refactored into a generalized executable unit wrapper parent, then jobs and tasks.
+ """
+ # Abstract this to be more useful for running tasks that *don't* necessarily compose a job.
+
+ def __init__(self, task, queue):
+ super(TaskWrapper, self).__init__(task.job, queue)
+ self.task_id = task.id
+ self.parallelism = None
+ if task.part_file:
+ #do this better
+ self.working_directory = os.path.dirname(task.part_file)
+ else:
+ self.working_directory = None
+ self.status = task.states.NEW
+
+ def get_job( self ):
+ if self.job_id:
+ return self.sa_session.query( model.Job ).get( self.job_id )
+ else:
+ return None
+
+ def get_task( self ):
+ return self.sa_session.query(model.Task).get(self.task_id)
+
+ def get_param_dict( self ):
+ """
+ Restore the dictionary of parameters from the database.
+ """
+ job = self.sa_session.query( model.Job ).get( self.job_id )
+ param_dict = dict( [ ( p.name, p.value ) for p in job.parameters ] )
+ param_dict = self.tool.params_from_strings( param_dict, self.app )
+ return param_dict
+
+ def prepare( self ):
+ """
+ Prepare the job to run by creating the working directory and the
+ config files.
+ """
+ # Restore parameters from the database
+ job = self.get_job()
+ task = self.get_task()
+ if job.user is None and job.galaxy_session is None:
+ raise Exception( 'Job %s has no user and no session.' % job.id )
+ incoming = dict( [ ( p.name, p.value ) for p in job.parameters ] )
+ incoming = self.tool.params_from_strings( incoming, self.app )
+ # Do any validation that could not be done at job creation
+ self.tool.handle_unvalidated_param_values( incoming, self.app )
+ # Restore input / output data lists
+ inp_data = dict( [ ( da.name, da.dataset ) for da in job.input_datasets ] )
+ # DBTODO New method for generating command line for a task?
+ out_data = dict( [ ( da.name, da.dataset ) for da in job.output_datasets ] )
+ out_data.update( [ ( da.name, da.dataset ) for da in job.output_library_datasets ] )
+ # These can be passed on the command line if wanted as $userId $userEmail
+ if job.history and job.history.user: # check for anonymous user!
+ userId = '%d' % job.history.user.id
+ userEmail = str(job.history.user.email)
+ else:
+ userId = 'Anonymous'
+ userEmail = 'Anonymous'
+ incoming['userId'] = userId
+ incoming['userEmail'] = userEmail
+ # Build params, done before hook so hook can use
+ param_dict = self.tool.build_param_dict( incoming, inp_data, out_data, self.get_output_fnames(), self.working_directory )
+ fnames = {}
+ for v in self.get_input_fnames():
+ fnames[v] = os.path.join(self.working_directory, os.path.basename(v))
+ for dp in [x.real_path for x in self.get_output_fnames()]:
+ fnames[dp] = os.path.join(self.working_directory, os.path.basename(dp))
+ # Certain tools require tasks to be completed prior to job execution
+ # ( this used to be performed in the "exec_before_job" hook, but hooks are deprecated ).
+ self.tool.exec_before_job( self.queue.app, inp_data, out_data, param_dict )
+ # Run the before queue ("exec_before_job") hook
+ self.tool.call_hook( 'exec_before_job', self.queue.app, inp_data=inp_data,
+ out_data=out_data, tool=self.tool, param_dict=incoming)
+ self.sa_session.flush()
+ # Build any required config files
+ config_filenames = self.tool.build_config_files( param_dict, self.working_directory )
+ # FIXME: Build the param file (might return None, DEPRECATED)
+ param_filename = self.tool.build_param_file( param_dict, self.working_directory )
+ # Build the job's command line
+ self.command_line = self.tool.build_command_line( param_dict )
+ # HACK, Fix this when refactored.
+ for k, v in fnames.iteritems():
+ self.command_line = self.command_line.replace(k, v)
+ # FIXME: for now, tools get Galaxy's lib dir in their path
+ if self.command_line and self.command_line.startswith( 'python' ):
+ self.galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root
+ # Shell fragment to inject dependencies
+ if self.app.config.use_tool_dependencies:
+ self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
+ else:
+ self.dependency_shell_commands = None
+ # We need command_line persisted to the db in order for Galaxy to re-queue the job
+ # if the server was stopped and restarted before the job finished
+ task.command_line = self.command_line
+ self.sa_session.add( task )
+ self.sa_session.flush()
+ # # Return list of all extra files
+ extra_filenames = config_filenames
+ if param_filename is not None:
+ extra_filenames.append( param_filename )
+ self.param_dict = param_dict
+ self.extra_filenames = extra_filenames
+ self.status = 'prepared'
+ return extra_filenames
+
+ def fail( self, message, exception=False ):
+ log.error("TaskWrapper Failure %s" % message)
+ self.status = 'error'
+ # How do we want to handle task failure? Fail the job and let it clean up?
+
+ def change_state( self, state, info = False ):
+ task = self.get_task()
+ self.sa_session.refresh( task )
+ if info:
+ task.info = info
+ task.state = state
+ self.sa_session.add( task )
+ self.sa_session.flush()
+
+ def get_state( self ):
+ task = self.get_task()
+ self.sa_session.refresh( task )
+ return task.state
+
+ def set_runner( self, runner_url, external_id ):
+ task = self.get_task()
+ self.sa_session.refresh( task )
+ task.task_runner_name = runner_url
+ task.task_runner_external_id = external_id
+ # DBTODO Check task job_runner_stuff
+ self.sa_session.add( task )
+ self.sa_session.flush()
+
+ def finish( self, stdout, stderr ):
+ # DBTODO integrate previous finish logic.
+ # Simple finish for tasks. Just set the flag OK.
+ log.debug( 'task %s for job %d ended' % (self.task_id, self.job_id) )
+ """
+ Called to indicate that the associated command has been run. Updates
+ the output datasets based on stderr and stdout from the command, and
+ the contents of the output files.
+ """
+ # default post job setup_external_metadata
+ self.sa_session.expunge_all()
+ task = self.get_task()
+ # if the job was deleted, don't finish it
+ if task.state == task.states.DELETED:
+ self.cleanup()
+ return
+ elif task.state == task.states.ERROR:
+ # Job was deleted by an administrator
+ self.fail( task.info )
+ return
+ if stderr:
+ task.state = task.states.ERROR
+ else:
+ task.state = task.states.OK
+ # Save stdout and stderr
+ if len( stdout ) > 32768:
+ log.error( "stdout for task %d is greater than 32K, only first part will be logged to database" % task.id )
+ task.stdout = stdout[:32768]
+ if len( stderr ) > 32768:
+ log.error( "stderr for job %d is greater than 32K, only first part will be logged to database" % task.id )
+ task.stderr = stderr[:32768]
+ task.command_line = self.command_line
+ self.sa_session.flush()
+ log.debug( 'task %d ended' % self.task_id )
+
+ def cleanup( self ):
+ # There is no task cleanup. The job cleans up for all tasks.
+ pass
+
+ def get_command_line( self ):
+ return self.command_line
+
+ def get_session_id( self ):
+ return self.session_id
+
+ def get_output_file_id( self, file ):
+ # There is no permanent output file for tasks.
+ return None
+
+ def get_tool_provided_job_metadata( self ):
+ # DBTODO Handle this as applicable for tasks.
+ return None
+
+ def get_dataset_finish_context( self, job_context, dataset ):
+ # Handled at the parent job level. Do nothing here.
+ pass
+
+ def check_output_sizes( self ):
+ sizes = []
+ output_paths = self.get_output_fnames()
+ for outfile in [ str( o ) for o in output_paths ]:
+ sizes.append( ( outfile, os.stat( outfile ).st_size ) )
+ return sizes
+
+ def setup_external_metadata( self, exec_dir = None, tmp_dir = None, dataset_files_path = None, config_root = None, datatypes_config = None, set_extension = True, **kwds ):
+ # There is no metadata setting for tasks. This is handled after the merge, at the job level.
+ pass
+
+ @property
+ def user( self ):
+ pass
+
class DefaultJobDispatcher( object ):
def __init__( self, app ):
self.app = app
@@ -768,10 +977,15 @@ class DefaultJobDispatcher( object ):
start_job_runners = ["local"]
if app.config.start_job_runners is not None:
start_job_runners.extend( app.config.start_job_runners.split(",") )
+ if app.config.use_tasked_jobs:
+ start_job_runners.append("tasks")
for runner_name in start_job_runners:
if runner_name == "local":
import runners.local
self.job_runners[runner_name] = runners.local.LocalJobRunner( app )
+ elif runner_name == "tasks":
+ import runners.tasks
+ self.job_runners[runner_name] = runners.tasks.TaskedJobRunner( app )
elif runner_name == "pbs":
import runners.pbs
self.job_runners[runner_name] = runners.pbs.PBSJobRunner( app )
@@ -782,12 +996,17 @@ class DefaultJobDispatcher( object ):
import runners.drmaa
self.job_runners[runner_name] = runners.drmaa.DRMAAJobRunner( app )
else:
- log.error( "Unable to start unknown job runner: %s" %runner_name )
+ log.error( "Unable to start unknown job runner: '%s'" %runner_name )
def put( self, job_wrapper ):
- runner_name = ( job_wrapper.tool.job_runner.split(":", 1) )[0]
- log.debug( "dispatching job %d to %s runner" %( job_wrapper.job_id, runner_name ) )
- self.job_runners[runner_name].put( job_wrapper )
+ if self.app.config.use_tasked_jobs and job_wrapper.tool.parallelism is not None and not isinstance(job_wrapper, TaskWrapper):
+ runner_name = "tasks"
+ log.debug( "dispatching job %d to %s runner" %( job_wrapper.job_id, runner_name ) )
+ self.job_runners[runner_name].put( job_wrapper )
+ else:
+ runner_name = ( job_wrapper.tool.job_runner.split(":", 1) )[0]
+ log.debug( "dispatching job %d to %s runner" %( job_wrapper.job_id, runner_name ) )
+ self.job_runners[runner_name].put( job_wrapper )
def stop( self, job ):
runner_name = ( job.job_runner_name.split(":", 1) )[0]
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -176,6 +176,44 @@ class Job( object ):
dataset.peek = 'Job deleted'
dataset.info = 'Job output deleted by user before job completed'
+class Task( object ):
+ """
+ A task represents a single component of a job.
+ """
+ states = Bunch( NEW = 'new',
+ WAITING = 'waiting',
+ QUEUED = 'queued',
+ RUNNING = 'running',
+ OK = 'ok',
+ ERROR = 'error',
+ DELETED = 'deleted' )
+
+ def __init__( self, job, part_file = None ):
+ self.command_line = None
+ self.parameters = []
+ self.state = Task.states.NEW
+ self.info = None
+ self.part_file = part_file
+ self.task_runner_name = None
+ self.task_runner_external_id = None
+ self.job = job
+ self.stdout = None
+ self.stderr = None
+
+ def set_state( self, state ):
+ self.state = state
+
+ def get_param_values( self, app ):
+ """
+ Read encoded parameter values from the database and turn back into a
+ dict of tool parameter values.
+ """
+ param_dict = dict( [ ( p.name, p.value ) for p in self.parent_job.parameters ] )
+ tool = app.toolbox.tools_by_id[self.tool_id]
+ param_dict = tool.params_from_strings( param_dict, app )
+ return param_dict
+
+
class JobParameter( object ):
def __init__( self, name, value ):
self.name = name
--- a/tools/samtools/sam_bitwise_flag_filter.xml
+++ b/tools/samtools/sam_bitwise_flag_filter.xml
@@ -1,5 +1,6 @@
<tool id="sam_bw_filter" name="Filter SAM" version="1.0.0"><description>on bitwise flag values</description>
+ <parallelism method="basic"></parallelism><command interpreter="python">
sam_bitwise_flag_filter.py
--input_sam_file=$input1
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -367,7 +367,12 @@ class Tool:
self.redirect_url_params = ''
# Short description of the tool
self.description = util.xml_text(root, "description")
- # Job runner
+ # Parallelism for tasks, read from tool config.
+ parallelism = root.find("parallelism")
+ if parallelism is not None and parallelism.get("method"):
+ self.parallelism = parallelism.get("method")
+ else:
+ self.parallelism = None
if self.app.config.start_job_runners is None:
# Jobs are always local regardless of tool config if no additional
# runners are started
--- a/tools/sr_mapping/bwa_wrapper.xml
+++ b/tools/sr_mapping/bwa_wrapper.xml
@@ -1,5 +1,6 @@
<tool id="bwa_wrapper" name="Map with BWA" version="1.1.0"><description></description>
+ <parallelism method="basic"></parallelism><command interpreter="python">
bwa_wrapper.py
--threads="4"
--- /dev/null
+++ b/lib/galaxy/jobs/runners/tasks.py
@@ -0,0 +1,206 @@
+import logging
+import subprocess
+from Queue import Queue
+import threading
+
+from galaxy import model
+
+import os, errno
+from time import sleep
+
+from galaxy.jobs import TaskWrapper
+
+log = logging.getLogger( __name__ )
+
+class TaskedJobRunner( object ):
+ """
+ Job runner backed by a finite pool of worker threads. FIFO scheduling
+ """
+ STOP_SIGNAL = object()
+ def __init__( self, app ):
+ """Start the job runner with 'nworkers' worker threads"""
+ self.app = app
+ self.sa_session = app.model.context
+ # start workers
+ self.queue = Queue()
+ self.threads = []
+ nworkers = app.config.local_task_queue_workers
+ log.info( "Starting tasked-job runners" )
+ for i in range( nworkers ):
+ worker = threading.Thread( target=self.run_next )
+ worker.start()
+ self.threads.append( worker )
+ log.debug( "%d workers ready", nworkers )
+
+ def run_next( self ):
+ """Run the next job, waiting until one is available if neccesary"""
+ while 1:
+ job_wrapper = self.queue.get()
+ if job_wrapper is self.STOP_SIGNAL:
+ return
+ try:
+ self.run_job( job_wrapper )
+ except:
+ log.exception( "Uncaught exception running tasked job" )
+
+ def run_job( self, job_wrapper ):
+ job_wrapper.set_runner( 'tasks:///', None )
+ stderr = stdout = command_line = ''
+ # Prepare the job to run
+ 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
+ # If we were able to get a command line, run the job. ( must be passed to tasks )
+ if command_line:
+ try:
+ # DBTODO read tool info and use the right kind of parallelism.
+ # For now, the only splitter is the 'basic' one, n-ways split on one input, one output.
+ # This is incredibly simplified. Parallelism ultimately needs to describe which inputs, how, etc.
+ job_wrapper.change_state( model.Job.states.RUNNING )
+ self.sa_session.flush()
+ parent_job = job_wrapper.get_job()
+ # Split with the tool-defined method.
+ if job_wrapper.tool.parallelism == "basic":
+ from galaxy.jobs.splitters import basic
+ if len(job_wrapper.get_input_fnames()) > 1 or len(job_wrapper.get_output_fnames()) > 1:
+ log.error("The basic splitter is not capable of handling jobs with multiple inputs or outputs.")
+ job_wrapper.change_state( model.Job.states.ERROR )
+ job_wrapper.fail("Job Splitting Failed, the basic splitter only handles tools with one input and one output")
+ # Requeue as a standard job?
+ return
+ input_file = job_wrapper.get_input_fnames()[0]
+ working_directory = job_wrapper.working_directory
+ # DBTODO execute an external task to do the splitting, this should happen at refactor.
+ # Regarding number of ways split, use "hints" in tool config?
+ # If the number of tasks is sufficiently high, we can use it to calculate job completion % and give a running status.
+ basic.split(input_file, working_directory,
+ 20, #Needs serious experimentation to find out what makes the most sense.
+ parent_job.input_datasets[0].dataset.ext)
+ # Tasks in this parts list are in alphabetical listdir order (15 before 5), but that should not matter.
+ parts = [os.path.join(os.path.abspath(job_wrapper.working_directory), p, os.path.basename(input_file))
+ for p in os.listdir(job_wrapper.working_directory)
+ if p.startswith('task_')]
+ else:
+ job_wrapper.change_state( model.Job.states.ERROR )
+ job_wrapper.fail("Job Splitting Failed, no match for '%s'" % job_wrapper.tool.parallelism)
+ # Assemble parts into task_wrappers
+
+ # Not an option for now. Task objects don't *do* anything useful yet, but we'll want them tracked outside this thread to do anything.
+ # if track_tasks_in_database:
+ tasks = []
+ task_wrappers = []
+ for part in parts:
+ task = model.Task(parent_job, part)
+ self.sa_session.add(task)
+ tasks.append(task)
+ self.sa_session.flush()
+ # Must flush prior to the creation and queueing of task wrappers.
+ for task in tasks:
+ tw = TaskWrapper(task, job_wrapper.queue)
+ task_wrappers.append(tw)
+ self.app.job_manager.dispatcher.put(tw)
+ tasks_incomplete = False
+ sleep_time = 1
+ while tasks_incomplete is False:
+ tasks_incomplete = True
+ for tw in task_wrappers:
+ if not tw.get_state() == model.Task.states.OK:
+ tasks_incomplete = False
+ sleep( sleep_time )
+ if sleep_time < 8:
+ sleep_time *= 2
+ output_filename = job_wrapper.get_output_fnames()[0].real_path
+ basic.merge(working_directory, output_filename)
+ log.debug('execution finished: %s' % command_line)
+ for tw in task_wrappers:
+ stdout += tw.get_task().stdout
+ stderr += tw.get_task().stderr
+ except Exception:
+ job_wrapper.fail( "failure running job", exception=True )
+ log.exception("failure running job %d" % job_wrapper.job_id)
+ return
+
+ #run the metadata setting script here
+ #this is terminate-able when output dataset/job is deleted
+ #so that long running set_meta()s can be canceled without having to reboot the server
+ if job_wrapper.get_state() not in [ model.Job.states.ERROR, model.Job.states.DELETED ] and self.app.config.set_metadata_externally and job_wrapper.output_paths:
+ external_metadata_script = job_wrapper.setup_external_metadata( output_fnames = job_wrapper.get_output_fnames(),
+ set_extension = True,
+ kwds = { 'overwrite' : False } ) #we don't want to overwrite metadata that was copied over in init_meta(), as per established behavior
+ log.debug( 'executing external set_meta script for job %d: %s' % ( job_wrapper.job_id, external_metadata_script ) )
+ external_metadata_proc = subprocess.Popen( args = external_metadata_script,
+ shell = True,
+ env = os.environ,
+ preexec_fn = os.setpgrp )
+ job_wrapper.external_output_metadata.set_job_runner_external_pid( external_metadata_proc.pid, self.sa_session )
+ external_metadata_proc.wait()
+ log.debug( 'execution of external set_meta finished for job %d' % job_wrapper.job_id )
+
+ # Finish the job
+ try:
+ job_wrapper.finish( stdout, stderr )
+ except:
+ log.exception("Job wrapper finish method failed")
+ job_wrapper.fail("Unable to finish job", exception=True)
+
+ 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.queue.put( job_wrapper )
+
+ def shutdown( self ):
+ """Attempts to gracefully shut down the worker threads"""
+ log.info( "sending stop signal to worker threads" )
+ for i in range( len( self.threads ) ):
+ self.queue.put( self.STOP_SIGNAL )
+ log.info( "local job runner stopped" )
+
+ def check_pid( self, pid ):
+ # DBTODO Need to check all subtask pids and return some sort of cumulative result.
+ return True
+ try:
+ os.kill( pid, 0 )
+ return True
+ except OSError, e:
+ if e.errno == errno.ESRCH:
+ log.debug( "check_pid(): PID %d is dead" % pid )
+ else:
+ log.warning( "check_pid(): Got errno %s when attempting to check PID %d: %s" %( errno.errorcode[e.errno], pid, e.strerror ) )
+ return False
+
+ def stop_job( self, job ):
+ # DBTODO Call stop on all of the tasks.
+ #if our local job has JobExternalOutputMetadata associated, then our primary job has to have already finished
+ if job.external_output_metadata:
+ pid = job.external_output_metadata[0].job_runner_external_pid #every JobExternalOutputMetadata has a pid set, we just need to take from one of them
+ else:
+ pid = job.job_runner_external_id
+ if pid in [ None, '' ]:
+ log.warning( "stop_job(): %s: no PID in database for job, unable to stop" % job.id )
+ return
+ pid = int( pid )
+ if not self.check_pid( pid ):
+ log.warning( "stop_job(): %s: PID %d was already dead or can't be signaled" % ( job.id, pid ) )
+ return
+ for sig in [ 15, 9 ]:
+ try:
+ os.killpg( pid, sig )
+ except OSError, e:
+ log.warning( "stop_job(): %s: Got errno %s when attempting to signal %d to PID %d: %s" % ( job.id, errno.errorcode[e.errno], sig, pid, e.strerror ) )
+ return # give up
+ sleep( 2 )
+ if not self.check_pid( pid ):
+ log.debug( "stop_job(): %s: PID %d successfully killed with signal %d" %( job.id, pid, sig ) )
+ return
+ else:
+ log.warning( "stop_job(): %s: PID %d refuses to die after signaling TERM/KILL" %( job.id, pid ) )
+
+ def recover( self, job, job_wrapper ):
+ # DBTODO Task Recovery, this should be possible.
+ job_wrapper.change_state( model.Job.states.ERROR, info = "This job was killed when Galaxy was restarted. Please retry the job." )
+
--- a/universe_wsgi.ini.sample
+++ b/universe_wsgi.ini.sample
@@ -347,6 +347,11 @@ use_interactive = True
# Necessary if you're running the load balanced setup.
#track_jobs_in_database = False
+# This enables splitting of jobs into tasks, if specified by the particular tool config.
+# This is a new feature and not recommended for production servers yet.
+#use_tasked_jobs = True
+#local_task_queue_workers = 2
+
# Enable job recovery (if Galaxy is restarted while cluster jobs are running,
# it can "recover" them when it starts). This is not safe to use if you are
# running more than one Galaxy server using the same database.
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -391,7 +391,24 @@ JobImportHistoryArchive.table = Table( "
Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
Column( "archive_dir", TEXT )
)
-
+
+Task.table = Table( "task", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "execution_time", DateTime ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "state", String( 64 ), index=True ),
+ Column( "command_line", TEXT ),
+ Column( "param_filename", String( 1024 ) ),
+ Column( "runner_name", String( 255 ) ),
+ Column( "stdout", TEXT ),
+ Column( "stderr", TEXT ),
+ Column( "traceback", TEXT ),
+ Column( "job_id", Integer, ForeignKey( "job.id" ), index=True, nullable=False ),
+ Column( "part_file", String(1024)),
+ Column( "task_runner_name", String( 255 ) ),
+ Column( "task_runner_external_id", String( 255 ) ) )
+
PostJobAction.table = Table("post_job_action", metadata,
Column("id", Integer, primary_key=True),
Column("workflow_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True, nullable=False),
@@ -1251,6 +1268,10 @@ assign_mapper( context, Job, Job.table,
output_library_datasets=relation( JobToOutputLibraryDatasetAssociation, lazy=False ),
external_output_metadata = relation( JobExternalOutputMetadata, lazy = False ) ) )
+assign_mapper( context, Task, Task.table,
+ properties=dict( job = relation( Job )))
+
+
assign_mapper( context, Event, Event.table,
properties=dict( history=relation( History ),
galaxy_session=relation( GalaxySession ),
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -4,7 +4,6 @@ Universe configuration builder.
import sys, os
import logging, logging.config
-from optparse import OptionParser
import ConfigParser
from galaxy.util import string_as_bool
@@ -76,6 +75,9 @@ class Configuration( object ):
self.error_email_to = kwargs.get( 'error_email_to', None )
self.smtp_server = kwargs.get( 'smtp_server', None )
self.start_job_runners = kwargs.get( 'start_job_runners', None )
+ # Tasked job runner.
+ self.use_tasked_jobs = string_as_bool( kwargs.get( 'use_tasked_jobs', False ) )
+ self.local_task_queue_workers = int(kwargs.get("local_task_queue_workers", 2))
self.default_cluster_job_runner = kwargs.get( 'default_cluster_job_runner', 'local:///' )
self.pbs_application_server = kwargs.get('pbs_application_server', "" )
self.pbs_dataset_server = kwargs.get('pbs_dataset_server', "" )
--- /dev/null
+++ b/lib/galaxy/model/migrate/versions/0061_tasks.py
@@ -0,0 +1,49 @@
+"""
+Migration script to create tables task management.
+"""
+from sqlalchemy import *
+from sqlalchemy.orm import *
+from migrate import *
+from migrate.changeset import *
+
+import datetime
+
+import logging
+log = logging.getLogger( __name__ )
+
+metadata = MetaData( migrate_engine )
+db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) )
+now = datetime.datetime.utcnow
+
+Task_table = Table( "task", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "execution_time", DateTime ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "state", String( 64 ), index=True ),
+ Column( "command_line", TEXT ),
+ Column( "param_filename", String( 1024 ) ),
+ Column( "runner_name", String( 255 ) ),
+ Column( "stdout", TEXT ),
+ Column( "stderr", TEXT ),
+ Column( "traceback", TEXT ),
+ Column( "job_id", Integer, ForeignKey( "job.id" ), index=True, nullable=False ),
+ Column( "part_file", String(1024)),
+ Column( "task_runner_name", String( 255 ) ),
+ Column( "task_runner_external_id", String( 255 ) ) )
+
+tables = [Task_table]
+
+def upgrade():
+ print __doc__
+ metadata.reflect()
+ for table in tables:
+ try:
+ table.create()
+ except:
+ log.warn( "Failed to create table '%s', ignoring (might result in wrong schema)" % table.name )
+
+def downgrade():
+ metadata.reflect()
+ for table in tables:
+ table.drop()
--- /dev/null
+++ b/lib/galaxy/jobs/splitters/basic.py
@@ -0,0 +1,85 @@
+import os, logging
+log = logging.getLogger( __name__ )
+
+def _file_len(fname):
+ i = 0
+ with open(fname) as f:
+ for i, l in enumerate(f):
+ pass
+ return i + 1
+
+def _fq_seq_count(fname):
+ count = 0
+ with open(fname) as f:
+ for i, l in enumerate(f):
+ if l.startswith('@'):
+ count += 1
+ return count
+
+def split_fq(input_file, working_directory, parts):
+ # Temporary, switch this to use the fq reader in lib/galaxy_utils/sequence.
+ outputs = []
+ length = _fq_seq_count(input_file)
+ if length < 1:
+ return outputs
+ if length < parts:
+ parts = length
+ len_each, remainder = divmod(length, parts)
+ with open(input_file, 'rt') as f:
+ for p in range(0, parts):
+ part_dir = os.path.join( os.path.abspath(working_directory), 'task_%s' % p)
+ if not os.path.exists( part_dir ):
+ os.mkdir( part_dir )
+ part_path = os.path.join(part_dir, os.path.basename(input_file))
+ with open(part_path, 'w') as part_file:
+ for l in range(0, len_each):
+ part_file.write(f.readline())
+ part_file.write(f.readline())
+ part_file.write(f.readline())
+ part_file.write(f.readline())
+ if remainder > 0:
+ part_file.write(f.readline())
+ part_file.write(f.readline())
+ part_file.write(f.readline())
+ part_file.write(f.readline())
+ remainder -= 1
+ outputs.append(part_path)
+ return outputs
+
+def split_txt(input_file, working_directory, parts):
+ outputs = []
+ length = _file_len(input_file)
+ if length < parts:
+ parts = length
+ len_each, remainder = divmod(length, parts)
+ with open(input_file, 'rt') as f:
+ for p in range(0, parts):
+ part_dir = os.path.join( os.path.abspath(working_directory), 'task_%s' % p)
+ if not os.path.exists( part_dir ):
+ os.mkdir( part_dir )
+ part_path = os.path.join(part_dir, os.path.basename(input_file))
+ with open(part_path, 'w') as part_file:
+ for l in range(0, len_each):
+ part_file.write(f.readline())
+ if remainder > 0:
+ part_file.write(f.readline())
+ remainder -= 1
+ outputs.append(part_path)
+ return outputs
+
+def split( input_file, working_directory, parts, file_type = None):
+ #Implement a better method for determining how to split.
+ if file_type.startswith('fastq'):
+ return split_fq(input_file, working_directory, parts)
+ else:
+ return split_txt(input_file, working_directory, parts)
+
+def merge( working_directory, output_file ):
+ output_file_name = os.path.basename(output_file)
+ task_dirs = [os.path.join(working_directory, x) for x in os.listdir(working_directory) if x.startswith('task_')]
+ task_dirs.sort(key = lambda x: int(x.split('task_')[-1]))
+ for task_dir in task_dirs:
+ try:
+ os.system( 'cat %s >> %s' % ( os.path.join(task_dir, output_file_name), output_file ) )
+ except Exception, e:
+ log.error(str(e))
1
0
galaxy-dist commit bbc0f56e3e16: A few more bug fixes in the new sample tracking code.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '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 1289427445 18000
# Node ID bbc0f56e3e16f93bf3c81d09b0fa12876a3f2579
# Parent b1813ff5bb4ef20d0b5d5a1fdcfc16cf6ec3ba53
A few more bug fixes in the new sample tracking code.
--- a/templates/admin/requests/select_datasets_to_transfer.mako
+++ b/templates/admin/requests/select_datasets_to_transfer.mako
@@ -127,7 +127,7 @@
</div>
%if sample and sample.datasets:
- <% title = 'Datasets currently selected for "%s"' % sample.name %>
+ <% title = 'All selected datasets for "%s"' % sample.name %><p/>
${render_sample_datasets( 'requests_admin', sample, sample.datasets, title )}
%endif
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -952,9 +952,10 @@ class RequestsCommon( BaseController, Us
sample_datasets = sample.transfer_error_dataset_files
return trans.fill_template( '/requests/common/view_sample_datasets.mako',
cntrller=cntrller,
+ title=title,
sample=sample,
sample_datasets=sample_datasets,
- transfer_status=transferr_status,
+ transfer_status=transfer_status,
message=message,
status=status,
files=[],
--- a/templates/requests/common/common.mako
+++ b/templates/requests/common/common.mako
@@ -329,10 +329,26 @@
%endif
</td><td>
- %if is_admin and sample.untransferred_dataset_files:
- <a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_admin', action='manage_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.transferred_dataset_files )}</a>
+ %if is_admin:
+ %if sample.untransferred_dataset_files:
+ ## At least 1 selected dataset is not yet transferred, so this link
+ ## will direct the admin to a page allowing them to transfer datasets.
+ <a href="${h.url_for( controller='requests_common', action='manage_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.transferred_dataset_files )}</a>
+ %else:
+ ## All selected datasets have successfully transferred, so this link
+ ## will direct the admin to a page displaying all transferred datasets.
+ <a href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ), transfer_status=trans.model.SampleDataset.transfer_status.COMPLETE )}">${len( sample.transferred_dataset_files )}</a>
+ %endif
%else:
- ${len( sample.transferred_dataset_files )}
+ %if sample.transferred_dataset_files:
+ ## The cuurent user is not an admin, so this link will direct the
+ ## user to the target data library containing those datasets that
+ ## were successfully transferred.
+ <a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library', id=trans.security.encode_id( sample.library.id ) )}">${len( sample.transferred_dataset_files )}</a>
+ %else:
+ ## Display a 0 with no link.
+ ${len( sample.transferred_dataset_files )}
+ %endif
%endif
</td>
%endif
--- a/templates/requests/common/view_sample_datasets.mako
+++ b/templates/requests/common/view_sample_datasets.mako
@@ -40,4 +40,5 @@
No datasets with status ${transfer_status}" belong to this sample
%else:
No datasets have been selected for this sample.
+ %endif
%endif
1
0
galaxy-dist commit 865d7175847a: Corrected bug in reference to genome file in NGS simulation tool
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Kelly Vincent <kpvincent(a)bx.psu.edu>
# Date 1289492971 18000
# Node ID 865d7175847ad322959f6b85532357d0f69afa46
# Parent 591c53b44ae4a6237a841adaa3abfe59829beebe
Corrected bug in reference to genome file in NGS simulation tool
--- a/tools/ngs_simulation/ngs_simulation.xml
+++ b/tools/ngs_simulation/ngs_simulation.xml
@@ -5,7 +5,7 @@
ngs_simulation.py
#if $in_type.input_type == "built-in"
--input="${ filter( lambda x: str( x[0] ) == str( $in_type.genome ), $__app__.tool_data_tables[ 'ngs_sim_fasta' ].get_fields() )[0][-1] }"
- --genome=$genome
+ --genome=$in_type.genome
#else
--input=$in_type.input1
#end if
1
0
galaxy-dist commit ca23ea683d26: Fix for workflow inputs that aren't a subtype of text, added 'data' to registry, which is the appropriate supertype.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1289497176 18000
# Node ID ca23ea683d26df004e666ede82950c712e3ac637
# Parent ef3e52251722c1086160643a5f700c5db75a71d4
Fix for workflow inputs that aren't a subtype of text, added 'data' to registry, which is the appropriate supertype.
Note that this *will* allow someone to change the datatype of something *to* data.
--- a/lib/galaxy/datatypes/registry.py
+++ b/lib/galaxy/datatypes/registry.py
@@ -208,6 +208,10 @@ class Registry( object ):
'txt' : 'text/plain',
'wig' : 'text/plain'
}
+ # super supertype fix for input steps in workflows.
+ if 'data' not in self.datatypes_by_extension:
+ self.datatypes_by_extension['data'] = data.Data()
+ self.mimetypes_by_extension['data'] = 'application/octet-stream'
# Default values - the order in which we attempt to determine data types is critical
# because some formats are much more flexibly defined than others.
if len(self.sniff_order) < 1:
1
0