galaxy-commits
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
November 2010
- 1 participants
- 286 discussions
# 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 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
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
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 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 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 479b377ef455: fix the add sample bug in request page
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 rc
# Date 1289333440 18000
# Node ID 479b377ef455f2d676fb84363b0b6d437d6af5f6
# Parent 7c60d74ba2df4488df90d08ed0ae9c7cc3871222
fix the add sample bug in request page
--- a/templates/requests/common/common.mako
+++ b/templates/requests/common/common.mako
@@ -131,6 +131,8 @@
is_submitted = False
is_unsubmitted = False
display_checkboxes = False
+ display_bar_code = False
+ display_datasets = False
%><%
if display_checkboxes and trans.security.encode_id( sample.id ) in encoded_selected_sample_ids:
1
0
galaxy-dist commit 7c60d74ba2df: Aggressively sanitize metadata parameter values when provided on the upload form and which are subsequently used for filename substitution.
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 Daniel Blankenberg <dan(a)bx.psu.edu>
# Date 1289329026 18000
# Node ID 7c60d74ba2df4488df90d08ed0ae9c7cc3871222
# Parent 9ee40043b826b3bc1e29f84996f9fe14dd119a45
Aggressively sanitize metadata parameter values when provided on the upload form and which are subsequently used for filename substitution.
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -155,6 +155,26 @@ def sanitize_param(value):
print value
raise Exception, 'Unknown parameter type (%s)' % ( type( value ) )
+valid_filename_chars = set( string.ascii_letters + string.digits + '_.' )
+invalid_filenames = [ '', '.', '..' ]
+def sanitize_for_filename( text, default=None ):
+ """
+ Restricts the characters that are allowed in a filename portion; Returns default value or a unique id string if result is not a valid name.
+ Method is overly aggressive to minimize possible complications, but a maximum length is not considered.
+ """
+ out = []
+ for c in text:
+ if c in valid_filename_chars:
+ out.append( c )
+ else:
+ out.append( '_' )
+ out = ''.join( out )
+ if out in invalid_filenames:
+ if default is None:
+ return sanitize_for_filename( str( unique_id() ) )
+ return default
+ return out
+
class Params:
"""
Stores and 'sanitizes' parameters. Alphanumeric characters and the
--- a/lib/galaxy/tools/parameters/grouping.py
+++ b/lib/galaxy/tools/parameters/grouping.py
@@ -12,7 +12,7 @@ import StringIO, os, urllib
from galaxy.datatypes import sniff
from galaxy.util.bunch import Bunch
from galaxy.util.odict import odict
-from galaxy.util import json, relpath
+from galaxy.util import json, relpath, sanitize_for_filename
class Group( object ):
def __init__( self ):
@@ -335,10 +335,14 @@ class UploadDataset( Group ):
dataset.composite_files = {}
#load metadata
files_metadata = context.get( self.metadata_ref, {} )
+ metadata_name_substition_default_dict = dict( [ ( composite_file.substitute_name_with_metadata, d_type.metadata_spec[ composite_file.substitute_name_with_metadata ].default ) for composite_file in d_type.composite_files.values() if composite_file.substitute_name_with_metadata ] )
for meta_name, meta_spec in d_type.metadata_spec.iteritems():
if meta_spec.set_in_upload:
if meta_name in files_metadata:
- dataset.metadata[ meta_name ] = files_metadata[ meta_name ]
+ meta_value = files_metadata[ meta_name ]
+ if meta_name in metadata_name_substition_default_dict:
+ meta_value = sanitize_for_filename( meta_value, default = metadata_name_substition_default_dict[ meta_name ] )
+ dataset.metadata[ meta_name ] = meta_value
dataset.precreated_name = dataset.name = self.get_composite_dataset_name( context )
if dataset.datatype.composite_type == 'auto_primary_file':
#replace sniff here with just creating an empty file
1
0
galaxy-dist commit 2c2a22396a26: Backed out changeset 081ce300b688 (some files accidentally committed).
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 Nate Coraor <nate(a)bx.psu.edu>
# Date 1289402767 18000
# Node ID 2c2a22396a2641a52dc5f200d26a3d947cf11cbd
# Parent 081ce300b688968521edd30f25a020d96999cd64
Backed out changeset 081ce300b688 (some files accidentally committed).
--- a/test/functional/test_sample_tracking.py
+++ b/test/functional/test_sample_tracking.py
@@ -388,7 +388,6 @@ class TestFormsAndRequests( TwillTestCas
% ( request_two.name, request_two.states.REJECTED )
def test_055_reset_data_for_later_test_runs( self ):
"""Reseting data to enable later test runs to pass"""
- '''
# Logged in as admin_user
##################
# Delete request_type permissions
@@ -433,4 +432,3 @@ class TestFormsAndRequests( TwillTestCas
# Manually delete the group from the database
refresh( group )
delete( group )
- '''
--- a/static/welcome.html
+++ b/static/welcome.html
@@ -2,211 +2,20 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<meta name="generator" content="Docutils 0.3.9: http://docutils.sourceforge.net/" />
-<link rel="stylesheet" href="style/base.css" type="text/css" />
-<style type="text/css">
-
- .quickie {
- text-align: center;
- background: black;
- margin: 10px;
- }
-
- .current-quickie {
- width: 300px;
- background: white;
- margin: auto;
- }
-
- .current-quickie img {
- padding: 15px;
- border: 1px solid #ccc;
- margin: auto;
- background-color: white;
- -moz-border-radius:4px;
- -webkit-border-radius:4px;
-
- }
-
- .previous {
- width: 100%;
- overflow: auto;
- border: solid #ccc 1px;
- -moz-border-radius:4px;
- -webkit-border-radius:4px;
-
- }
- .previous .quickie {
- padding-top: 10px;
- min-height: 90px;
- min-width: 150px;
- }
- #screencasts {
- max-width: 50em;
- margin-left: auto;
- margin-right: auto;
- }
-
-</style>
-<script type="text/javascript" src="http://galaxy.psu.edu/welcome_img/jquery.min.js"></script>
-<script type="text/javascript" src="http://galaxy.psu.edu/welcome_img/jquery.cycle.all.2.72.js"></script>
-<script type="text/javascript">
-
-$(document).ready(function() {
- $('.current-quickie').cycle({
- fx: 'fade',
- pause: 1,
- timeout: 1000,
- speed: 1000
- });
-});
-</script>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" href="style/base.css" type="text/css" /></head><body>
-<div class="document">
-<h3 align="center">Here is what's happening...</h3>
-<div align="center" class="current-quickie">
- <a target="_blank" href="http://usegalaxy.org/cloud"><img src="http://galaxy.psu.edu/welcome_img/welcome_images.001.png" width="300" height="200"></a>
- <a target="_blank" href="http://bitbucket.org/galaxy/galaxy-central/wiki/ISMB2010_GalaxyTutorial_3_R…"><img src="http://galaxy.psu.edu/welcome_img/welcome_images.002.png" width="300" height="200"></a>
- <a target="_blank" href="http://main.g2.bx.psu.edu/u/aun1/p/ismb2010-demo"><img src="http://galaxy.psu.edu/welcome_img/welcome_images.003.png" width="300" height="200" /></a>
- <a target="_blank" href="http://bitbucket.org/galaxy/galaxy-central/wiki/DataLibraries/Tutorial/Data…"><img src="http://galaxy.psu.edu/welcome_img/welcome_images.004.png" width="300" height="200" /></a>
- <a target="_blank" href="http://main.g2.bx.psu.edu/u/aun1/p/windshield-splatter"><img src="http://galaxy.psu.edu/welcome_img/welcome_images.005.png" width="300" height="200" /></a>
-</div>
-
-<h3 align="center">Live Quickies</h3>
-
-<div class="previous" id="previous">
- <table border="0" cellpadding="0" cellspacing="0" width="100%">
- <tr>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie1_TabSeq/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie1_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie2_Grouping/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie2_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie3_Intervals/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie3_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie4_whatsNew/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie4_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie5_join/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie5_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie6_share/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie6_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie7_sr_beta/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie7_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie8_solid_single_end/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie8_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie9_solid_mate_pair/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie9_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie10_custom_genome/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie10_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie_11_illumina_se/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie11_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie12_illumina_pe/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie12_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie_13_fastq_basic/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie13_small.png" border="0">
- </div>
- </a>
- </td>
-
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie_14_fastq_adv/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie14_small.png" border="0">
- </div>
- </a>
- </td>
- <td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie_15_lastz_frag/flow.html',width:640,height:480,scroll:'no'})">
- <div class="quickie">
- <img src="images/qk/quickie15_small.png" border="0">
- </div>
- </a>
- </td>
-
-
-
- </tr>
- </table>
-</div>
-
-<script type="text/javascript">
- // Scroll to last quickie in box
- document.getElementById("previous").scrollLeft = 100000000;
-</script>
-
-<br/>
-<br/>
-<br/>
-<hr/>
-
-<p><a target="_blank" class="reference" href="http://g2.trac.bx.psu.edu/wiki/GalaxyTeam">The Galaxy team</a> is a part of <a target="_blank" class="reference" href="http://www.bx.psu.edu">BX</a> at <a target="_blank" class="reference" href="http://www.psu.edu">Penn State</a>.</p>
-
-This project is supported in part by <a target="_blank" class="reference" href="http://www.nsf.gov">NSF</a>, <a target="_blank" class="reference" href="http://www.genome.gov">NHGRI</a>, <a target="_blank" class="reference" href="http://www.huck.psu.edu">The Huck Institutes of the Life Sciences</a>, and <a target="_blank" class="reference" href="http://www.ics.psu.edu">The Institute for CyberScience at Penn State</a>.
-<p><small>Galaxy build: <b>$Rev 3885:1ab9d6b0ddfc$</b></small></p>
-</div>
-</div>
+ <div class="document">
+ <div class="donemessagelarge">
+ <strong>Hello world! It's running...</strong>
+ <hr>
+ To customize this page edit <code>static/welcome.html</code>
+ </div>
+ <br/>
+ <img src="images/noodles.png" alt="WWFSMD?" style="display: block; margin-left: auto; margin-right: auto;" />
+ <hr/>
+ This project is supported in part by <a target="_blank" class="reference" href="http://www.nsf.gov">NSF</a>, <a target="_blank" class="reference" href="http://www.genome.gov">NHGRI</a>, and <a target="_blank" class="reference" href="http://www.huck.psu.edu">the Huck Institutes of the Life Sciences</a>.
+ </div></body></html>
--- a/scripts/taxonomy/processTaxonomy.sh
+++ b/scripts/taxonomy/processTaxonomy.sh
@@ -10,9 +10,5 @@ cat gi_taxid_nucl.dmp gi_taxid_prot.dmp
echo "Sorting gi2tax files..."
sort -n -k 1 gi_taxid_all.dmp > gi_taxid_sorted.txt
rm gi_taxid_nucl.dmp gi_taxid_prot.dmp gi_taxid_all.dmp
-echo "Removing parenthesis from names.dmp"
-cat names.dmp | sed s/\(/_/g | sed s/\)/_/g > names.temporary
-mv names.dmp names.dmp.orig
-mv names.temporary names.dmp
1
0