galaxy-commits
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions

20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1286910360 14400
# Node ID 772d6986c1afc7e78a9543fb5445e4d327df7df1
# Parent 71dd9ee7b9b945b28359f3e422095d7623841bce
sample_tracking:
- bug fix in method to set library permissions for the data transfer user
- code cleanup
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -102,8 +102,7 @@ class DataTransferGrid( grids.Grid ):
return sample_dataset.status
# Grid definition
title = "Sample Datasets"
- # TODO: can this be grid.mako???
- template = "admin/requests/datasets_grid.mako"
+ template = "admin/requests/grid.mako"
model_class = model.SampleDataset
default_sort_key = "-create_time"
num_rows_per_page = 50
@@ -118,7 +117,9 @@ class DataTransferGrid( grids.Grid ):
SizeColumn( "Size",
#key='size',
filterable="advanced" ),
- grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
+ grids.GridColumn( "Last Updated",
+ key="update_time",
+ format=time_ago ),
StatusColumn( "Status",
#key='status',
filterable="advanced" ),
@@ -560,7 +561,7 @@ class RequestsAdmin( BaseController, Use
lfp = trans.model.LibraryFolderPermissions( trans.app.security_agent.permitted_actions.LIBRARY_ADD.action,
folder,
datatx_user_private_role )
- trans.sa_session.add( dp )
+ trans.sa_session.add( lfp )
trans.sa_session.flush()
return datatx_user
def __send_message( self, trans, datatx_info, sample, id_list ):
--- a/templates/admin/requests/datasets_grid.mako
+++ /dev/null
@@ -1,27 +0,0 @@
-<%def name="custom_javascripts()">
- <script type="text/javascript">
- $("#select-dataset-action-button").bind( "click", function(e) {
- $.ajax({
- url: "${h.url_for( controller='requests_admin', action='remote_file_browser' )}",
- data: {id: 6},
- error: function() { alert( "Couldn't create new browser" ) },
- success: function(form_html) {
- show_modal("Select file", form_html, {
- "Cancel": function() { window.location = "${h.url_for( controller='requests_admin', action='browse_requests' )}"; },
- "Continue": function() { $(document).trigger("convert_dbkeys"); continue_fn(); }
- });
- $("#new-title").focus();
- replace_big_select_inputs();
- }
- });
- }
- </script>
-</%def>
-
-<%def name="javascripts()">
- ${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jquery.event.drag", "jquery.autocomplete", "jquery.mousewheel", "trackster", "ui.core", "ui.sortable" )}
- ${self.custom_javascripts()}
- ${parent.javascripts()}
-</%def>
-
-<%inherit file="/grid_base.mako"/>
1
0

galaxy-dist commit 71dd9ee7b9b9: sample_tracking bug fixes:
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 1286907824 14400
# Node ID 71dd9ee7b9b945b28359f3e422095d7623841bce
# Parent 0e7dbb91575d9c8abf39f759cd7bdf54efadcd80
sample_tracking bug fixes:
- samples search now working for new unsubmitted requests
- sample state auto update using ajax now working
--- a/templates/requests/common/manage_request.mako
+++ b/templates/requests/common/manage_request.mako
@@ -73,13 +73,13 @@
dataType: "json",
data: { ids: ids.join( "," ), states: states.join( "," ) },
success : function ( data ) {
- $.each( data, function( cntrller, id, val ) {
+ $.each( data, function(id, val, cntrller ) {
// 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;
+ sample_states[ parseInt( id ) ] = val.state;
});
updater( sample_states );
},
@@ -272,7 +272,6 @@
<input type="submit" name="cancel_change_state_button" value="Cancel"/></div>
%elif sample_operation_selected_value == trans.app.model.Sample.bulk_operations.SELECT_LIBRARY:
- ## sample_operation_selected_value == 'Select data library and folder'
<% libraries_selected_value = libraries_select_field.get_selected( return_value=True ) %><div class="form-row"><label>Select data library:</label>
@@ -303,7 +302,7 @@
%if request.samples and request.is_submitted:
<script type="text/javascript">
// Updater
- updater({${ ",".join( [ '"%s" : "%s"' % ( trans.security.encode_id( s.id ), s.state.name ) for s in request.samples ] ) }});
+ updater({${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
</script>
%endif
%if not managing_samples:
--- a/lib/galaxy/web/controllers/requests.py
+++ b/lib/galaxy/web/controllers/requests.py
@@ -13,11 +13,6 @@ class UserRequestsGrid( RequestsGrid ):
operations.append( grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted and item.is_unsubmitted ) ) )
operations.append( grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted and item.is_new ) ) )
operations.append( grids.GridOperation( "Undelete", allow_multiple=True, condition=( lambda item: item.deleted ) ) )
- global_actions = [
- grids.GridAction( "Create new request", dict( controller='requests_common',
- action='create_request',
- cntrller='requests' ) )
- ]
def apply_query_filter( self, trans, query, **kwd ):
# gvk ( 9/28/10 ) TODO: is this method needed?
return query.filter_by( user=trans.user )
@@ -69,6 +64,14 @@ class Requests( BaseController ):
message = "%d requests (highlighted in red) were rejected. Click on the request name for details." % rejected
kwd[ 'status' ] = status
kwd[ 'message' ] = message
+ # show the create request button to the user, only when the user has permissions
+ # to at least one request_type (sequencer configuration)
+ if len( trans.user.accessible_request_types( trans ) ):
+ self.request_grid.global_actions = [ grids.GridAction( "Create new request", dict( controller='requests_common',
+ action='create_request',
+ cntrller='requests' ) ) ]
+ else:
+ self.request_grid.global_actions = []
# Render the list view
return self.request_grid( trans, **kwd )
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -105,8 +105,7 @@ class RequestsGrid( grids.Grid ):
class RequestsCommon( BaseController, UsesFormDefinitionWidgets ):
@web.json
- def sample_state_updates( self, trans, cntrller, ids=None, states=None ):
- pass
+ def sample_state_updates( self, trans, ids=None, states=None, cntrller=None ):
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
@@ -118,16 +117,16 @@ class RequestsCommon( BaseController, Us
for id, state in zip( ids, states ):
sample = trans.sa_session.query( self.app.model.Sample ).get( id )
if sample.state.name != state:
- rval[id] = { "state": sample.state.name,
- "datasets": len( sample.datasets ),
- "html_state": unicode( trans.fill_template( "requests/common/sample_state.mako",
- sample=sample,
- cntrller=cntrller ),
- 'utf-8' ),
- "html_datasets": unicode( trans.fill_template( "requests/common/sample_datasets.mako",
- sample=sample,
- cntrller=cntrller ),
- 'utf-8' ) }
+ rval[ id ] = { "state": sample.state.name,
+ "datasets": len( sample.datasets ),
+ "html_state": unicode( trans.fill_template( "requests/common/sample_state.mako",
+ sample=sample,
+ cntrller=cntrller ),
+ 'utf-8' ),
+ "html_datasets": unicode( trans.fill_template( "requests/common/sample_datasets.mako",
+ sample=sample,
+ cntrller=cntrller ),
+ 'utf-8' ) }
return rval
@web.expose
@web.require_login( "create sequencing requests" )
--- a/templates/requests/common/find_samples.mako
+++ b/templates/requests/common/find_samples.mako
@@ -66,7 +66,11 @@
%for sample in samples:
<div class="form-row">
Sample: <b>${sample.name}</b> | Barcode: ${sample.bar_code}<br/>
- State: ${sample.state.name}<br/>
+ %if sample.request.is_new:
+ State: Unsubmitted<br/>
+ %else:
+ State: ${sample.state.name}<br/>
+ %endif
Datasets: <a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.transferred_dataset_files}/${len( sample.datasets )}</a><br/>
%if is_admin:
<i>User: ${sample.request.user.email}</i>
1
0

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 1286829357 14400
# Node ID 4bc2738671123bf3731e940ae1f2a0004ceca2be
# Parent b67978eeba2c67c86453515d90f81a13acb88586
First pass at Galaxy sample tracking re-write. Highlights: lots of code cleanup with several not-needed methods removed, many mthods and mako templates have been renamed clarify what they do, all object ids are now encoded, methods in the requests_common controller that include features requiring an admin user are now secured. More work is needed in the requests_admin controller which will be in the next commit.
--- a/templates/requests/common/sample_events.mako
+++ b/templates/requests/common/sample_events.mako
@@ -6,7 +6,7 @@
<h2>Events for Sample "${sample.name}"</h2><ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='show', id=trans.security.encode_id(sample.request.id) )}">
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}"><span>Browse this request</span></a></li></ul>
--- a/templates/library/common/browse_library.mako
+++ b/templates/library/common/browse_library.mako
@@ -471,7 +471,7 @@
<h2>Data Library “${library.name}”</h2><ul class="manage-table-actions">
- %if not library.deleted and ( ( trans.user_is_admin() and cntrller == 'requests_admin' ) or can_add ):
+ %if not library.deleted and ( ( trans.user_is_admin() and cntrller == 'library_admin' ) or can_add ):
<li><a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( library.root_folder.id ), use_panels=use_panels, show_deleted=show_deleted )}"><span>Add datasets</span></a></li><li><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( library.root_folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add folder</a></li>
%endif
--- /dev/null
+++ b/templates/requests/find_samples_index.mako
@@ -0,0 +1,14 @@
+<%inherit file="/webapps/galaxy/base_panels.mako"/>
+
+<%def name="init()">
+ <%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.active_view="requests"
+ self.message_box_visible=False
+ %>
+</%def>
+
+<%def name="center_panel()">
+ <iframe name="galaxy_main" id="galaxy_main" frameborder="0" style="position: absolute; width: 100%; height: 100%;" src="${h.url_for( controller='requests_common', action='find_samples' )}"></iframe>
+</%def>
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -856,9 +856,9 @@ APIKeys.table = Table( "api_keys", metad
assign_mapper( context, Sample, Sample.table,
properties=dict(
events=relation( SampleEvent, backref="sample",
- order_by=desc(SampleEvent.table.c.update_time) ),
+ order_by=desc( SampleEvent.table.c.update_time ) ),
datasets=relation( SampleDataset, backref="sample",
- order_by=desc(SampleDataset.table.c.update_time) ),
+ order_by=desc( SampleDataset.table.c.update_time ) ),
values=relation( FormValues,
primaryjoin=( Sample.table.c.form_values_id == FormValues.table.c.id ) ),
request=relation( Request,
@@ -885,9 +885,9 @@ assign_mapper( context, Request, Request
backref="requests" ),
samples=relation( Sample,
primaryjoin=( Request.table.c.id == Sample.table.c.request_id ),
- order_by=asc(Sample.table.c.id) ),
+ order_by=asc( Sample.table.c.id ) ),
events=relation( RequestEvent, backref="request",
- order_by=desc(RequestEvent.table.c.update_time) )
+ order_by=desc( RequestEvent.table.c.update_time ) )
) )
assign_mapper( context, RequestEvent, RequestEvent.table,
@@ -897,7 +897,7 @@ assign_mapper( context, RequestType, Req
properties=dict( states=relation( SampleState,
backref="request_type",
primaryjoin=( RequestType.table.c.id == SampleState.table.c.request_type_id ),
- order_by=asc(SampleState.table.c.update_time) ),
+ order_by=asc( SampleState.table.c.update_time ) ),
request_form=relation( FormDefinition,
primaryjoin=( RequestType.table.c.request_form_id == FormDefinition.table.c.id ) ),
sample_form=relation( FormDefinition,
--- a/templates/admin/requests/view_request_type.mako
+++ b/templates/admin/requests/view_request_type.mako
@@ -1,11 +1,12 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
-
%if message:
${render_msg( message, status )}
%endif
+<% states_list = request_type.states %>
+
<h2>Sequencer Configuration "${request_type.name}"</h2><div class="toolForm">
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -1,163 +1,38 @@
from galaxy.web.base.controller import *
from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy.model.orm import *
-from galaxy.datatypes import sniff
from galaxy import model, util
-from galaxy.util.streamball import StreamBall
-import logging, tempfile, zipfile, tarfile, os, sys, subprocess, smtplib, socket
-from galaxy.web.form_builder import *
-from datetime import datetime, timedelta
-from email.MIMEText import MIMEText
-from sqlalchemy.sql.expression import func, and_
-from sqlalchemy.sql import select
-import pexpect
-import ConfigParser, threading, time
+from galaxy.web.form_builder import *
+from galaxy.web.controllers.requests_common import RequestsGrid, invalid_id_redirect
from amqplib import client_0_8 as amqp
-import csv
+import logging, os, pexpect, ConfigParser
+
log = logging.getLogger( __name__ )
-#
-# ---- Request Grid ------------------------------------------------------------
-#
+class UserColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, request ):
+ return request.user.email
-class RequestsGrid( grids.Grid ):
- # Custom column types
- class NameColumn( grids.TextColumn ):
- def get_value(self, trans, grid, request):
- return request.name
- class DescriptionColumn( grids.TextColumn ):
- def get_value(self, trans, grid, request):
- return request.desc
- class SamplesColumn( grids.GridColumn ):
- def get_value(self, trans, grid, request):
- return str(len(request.samples))
- class TypeColumn( grids.TextColumn ):
- def get_value(self, trans, grid, request):
- return request.type.name
- class StateColumn( grids.GridColumn ):
- def __init__( self, col_name, key, model_class, event_class, filterable, link ):
- grids.GridColumn.__init__(self, col_name, key=key, model_class=model_class, filterable=filterable, link=link)
- self.event_class = event_class
- def get_value(self, trans, grid, request):
- if request.state() == request.states.REJECTED:
- return '<div class="count-box state-color-error">%s</div>' % request.state()
- elif request.state() == request.states.NEW:
- return '<div class="count-box state-color-queued">%s</div>' % request.state()
- elif request.state() == request.states.SUBMITTED:
- return '<div class="count-box state-color-running">%s</div>' % request.state()
- elif request.state() == request.states.COMPLETE:
- return '<div class="count-box state-color-ok">%s</div>' % request.state()
- return request.state()
- def filter( self, trans, user, query, column_filter ):
- """ Modify query to filter request by state. """
- if column_filter == "All":
- return query
- if column_filter:
- # select r.id, r.name, re.id, re.state
- # from request as r, request_event as re
- # where re.request_id=r.id and re.state='Complete' and re.create_time in
- # (select MAX( create_time)
- # from request_event
- # group by request_id)
- q = query.join(self.event_class.table)\
- .filter( self.model_class.table.c.id==self.event_class.table.c.request_id )\
- .filter( self.event_class.table.c.state==column_filter )\
- .filter( self.event_class.table.c.id.in_(select(columns=[func.max(self.event_class.table.c.id)],
- from_obj=self.event_class.table,
- group_by=self.event_class.table.c.request_id)))
- return q
- def get_accepted_filters( self ):
- """ Returns a list of accepted filters for this column. """
- accepted_filter_labels_and_vals = [ model.Request.states.NEW,
- model.Request.states.REJECTED,
- model.Request.states.SUBMITTED,
- model.Request.states.COMPLETE,
- "All"]
- accepted_filters = []
- for val in accepted_filter_labels_and_vals:
- label = val.lower()
- args = { self.key: val }
- accepted_filters.append( grids.GridColumnFilter( label, args) )
- return accepted_filters
- class UserColumn( grids.TextColumn ):
- def get_value(self, trans, grid, request):
- return request.user.email
- def sort( self, query, ascending ):
- """ Sort column using case-insensitive alphabetical sorting on item's username. """
- if ascending:
- query = query.order_by( func.lower ( self.model_class.email ).asc() )
- else:
- query = query.order_by( func.lower( self.model_class.email ).desc() )
- return query
-
- # Grid definition
- title = "Sequencing Requests"
- template = "admin/requests/grid.mako"
- model_class = model.Request
- default_sort_key = "-update_time"
- num_rows_per_page = 50
- preserve_state = True
- use_paging = True
- default_filter = dict( deleted="False")
- columns = [
- NameColumn( "Name",
- key="name",
- model_class=model.Request,
- link=( lambda item: iff( item.deleted, None, dict( operation="show", id=item.id ) ) ),
- attach_popup=True,
- filterable="advanced" ),
- DescriptionColumn( "Description",
- key='desc',
- model_class=model.Request,
- filterable="advanced" ),
- SamplesColumn( "Sample(s)",
- link=( lambda item: iff( item.deleted, None, dict( operation="show", id=item.id ) ) ), ),
- TypeColumn( "Sequencer",
- link=( lambda item: iff( item.deleted, None, dict( operation="view_type", id=item.type.id ) ) ), ),
- grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
- grids.DeletedColumn( "Deleted",
- key="deleted",
- visible=False,
- filterable="advanced" ),
- StateColumn( "State",
- key='state',
- model_class=model.Request,
- event_class=model.RequestEvent,
- filterable="advanced",
- link=( lambda item: iff( item.deleted, None, dict( operation="events", id=item.id ) ) ),
- ),
- UserColumn( "User",
- #key='owner',
- model_class=model.User
- #filterable="advanced"
- )
-
- ]
- columns.append( grids.MulticolFilterColumn( "Search",
- cols_to_filter=[ columns[0], columns[1], columns[6] ],
- key="free-text-search",
- visible=False,
- filterable="standard" ) )
- operations = [
- grids.GridOperation( "Submit", allow_multiple=False, condition=( lambda item: not item.deleted and item.unsubmitted() and item.samples ),
- confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." ),
- grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ),
- grids.GridOperation( "Reject", allow_multiple=False, condition=( lambda item: not item.deleted and item.submitted() ) ),
- grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted ) ),
- grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ),
- grids.GridOperation( "Purge", allow_multiple=False, confirm="This will permanently delete the sequencing request. Click OK to proceed.", condition=( lambda item: item.deleted ) ),
- ]
+class AdminRequestsGrid( RequestsGrid ):
+ columns = [ col for col in RequestsGrid.columns ]
+ columns.append( UserColumn( "User",
+ model_class=model.User,
+ key='username' ) )
+ operations = [ operation for operation in RequestsGrid.operations ]
+ operations.append( grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ) )
+ operations.append( grids.GridOperation( "Reject", allow_multiple=False, condition=( lambda item: not item.deleted and item.is_submitted ) ) )
+ operations.append( grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted ) ) )
+ operations.append( grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ) )
+ operations.append( grids.GridOperation( "Purge",
+ allow_multiple=False,
+ confirm="This will permanently delete this sequencing request. Click OK to proceed.",
+ condition=( lambda item: item.deleted ) ) )
global_actions = [
grids.GridAction( "Create new request", dict( controller='requests_common',
- cntrller='requests_admin',
- action='new',
- select_request_type='True' ) )
+ action='create_request',
+ cntrller='requests_admin' ) )
]
-
-#
-# ---- Request Type Grid ------------------------------------------------------
-#
class RequestTypeGrid( grids.Grid ):
# Custom column types
class NameColumn( grids.TextColumn ):
@@ -172,9 +47,10 @@ class RequestTypeGrid( grids.Grid ):
class SampleFormColumn( grids.TextColumn ):
def get_value(self, trans, grid, request_type):
return request_type.sample_form.name
+
# Grid definition
title = "Sequencer Configurations"
- template = "admin/requests/manage_request_types.mako"
+ template = "admin/requests/grid.mako"
model_class = model.RequestType
default_sort_key = "-create_time"
num_rows_per_page = 50
@@ -183,23 +59,21 @@ class RequestTypeGrid( grids.Grid ):
default_filter = dict( deleted="False" )
columns = [
NameColumn( "Name",
- key="name",
- model_class=model.RequestType,
+ key="name",
link=( lambda item: iff( item.deleted, None, dict( operation="view", id=item.id ) ) ),
attach_popup=True,
filterable="advanced" ),
DescriptionColumn( "Description",
key='desc',
- model_class=model.Request,
filterable="advanced" ),
RequestFormColumn( "Request Form",
- link=( lambda item: iff( item.deleted, None, dict( operation="view_form", id=item.request_form.id ) ) ), ),
+ link=( lambda item: iff( item.deleted, None, dict( operation="view_form", id=item.request_form.id ) ) ) ),
SampleFormColumn( "Sample Form",
- link=( lambda item: iff( item.deleted, None, dict( operation="view_form", id=item.sample_form.id ) ) ), ),
+ link=( lambda item: iff( item.deleted, None, dict( operation="view_form", id=item.sample_form.id ) ) ) ),
grids.DeletedColumn( "Deleted",
- key="deleted",
- visible=False,
- filterable="advanced" )
+ key="deleted",
+ visible=False,
+ filterable="advanced" )
]
columns.append( grids.MulticolFilterColumn( "Search",
cols_to_filter=[ columns[0], columns[1] ],
@@ -208,53 +82,45 @@ class RequestTypeGrid( grids.Grid ):
filterable="standard" ) )
operations = [
grids.GridOperation( "Permissions", allow_multiple=False, condition=( lambda item: not item.deleted ) ),
- #grids.GridOperation( "Clone", allow_multiple=False, condition=( lambda item: not item.deleted ) ),
grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted ) ),
grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ),
]
global_actions = [
- grids.GridAction( "Create new sequencer configuration", dict( controller='requests_admin',
- action='create_request_type' ) )
+ grids.GridAction( "Create new sequencer configuration", dict( controller='requests_admin', action='create_request_type' ) )
]
-
-# ---- Data Transfer Grid ------------------------------------------------------
-#
class DataTransferGrid( grids.Grid ):
# Custom column types
class NameColumn( grids.TextColumn ):
- def get_value(self, trans, grid, sample_dataset):
+ def get_value( self, trans, grid, sample_dataset ):
return sample_dataset.name
class SizeColumn( grids.TextColumn ):
- def get_value(self, trans, grid, sample_dataset):
+ def get_value( self, trans, grid, sample_dataset ):
return sample_dataset.size
class StatusColumn( grids.TextColumn ):
- def get_value(self, trans, grid, sample_dataset):
+ def get_value( self, trans, grid, sample_dataset ):
return sample_dataset.status
# Grid definition
title = "Sample Datasets"
+ # TODO: can this be grid.mako???
template = "admin/requests/datasets_grid.mako"
model_class = model.SampleDataset
default_sort_key = "-create_time"
num_rows_per_page = 50
preserve_state = True
use_paging = True
- #default_filter = dict( deleted="False" )
columns = [
NameColumn( "Name",
#key="name",
- model_class=model.SampleDataset,
link=( lambda item: dict( operation="view", id=item.id ) ),
attach_popup=True,
filterable="advanced" ),
SizeColumn( "Size",
#key='size',
- model_class=model.SampleDataset,
filterable="advanced" ),
grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
StatusColumn( "Status",
#key='status',
- model_class=model.SampleDataset,
filterable="advanced" ),
]
columns.append( grids.MulticolFilterColumn( "Search",
@@ -263,517 +129,443 @@ class DataTransferGrid( grids.Grid ):
visible=False,
filterable="standard" ) )
operations = [
- grids.GridOperation( "Start Transfer", allow_multiple=True, condition=( lambda item: item.status in [model.Sample.transfer_status.NOT_STARTED] ) ),
- grids.GridOperation( "Rename", allow_multiple=True, allow_popup=False, condition=( lambda item: item.status in [model.Sample.transfer_status.NOT_STARTED] ) ),
- grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.status in [model.Sample.transfer_status.NOT_STARTED] ) ),
+ grids.GridOperation( "Start Transfer", allow_multiple=True, condition=( lambda item: item.status in [ model.Sample.transfer_status.NOT_STARTED ] ) ),
+ grids.GridOperation( "Rename", allow_multiple=True, allow_popup=False, condition=( lambda item: item.status in [ model.Sample.transfer_status.NOT_STARTED ] ) ),
+ grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.status in [ model.Sample.transfer_status.NOT_STARTED ] ) ),
]
def apply_query_filter( self, trans, query, **kwd ):
- return query.filter_by( sample_id=kwd['sample_id'] )
-#
-# ---- Request Controller ------------------------------------------------------
-#
+ sample_id = kwd.get( 'sample_id', None )
+ if not sample_id:
+ return query
+ return query.filter_by( sample_id=trans.security.decode_id( sample_id ) )
class RequestsAdmin( BaseController, UsesFormDefinitionWidgets ):
- request_grid = RequestsGrid()
+ request_grid = AdminRequestsGrid()
requesttype_grid = RequestTypeGrid()
datatx_grid = DataTransferGrid()
-
@web.expose
@web.require_admin
def index( self, trans ):
return trans.fill_template( "/admin/requests/index.mako" )
-
@web.expose
@web.require_admin
- def list( self, trans, **kwd ):
- '''
- List all request made by the current user
- '''
+ def browse_requests( self, trans, **kwd ):
if 'operation' in kwd:
operation = kwd['operation'].lower()
- if not kwd.get( 'id', None ):
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='list',
- status='error',
- message="Invalid request ID") )
- if operation == "show":
+ if operation == "edit":
return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_basic_request_info',
cntrller='requests_admin',
- action='show',
**kwd ) )
- elif operation == "submit":
+ if operation == "manage_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
cntrller='requests_admin',
- action='submit',
**kwd ) )
- elif operation == "delete":
+ if operation == "reject":
+ return self.reject_request( trans, **kwd )
+ if operation == "view_type":
+ return self.view_request_type( trans, **kwd )
+ if operation == "delete":
return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='delete_request',
cntrller='requests_admin',
- action='delete',
**kwd ) )
- elif operation == "undelete":
+ if operation == "undelete":
return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='undelete_request',
cntrller='requests_admin',
- action='undelete',
**kwd ) )
- elif operation == "edit":
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller='requests_admin',
- action='edit',
- show=True, **kwd ) )
- elif operation == "events":
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller='requests_admin',
- action='events',
- **kwd ) )
- elif operation == "reject":
- return self.__reject_request( trans, **kwd )
- elif operation == "view_type":
- return self.__view_request_type( trans, **kwd )
- # Render the grid view
+ # Render the list view
return self.request_grid( trans, **kwd )
-
@web.json
- def get_file_details( self, trans, id=None, folder_path=None ):
- def print_ticks(d):
+ def get_file_details( self, trans, id, folder_path ):
+ def print_ticks( d ):
+ # TODO: why is this method here? Add comments!
pass
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
- request = trans.sa_session.query( self.app.model.Request ).get( int(id) )
+ request = trans.sa_session.query( trans.model.Request ).get( int( id ) )
datatx_info = request.type.datatx_info
cmd = 'ssh %s@%s "ls -oghp \'%s\'"' % ( datatx_info['username'],
datatx_info['host'],
- folder_path )
- output = pexpect.run(cmd, events={'.ssword:*': datatx_info['password']+'\r\n',
- pexpect.TIMEOUT:print_ticks},
- timeout=10)
- return unicode(output.replace('\n', '<br/>'))
-
+ folder_path )
+ output = pexpect.run( cmd,
+ events={ '.ssword:*' : datatx_info[ 'password'] + '\r\n', pexpect.TIMEOUT : print_ticks },
+ timeout=10 )
+ return unicode( output.replace( '\n', '<br/>' ) )
@web.json
- def open_folder( self, trans, id=None, folder_path=None ):
- def print_ticks(d):
+ def open_folder( self, trans, id, folder_path ):
+ def print_ticks( d ):
pass
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
- request = trans.sa_session.query( self.app.model.Request ).get( int(id) )
- return self.__get_files(trans, request.type, folder_path)
-
- def __reject_request(self, trans, **kwd):
- try:
- request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id(kwd['id']) )
- except:
- message = "Invalid request ID"
- log.warn( message )
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='list',
- status='error',
- message=message,
- **kwd) )
- return trans.fill_template( '/admin/requests/reject.mako',
- request=request)
+ request = trans.sa_session.query( trans.model.Request ).get( int( id ) )
+ return self.__get_files( trans, request.type, folder_path )
@web.expose
@web.require_admin
- def reject(self, trans, **kwd):
+ def reject( self, trans, **kwd ):
params = util.Params( kwd )
- if params.get('cancel_reject_button', False):
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='list',
- operation='show_request',
- id=kwd['id']))
+ request_id = params.get( 'id', '' )
+ status = params.get( 'status', 'done' )
+ message = params.get( 'message', 'done' )
+ if params.get( 'cancel_reject_button', False ):
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
+ cntrller='requests_admin',
+ id=request_id ) )
try:
- request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id(kwd['id']) )
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
- message = "Invalid request ID"
- log.warn( message )
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='list',
- status='error',
- message=message,
- **kwd) )
- # validate
- if not params.get('comment', ''):
+ return invalid_id_redirect( trans, 'requests_admin', request_id )
+ # Validate
+ comment = util.restore_text( params.get( 'comment', '' ) )
+ if not comment:
+ status='error'
+ message='A reason for rejecting the request is required.'
return trans.fill_template( '/admin/requests/reject.mako',
- request=request, status='error',
- message='A comment is required for rejecting a request.')
- # create an event with state 'Rejected' for this request
- comments = util.restore_text( params.comment )
- event = trans.app.model.RequestEvent(request, request.states.REJECTED, comments)
+ cntrller='requests_admin',
+ request=request,
+ status=status,
+ message=message )
+ # Create an event with state 'Rejected' for this request
+ event = trans.model.RequestEvent( request, request.states.REJECTED, comment )
trans.sa_session.add( event )
trans.sa_session.flush()
+ message='Request (%s) has been rejected.' % request.name
return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='list',
- status='done',
- message='Request <b>%s</b> has been rejected.' % request.name) )
- #
+ action='browse_requests',
+ status=status,
+ message=message,
+ **kwd ) )
# Data transfer from sequencer
- #
-
@web.expose
@web.require_admin
def manage_datasets( self, trans, **kwd ):
if 'operation' in kwd:
- operation = kwd['operation'].lower()
- if not kwd.get( 'id', None ):
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='list',
- status='error',
- message="Invalid sample dataset ID") )
+ operation = kwd[ 'operation' ].lower()
+ dataset_id = kwd.get( 'id', None )
+ if not dataset_id:
+ return invalid_id_redirect( trans, 'requests_admin', dataset_id )
+ id_list = util.listify( dataset_id )
if operation == "view":
- sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(kwd['id']) )
- return trans.fill_template( '/admin/requests/dataset.mako',
- sample=sample_dataset.sample,
- sample_dataset=sample_dataset)
-
+ sample_dataset_id = trans.security.decode_id( kwd['id'] )
+ sample_dataset = trans.sa_session.query( trans.model.SampleDataset ).get( sample_dataset_id )
+ return trans.fill_template( '/admin/requests/dataset.mako',
+ sample_dataset=sample_dataset )
elif operation == "delete":
- id_list = util.listify( kwd['id'] )
not_deleted = []
for id in id_list:
- sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(id) )
+ sample_dataset = trans.sa_session.query( trans.model.SampleDataset ).get( trans.security.decode_id( id ) )
sample_id = sample_dataset.sample_id
if sample_dataset.status == sample_dataset.sample.transfer_status.NOT_STARTED:
trans.sa_session.delete( sample_dataset )
trans.sa_session.flush()
else:
- not_deleted.append(sample_dataset.name)
- message = '%i dataset(s) have been successfully deleted. ' % (len(id_list) - len(not_deleted))
+ not_deleted.append( sample_dataset.name )
+ message = '%i datasets have been successfully deleted. ' % ( len( id_list ) - len( not_deleted ) )
status = 'done'
if not_deleted:
status = 'warning'
- message = message + '%s could not be deleted. Only datasets with transfer status "Not Started" can be deleted. ' % str(not_deleted)
+ message = message + '%s could not be deleted because their transfer status is not "Not Started". ' % str( not_deleted )
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
- sample_id=sample_id,
+ sample_id=trans.security.encode_id( sample_id ),
status=status,
- message=message) )
-
+ message=message ) )
elif operation == "rename":
- id_list = util.listify( kwd['id'] )
- sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(id_list[0]) )
+ sample_dataset_id = id_list[0]
+ sample_dataset = trans.sa_session.query( trans.model.SampleDataset ).get( trans.security.decode_id( sample_dataset_id ) )
return trans.fill_template( '/admin/requests/rename_datasets.mako',
sample=sample_dataset.sample,
id_list=id_list )
elif operation == "start transfer":
- id_list = util.listify( kwd['id'] )
- sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(id_list[0]) )
- self.__start_datatx(trans, sample_dataset.sample, id_list)
-
-
+ sample_dataset_id = id_list[0]
+ sample_dataset = trans.sa_session.query( trans.model.SampleDataset ).get( trans.security.decode_id( sample_dataset_id ) )
+ self.__start_datatx( trans, sample_dataset.sample, id_list )
# Render the grid view
+ sample_id = kwd.get( 'sample_id', None )
try:
- sample = trans.sa_session.query( trans.app.model.Sample ).get( kwd['sample_id'] )
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id ( sample_id ) )
except:
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='list',
- status='error',
- message="Invalid sample ID" ) )
- self.datatx_grid.title = 'Datasets of Sample "%s"' % sample.name
- self.datatx_grid.global_actions = [
- grids.GridAction( "Refresh",
- dict( controller='requests_admin',
- action='manage_datasets',
- sample_id=sample.id) ),
- grids.GridAction( "Select Datasets",
- dict(controller='requests_admin',
- action='get_data',
- request_id=sample.request.id,
- folder_path=sample.request.type.datatx_info['data_dir'],
- sample_id=sample.id,
- show_page=True)),
- grids.GridAction( 'Data Library "%s"' % sample.library.name,
- dict(controller='library_common',
- action='browse_library',
- cntrller='library_admin',
- id=trans.security.encode_id( sample.library.id))),
- grids.GridAction( "Browse this request",
- dict( controller='requests_admin',
- action='list',
- operation='show',
- id=trans.security.encode_id(sample.request.id)))]
+ return invalid_id_redirect( trans, 'requests_admin', sample_id )
+ self.datatx_grid.title = 'Datasets of sample "%s"' % sample.name
+ self.datatx_grid.global_actions = [ grids.GridAction( "Refresh",
+ dict( controller='requests_admin',
+ action='manage_datasets',
+ sample_id=sample_id ) ),
+ grids.GridAction( "Select datasets",
+ dict( controller='requests_admin',
+ action='get_data',
+ request_id=trans.security.encode_id( sample.request.id ),
+ folder_path=sample.request.type.datatx_info[ 'data_dir' ],
+ sample_id=sample_id,
+ show_page=True ) ),
+ grids.GridAction( 'Data library "%s"' % sample.library.name,
+ dict( controller='library_common',
+ action='browse_library',
+ cntrller='library_admin',
+ id=trans.security.encode_id( sample.library.id ) ) ),
+ grids.GridAction( "Browse this request",
+ dict( controller='requests_common',
+ action='manage_request',
+ cntrller='requests_admin',
+ id=trans.security.encode_id( sample.request.id ) ) ) ]
return self.datatx_grid( trans, **kwd )
-
@web.expose
@web.require_admin
def rename_datasets( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ status = params.get( 'status', 'done' )
+ sample_id = kwd.get( 'sample_id', None )
try:
- sample = trans.sa_session.query( trans.app.model.Sample ).get( trans.security.decode_id(kwd['sample_id']))
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
except:
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='list',
- status='error',
- message="Invalid sample ID" ) )
+ return invalid_id_redirect( trans, 'requests_admin', sample_id )
if params.get( 'save_button', False ):
id_list = util.listify( kwd['id_list'] )
for id in id_list:
- sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(id) )
+ sample_dataset = trans.sa_session.query( trans.model.SampleDataset ).get( trans.security.decode_id( id ) )
prepend = util.restore_text( params.get( 'prepend_%i' % sample_dataset.id, '' ) )
- name = util.restore_text( params.get( 'name_%i' % sample_dataset.id, sample_dataset.name ) )
+ name = util.restore_text( params.get( 'name_%i' % sample_dataset.id, sample_dataset.name ) )
if prepend == 'None':
sample_dataset.name = name
else:
- sample_dataset.name = prepend+'_'+name
+ sample_dataset.name = '%s_%s' % ( prepend, name )
trans.sa_session.add( sample_dataset )
trans.sa_session.flush()
return trans.fill_template( '/admin/requests/rename_datasets.mako',
- sample=sample, id_list=id_list,
+ sample=sample,
+ id_list=id_list,
message='Changes saved successfully.',
status='done' )
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
- sample_id=sample.id) )
-
-
- def __get_files(self, trans, request_type, folder_path):
- '''
- This method retrieves the filenames to be transfer from the remote host.
- '''
+ sample_id=sample_id ) )
+ def __get_files( self, trans, request_type, folder_path ):
+ # Retrieves the filenames to be transferred from the remote host.
+ # FIXME: sample is used in this method, but no sample obj is received...
datatx_info = request_type.datatx_info
- if not datatx_info['host'] or not datatx_info['username'] or not datatx_info['password']:
+ if not datatx_info[ 'host' ] or not datatx_info[ 'username' ] or not datatx_info[ 'password' ]:
+ status = 'error'
message = "Error in sequencer login information."
return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='view_dataset_transfer',
cntrller='requests_admin' ,
- action='show_datatx_page',
- sample_id=trans.security.encode_id(sample.id),
- status='error',
- message=message))
- def print_ticks(d):
+ sample_id=trans.security.encode_id( sample.id ),
+ status=status,
+ message=message ) )
+ def print_ticks( d ):
pass
- cmd = 'ssh %s@%s "ls -p \'%s\'"' % ( datatx_info['username'],
- datatx_info['host'],
- folder_path)
- output = pexpect.run(cmd, events={'.ssword:*': datatx_info['password']+'\r\n',
- pexpect.TIMEOUT:print_ticks},
- timeout=10)
+ cmd = 'ssh %s@%s "ls -p \'%s\'"' % ( datatx_info['username'], datatx_info['host'], folder_path )
+ output = pexpect.run( cmd,
+ events={ '.ssword:*' : datatx_info['password'] + '\r\n', pexpect.TIMEOUT : print_ticks },
+ timeout=10 )
if 'No such file or directory' in output:
- message = "No such folder (%s) exists on the sequencer." % folder_path
+ status = 'error'
+ message = "No folder named (%s) exists on the sequencer." % folder_path
return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='view_dataset_transfer',
cntrller='requests_admin' ,
- action='show_datatx_page',
- sample_id=trans.security.encode_id(sample.id),
- message=message, status='error',
- folder_path=folder_path ))
+ sample_id=trans.security.encode_id( sample.id ),
+ folder_path=folder_path,
+ status=status,
+ message=message ) )
return output.splitlines()
-
-# def __get_files_in_dir(self, trans, sample, folder_path):
-# tmpfiles = self.__get_files(trans, sample, folder_path)
-# for tf in tmpfiles:
-# if tf[-1] == os.sep:
-# self.__get_files_in_dir(trans, sample, os.path.join(folder_path, tf))
-# else:
-# sample.dataset_files.append([os.path.join(folder_path, tf),
-# sample.transfer_status.NOT_STARTED])
-# trans.sa_session.add( sample )
-# trans.sa_session.flush()
-# return
-
-
- def __samples_selectbox(self, trans, request, sample_id=None):
- samples_selectbox = SelectField('sample_id')
- for i, s in enumerate(request.samples):
- if str(s.id) == sample_id:
- samples_selectbox.add_option(s.name, s.id, selected=True)
- else:
- samples_selectbox.add_option(s.name, s.id)
- return samples_selectbox
-
+ def __check_path( self, a_path ):
+ # Return a valid folder_path
+ if a_path and not a_path.endswith( os.sep ):
+ a_path += os.sep
+ return a_path
+ def __build_sample_id_select_field( self, request, selected_value ):
+ return build_select_field( trans, request.samples, 'name', 'sample_id', selected_value=selected_value, refresh_on_change=False )
@web.expose
@web.require_admin
- def get_data(self, trans, **kwd):
+ def get_data( self, trans, **kwd ):
params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ request_id = kwd.get( 'request_id', None )
try:
- request = trans.sa_session.query( trans.app.model.Request ).get( kwd['request_id'] )
+ request = trans.sa_session.query( trans.model.Request ).get( request_id )
except:
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='list',
- status='error',
- message="Invalid request ID" ) )
- files_list = util.listify( params.get( 'files_list', '' ) )
- folder_path = util.restore_text( params.get( 'folder_path',
- request.type.datatx_info['data_dir'] ) )
- sbox = self.__samples_selectbox(trans, request, kwd.get('sample_id', None))
+ return invalid_id_redirect( trans, 'requests_admin', request_id )
+ files_list = util.listify( params.get( 'files_list', '' ) )
+ folder_path = util.restore_text( params.get( 'folder_path', request.type.datatx_info[ 'data_dir' ] ) )
+ selected_value = kwd.get( 'sample_id', 'none' )
+ sample_id_select_field = self.__build_sample_id_select_field( request, selected_value )
if not folder_path:
- return trans.fill_template( '/admin/requests/get_data.mako',
- cntrller='requests_admin', request=request,
- samples_selectbox=sbox, files=[],
+ return trans.fill_template( '/admin/requests/dataset_transfer.mako',
+ cntrller='requests_admin',
+ request=request,
+ sample_id_select_field=sample_id_select_field,
+ files=[],
folder_path=folder_path )
- if folder_path[-1] != os.sep:
- folder_path = folder_path+os.sep
+ folder_path = self.__check_path( folder_path )
+ sample_id = kwd.get( 'sample_id', None )
if params.get( 'show_page', False ):
- if kwd.get('sample_id', None):
- sample = trans.sa_session.query( trans.app.model.Sample ).get( kwd['sample_id'] )
+ if sample_id:
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
if sample.datasets:
- folder_path = os.path.dirname(sample.datasets[-1].file_path)
- return trans.fill_template( '/admin/requests/get_data.mako',
- cntrller='requests_admin', request=request,
- samples_selectbox=sbox, files=[],
+ folder_path = os.path.dirname( sample.datasets[-1].file_path )
+ return trans.fill_template( '/admin/requests/dataset_transfer.mako',
+ cntrller='requests_admin',
+ request=request,
+ sample_id_select_field=sample_id_select_field,
+ files=[],
folder_path=folder_path,
- status=status, message=message )
+ status=status,
+ message=message )
elif params.get( 'browse_button', False ):
# get the filenames from the remote host
- files = self.__get_files(trans, request.type, folder_path)
- if folder_path[-1] != os.sep:
- folder_path += os.sep
- return trans.fill_template( '/admin/requests/get_data.mako',
- cntrller='requests_admin', request=request,
- samples_selectbox=sbox, files=files,
+ files = self.__get_files( trans, request.type, folder_path )
+ return trans.fill_template( '/admin/requests/dataset_transfer.mako',
+ cntrller='requests_admin',
+ request=request,
+ sample_id_select_field=sample_id_select_field,
+ files=files,
folder_path=folder_path,
- status=status, message=message )
+ status=status,
+ message=message )
elif params.get( 'folder_up', False ):
- if folder_path[-1] == os.sep:
- folder_path = os.path.dirname(folder_path[:-1])
# get the filenames from the remote host
- files = self.__get_files(trans, request.type, folder_path)
- if folder_path[-1] != os.sep:
- folder_path += os.sep
- return trans.fill_template( '/admin/requests/get_data.mako',
- cntrller='requests_admin',request=request,
- samples_selectbox=sbox, files=files,
+ files = self.__get_files( trans, request.type, folder_path )
+ return trans.fill_template( '/admin/requests/dataset_transfer.mako',
+ cntrller='requests_admin',
+ request=request,
+ sample_id_select_field=sample_id_select_field,
+ files=files,
folder_path=folder_path,
- status=status, message=message )
+ status=status,
+ message=message )
elif params.get( 'open_folder', False ):
- if len(files_list) == 1:
- folder_path = os.path.join(folder_path, files_list[0])
+ if len( files_list ) == 1:
+ folder_path = os.path.join( folder_path, files_list[0] )
+ folder_path = self.__check_path( folder_path )
# get the filenames from the remote host
- files = self.__get_files(trans, request.type, folder_path)
- if folder_path[-1] != os.sep:
- folder_path += os.sep
- return trans.fill_template( '/admin/requests/get_data.mako',
- cntrller='requests_admin', request=request,
- samples_selectbox=sbox, files=files,
+ files = self.__get_files( trans, request.type, folder_path )
+ return trans.fill_template( '/admin/requests/dataset_transfer.mako',
+ cntrller='requests_admin',
+ request=request,
+ sample_id_select_field=sample_id_select_field,
+ files=files,
folder_path=folder_path,
- status=status, message=message )
+ status=status,
+ message=message )
elif params.get( 'select_show_datasets_button', False ):
- sample = trans.sa_session.query( trans.app.model.Sample ).get( kwd['sample_id'] )
- retval = self.__save_sample_datasets(trans, sample, files_list, folder_path)
- if retval: message='The dataset(s) %s have been selected for sample <b>%s</b>' %(str(retval)[1:-1].replace("'", ""), sample.name)
- else: message = None
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
+ retval = self.__save_sample_datasets( trans, sample, files_list, folder_path )
+ if retval:
+ message = 'The datasets %s have been selected for sample <b>%s</b>' % ( str( retval )[1:-1].replace( "'", "" ), sample.name )
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
- sample_id=sample.id,
- status='done',
- message=message) )
+ sample_id=sample_id,
+ message=message,
+ status=status ) )
elif params.get( 'select_more_button', False ):
- sample = trans.sa_session.query( trans.app.model.Sample ).get( kwd['sample_id'] )
- retval = self.__save_sample_datasets(trans, sample, files_list, folder_path)
- if retval: message='The dataset(s) %s have been selected for sample <b>%s</b>' %(str(retval)[1:-1].replace("'", ""), sample.name)
- else: message = None
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
+ retval = self.__save_sample_datasets( trans, sample, files_list, folder_path )
+ if retval:
+ message='The datasets %s have been selected for sample <b>%s</b>' % ( str( retval )[1:-1].replace( "'", "" ), sample.name )
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='get_data',
- request_id=sample.request.id,
+ request_id=trans.security.encode_id( sample.request.id ),
folder_path=folder_path,
- sample_id=sample.id,
+ sample_id=sample_id,
open_folder=True,
- status='done',
- message=message))
+ message=message,
+ status=status ) )
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='get_data',
- request_id=sample.request.id,
+ request_id=trans.security.encode_id( sample.request.id ),
folder_path=folder_path,
- show_page=True))
-
- def __save_sample_datasets(self, trans, sample, files_list, folder_path):
+ show_page=True ) )
+ def __save_sample_datasets( self, trans, sample, files_list, folder_path ):
files = []
- if len(files_list):
+ if files_list:
for f in files_list:
- filepath = os.path.join(folder_path, f)
+ filepath = os.path.join( folder_path, f )
if f[-1] == os.sep:
- # the selected item is a folder so transfer all the
- # folder contents
+ # the selected item is a folder so transfer all the folder contents
# FIXME
#self.__get_files_in_dir(trans, sample, filepath)
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='get_data',
request=sample.request,
folder_path=folder_path,
- open_folder=True))
+ open_folder=True ) )
else:
- sample_dataset = trans.app.model.SampleDataset( sample=sample,
- file_path=filepath,
- status=sample.transfer_status.NOT_STARTED,
- name=self.__dataset_name(sample, filepath.split('/')[-1]),
- error_msg='',
- size=sample.dataset_size(filepath))
+ sample_dataset = trans.model.SampleDataset( sample=sample,
+ file_path=filepath,
+ status=sample.transfer_status.NOT_STARTED,
+ name=self.__dataset_name( sample, filepath.split( '/' )[-1] ),
+ error_msg='',
+ size=sample.dataset_size( filepath ) )
trans.sa_session.add( sample_dataset )
trans.sa_session.flush()
- files.append(str(sample_dataset.name))
+ files.append( str( sample_dataset.name ) )
return files
-
-
- def __dataset_name(self, sample, filepath):
- name = filepath.split('/')[-1]
- opt = sample.request.type.datatx_info.get('rename_dataset', sample.request.type.rename_dataset_options.NO)
- if opt == sample.request.type.rename_dataset_options.NO:
+ def __dataset_name( self, sample, filepath ):
+ name = filepath.split( '/' )[-1]
+ options = sample.request.type.rename_dataset_options
+ option = sample.request.type.datatx_info.get( 'rename_dataset', options.NO )
+ if option == options.NO:
return name
- elif opt == sample.request.type.rename_dataset_options.SAMPLE_NAME:
- return sample.name+'_'+name
- elif opt == sample.request.type.rename_dataset_options.EXPERIMENT_AND_SAMPLE_NAME:
- return sample.request.name+'_'+sample.name+'_'+name
- elif opt == sample.request.type.rename_dataset_options.EXPERIMENT_NAME:
- return sample.request.name+'_'+name
- def __setup_datatx_user(self, trans, library, folder):
- '''
- This method sets up the datatx user:
- - Checks if the user exists already, if not creates the user
+ elif option == options.SAMPLE_NAME:
+ return sample.name + '_' + name
+ elif option == options.EXPERIMENT_AND_SAMPLE_NAME:
+ return sample.request.name + '_' + sample.name + '_' + name
+ elif opt == options.EXPERIMENT_NAME:
+ return sample.request.name + '_' + name
+ def __setup_datatx_user( self, trans, library, folder ):
+ """
+ Sets up the datatx user:
+ - Checks if the user exists, if not creates them.
- Checks if the user had ADD_LIBRARY permission on the target library
and the target folder, if not sets up the permissions.
- '''
+ """
# Retrieve the upload user login information from the config file
config = ConfigParser.ConfigParser()
- config.read('transfer_datasets.ini')
- email = config.get("data_transfer_user_login_info", "email")
- password = config.get("data_transfer_user_login_info", "password")
+ config.read( 'transfer_datasets.ini' )
+ email = config.get( "data_transfer_user_login_info", "email" )
+ password = config.get( "data_transfer_user_login_info", "password" )
# check if the user already exists
- datatx_user = trans.sa_session.query( trans.app.model.User ) \
- .filter( trans.app.model.User.table.c.email==email ) \
+ datatx_user = trans.sa_session.query( trans.model.User ) \
+ .filter( trans.model.User.table.c.email==email ) \
.first()
if not datatx_user:
# if not create the user
- datatx_user = trans.app.model.User( email=email )
- datatx_user.set_password_cleartext( password )
+ datatx_user = trans.model.User( email=email, password=passsword )
if trans.app.config.use_remote_user:
datatx_user.external = True
trans.sa_session.add( datatx_user )
trans.sa_session.flush()
trans.app.security_agent.create_private_user_role( datatx_user )
trans.app.security_agent.user_set_default_permissions( datatx_user, history=False, dataset=False )
- for role in datatx_user.all_roles():
- if role.name == datatx_user.email and role.description == 'Private Role for %s' % datatx_user.email:
- datatx_user_private_role = role
- break
- def check_permission(item_actions, role):
- for item_permission in item_actions:
- if item_permission.action == trans.app.security_agent.permitted_actions.LIBRARY_ADD.action \
- and item_permission.role.id == role.id:
- return True
- return False
- # check if this user has 'add' permissions on the target library & folder
- # if not, set 'ADD' permission
- if not check_permission(library.actions, datatx_user_private_role):
- lp = trans.app.model.LibraryPermissions( trans.app.security_agent.permitted_actions.LIBRARY_ADD.action,
- library,
- datatx_user_private_role )
+ datatx_user_roles = datatx_user.all_roles()
+ datatx_user_private_role = trans.app.security_agent.get_private_user_role( datatx_user )
+ # Make sure this user has LIBRARY_ADD permissions on the target library and folder.
+ # If not, give them permission.
+ if not trans.app.security_agent.can_add_library_item( datatx_user_roles, library ):
+ lp = trans.model.LibraryPermissions( trans.app.security_agent.permitted_actions.LIBRARY_ADD.action,
+ library,
+ datatx_user_private_role )
trans.sa_session.add( lp )
- if not check_permission(folder.actions, datatx_user_private_role):
- dp = trans.app.model.LibraryFolderPermissions( trans.app.security_agent.permitted_actions.LIBRARY_ADD.action,
- folder,
- datatx_user_private_role )
+ if not trans.app.security_agent.can_add_library_item( datatx_user_roles, folder ):
+ lfp = trans.model.LibraryFolderPermissions( trans.app.security_agent.permitted_actions.LIBRARY_ADD.action,
+ folder,
+ datatx_user_private_role )
trans.sa_session.add( dp )
trans.sa_session.flush()
return datatx_user
-
- def __send_message(self, trans, datatx_info, sample, id_list):
- '''
- This method creates the xml message and sends it to the rabbitmq server
- '''
- # first create the xml message based on the following template
+ def __send_message( self, trans, datatx_info, sample, id_list ):
+ """Ceates an xml message and sends it to the rabbitmq server"""
+ # Create the xml message based on the following template
xml = \
''' <data_transfer><data_host>%(DATA_HOST)s</data_host>
@@ -792,73 +584,62 @@ class RequestsAdmin( BaseController, Use
</dataset>'''
datasets = ''
for id in id_list:
- sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id(id) )
+ sample_dataset = trans.sa_session.query( trans.model.SampleDataset ).get( trans.security.decode_id( id ) )
if sample_dataset.status == sample.transfer_status.NOT_STARTED:
- datasets = datasets + dataset_xml % dict(ID=str(sample_dataset.id),
- NAME=sample_dataset.name,
- FILE=sample_dataset.file_path)
+ datasets = datasets + dataset_xml % dict( ID=str( sample_dataset.id ),
+ NAME=sample_dataset.name,
+ FILE=sample_dataset.file_path )
sample_dataset.status = sample.transfer_status.IN_QUEUE
trans.sa_session.add( sample_dataset )
trans.sa_session.flush()
- data = xml % dict(DATA_HOST=datatx_info['host'],
- DATA_USER=datatx_info['username'],
- DATA_PASSWORD=datatx_info['password'],
- SAMPLE_ID=str(sample.id),
- LIBRARY_ID=str(sample.library.id),
- FOLDER_ID=str(sample.folder.id),
- DATASETS=datasets)
- # now send this message
- conn = amqp.Connection(host=trans.app.config.amqp['host']+":"+trans.app.config.amqp['port'],
- userid=trans.app.config.amqp['userid'],
- password=trans.app.config.amqp['password'],
- virtual_host=trans.app.config.amqp['virtual_host'],
- insist=False)
+ data = xml % dict( DATA_HOST=datatx_info['host'],
+ DATA_USER=datatx_info['username'],
+ DATA_PASSWORD=datatx_info['password'],
+ SAMPLE_ID=str(sample.id),
+ LIBRARY_ID=str(sample.library.id),
+ FOLDER_ID=str(sample.folder.id),
+ DATASETS=datasets )
+ # Send the message
+ conn = amqp.Connection( host=trans.app.config.amqp['host'] + ":" + trans.app.config.amqp['port'],
+ userid=trans.app.config.amqp['userid'],
+ password=trans.app.config.amqp['password'],
+ virtual_host=trans.app.config.amqp['virtual_host'],
+ insist=False )
chan = conn.channel()
- msg = amqp.Message(data.replace('\n', '').replace('\r', ''),
- content_type='text/plain',
- application_headers={'msg_type': 'data_transfer'})
+ msg = amqp.Message( data.replace( '\n', '' ).replace( '\r', '' ),
+ content_type='text/plain',
+ application_headers={'msg_type': 'data_transfer'} )
msg.properties["delivery_mode"] = 2
- chan.basic_publish(msg,
- exchange=trans.app.config.amqp['exchange'],
- routing_key=trans.app.config.amqp['routing_key'])
+ chan.basic_publish( msg,
+ exchange=trans.app.config.amqp['exchange'],
+ routing_key=trans.app.config.amqp['routing_key'] )
chan.close()
conn.close()
-
- def __start_datatx(self, trans, sample, id_list):
- # data transfer user
- datatx_user = self.__setup_datatx_user(trans, sample.library, sample.folder)
- # validate sequecer information
+ def __start_datatx( self, trans, sample, id_list ):
+ datatx_user = self.__setup_datatx_user( trans, sample.library, sample.folder )
+ # Validate sequencer information
datatx_info = sample.request.type.datatx_info
- if not datatx_info['host'] or \
- not datatx_info['username'] or \
- not datatx_info['password']:
- message = "Error in sequencer login information."
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='manage_datasets',
- sample_id=sample.id,
- status='error',
- message=message) )
- self.__send_message(trans, datatx_info, sample, id_list)
- message="%i dataset(s) have been queued for transfer from the sequencer. Click on <b>Refresh</b> button above to get the latest transfer status." % len(id_list)
+ if not datatx_info['host'] or not datatx_info['username'] or not datatx_info['password']:
+ message = "Error in sequencer login information."
+ status = "error"
+ else:
+ self.__send_message( trans, datatx_info, sample, id_list )
+ message = "%i datasets have been queued for transfer from the sequencer. Click on <b>Refresh</b> button above to get the latest transfer status." % len( id_list )
+ status = "done"
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
- sample_id=sample.id,
- status='done',
+ sample_id=trans.security.encode_id( sample.id ),
+ status=status,
message=message) )
-
-##
-#### Request Type Stuff ###################################################
-##
+ # Request Type Stuff
@web.expose
@web.require_admin
def manage_request_types( self, trans, **kwd ):
if 'operation' in kwd:
operation = kwd['operation'].lower()
- if not kwd.get( 'id', None ):
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='manage_request_types',
- status='error',
- message="Invalid requesttype ID") )
+ obj_id = kwd.get( 'id', None )
+ if obj_id is None:
+ return invalid_id_redirect( trans, 'requests_admin', obj_id, action='manage_request_types' )
if operation == "view":
return self.__view_request_type( trans, **kwd )
elif operation == "view_form":
@@ -873,30 +654,26 @@ class RequestsAdmin( BaseController, Use
return self.__show_request_type_permissions( trans, **kwd )
# Render the grid view
return self.requesttype_grid( trans, **kwd )
- def __view_request_type(self, trans, **kwd):
+ def __view_request_type( self, trans, **kwd ):
+ request_type_id = kwd.get( 'id', None )
try:
- rt = trans.sa_session.query( trans.app.model.RequestType ).get( trans.security.decode_id(kwd['id']) )
+ request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
except:
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='manage_request_types',
- status='error',
- message="Invalid requesttype ID") )
+ return invalid_id_redirect( trans, 'requests_admin', request_type_id, action='manage_request_types' )
+ forms = self.get_all_forms( trans )
+ rename_dataset_selectbox = self.__build_rename_dataset_select_list( trans, request_type )
return trans.fill_template( '/admin/requests/view_request_type.mako',
- request_type=rt,
- forms=self.get_all_forms( trans ),
- states_list=rt.states,
- rename_dataset_selectbox=self.__rename_dataset_selectbox(trans, rt) )
+ request_type=request_type,
+ forms=forms,
+ rename_dataset_selectbox=rename_dataset_selectbox )
def __view_form(self, trans, **kwd):
+ form_definition_id = kwd.get( 'id', None )
try:
- fd = trans.sa_session.query( trans.app.model.FormDefinition ).get( trans.security.decode_id(kwd['id']) )
+ form_definition = trans.sa_session.query( trans.model.FormDefinition ).get( trans.security.decode_id( form_definition_id ) )
except:
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='manage_request_types',
- status='error',
- message="Invalid form ID") )
+ return invalid_id_redirect( trans, 'requests_admin', form_definition_id, action='manage_request_types' )
return trans.fill_template( '/admin/forms/show_form_read_only.mako',
- form=fd )
-
+ form_definition=form_definition )
@web.expose
@web.require_admin
def create_request_type( self, trans, **kwd ):
@@ -904,221 +681,218 @@ class RequestsAdmin( BaseController, Use
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
if params.get( 'add_state_button', False ):
- rt_info, rt_states = self.__create_request_type_form(trans, **kwd)
- rt_states.append(("", ""))
+ rt_info_widgets, rt_states_widgets = self.__create_request_type_form( trans, **kwd )
+ rt_states_widgets.append( ( "", "" ) )
+ rename_dataset_selectbox = self.__build_rename_dataset_select_list( trans )
return trans.fill_template( '/admin/requests/create_request_type.mako',
- rt_info_widgets=rt_info,
- rt_states_widgets=rt_states,
+ rt_info_widgets=rt_info_widgets,
+ rt_states_widgets=rt_states_widgets,
+ rename_dataset_selectbox=rename_dataset_selectbox,
message=message,
- status=status,
- rename_dataset_selectbox=self.__rename_dataset_selectbox(trans))
+ status=status )
elif params.get( 'remove_state_button', False ):
- rt_info, rt_states = self.__create_request_type_form(trans, **kwd)
- index = int(params.get( 'remove_state_button', '' ).split(" ")[2])
- del rt_states[index-1]
+ rt_info_widgets, rt_states_widgets = self.__create_request_type_form( trans, **kwd )
+ index = int( params.get( 'remove_state_button', '' ).split(" ")[2] )
+ del rt_states_widgets[ index-1 ]
+ rename_dataset_selectbox = self.__build_rename_dataset_select_list( trans )
return trans.fill_template( '/admin/requests/create_request_type.mako',
- rt_info_widgets=rt_info,
- rt_states_widgets=rt_states,
+ rt_info_widgets=rt_info_widgets,
+ rt_states_widgets=rt_states_widgets,
+ rename_dataset_selectbox=rename_dataset_selectbox,
message=message,
- status=status,
- rename_dataset_selectbox=self.__rename_dataset_selectbox(trans))
+ status=status )
elif params.get( 'save_request_type', False ):
- rt, message = self.__save_request_type(trans, **kwd)
- if not rt:
+ request_type, message = self.__save_request_type( trans, **kwd )
+ if not request_type:
return trans.fill_template( '/admin/requests/create_request_type.mako',
- forms=self.get_all_forms( trans ),
message=message,
- status='error')
+ status='error' )
+ message = 'Sequencer configuration <b>%s</b> has been created' % request_type.name
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_request_types',
- message='Sequencer configuration <b>%s</b> has been created' % rt.name,
- status='done') )
+ message=message,
+ status=status ) )
elif params.get( 'save_changes', False ):
+ request_type_id = kwd.get( 'rt_id', None )
try:
- rt = trans.sa_session.query( trans.app.model.RequestType ).get( int(kwd['rt_id']) )
+ request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
except:
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='manage_request_types',
- message='Invalid sequencer configuration ID',
- status='error') )
- # data transfer info
- rt.datatx_info = dict(host=util.restore_text( params.get( 'host', '' ) ),
- username=util.restore_text( params.get( 'username', '' ) ),
- password=params.get( 'password', '' ),
- data_dir=util.restore_text( params.get( 'data_dir', '' ) ),
- rename_dataset=util.restore_text( params.get('rename_dataset', False) ))
- if rt.datatx_info.get('data_dir', '') and rt.datatx_info.get('data_dir', '')[-1] != os.sep:
- rt.datatx_info['data_dir'] = rt.datatx_info['data_dir']+os.sep
- trans.sa_session.add( rt )
+ return invalid_id_redirect( trans, 'requests_admin', request_type_id, action='manage_request_types' )
+ # Data transfer info - make sure password is retrieved from kwd rathe rthan Params since Params may have munged the characters.
+ request_type.datatx_info = dict( host=util.restore_text( params.get( 'host', '' ) ),
+ username=util.restore_text( params.get( 'username', '' ) ),
+ password=kwd.get( 'password', '' ),
+ data_dir=util.restore_text( params.get( 'data_dir', '' ) ),
+ rename_dataset=util.restore_text( params.get( 'rename_dataset', False ) ) )
+ data_dir = self.__check_path( request_type.datatx_info[ 'data_dir' ] )
+ request_type.datatx_info[ 'data_dir' ] = data_dir
+ trans.sa_session.add( request_type )
trans.sa_session.flush()
+ message = 'Changes made to sequencer configuration <b>%s</b> has been saved' % request_type.name
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_request_types',
operation='view',
- id=trans.security.encode_id(rt.id),
- message='Changes made to sequencer configuration <b>%s</b> has been saved' % rt.name,
- status='done') )
+ id=request_type_id,
+ message=message,
+ status=status ) )
else:
- rt_info, rt_states = self.__create_request_type_form(trans, **kwd)
+ rt_info_widgets, rt_states_widgets = self.__create_request_type_form( trans, **kwd )
+ rename_dataset_selectbox = self.__build_rename_dataset_select_list( trans )
return trans.fill_template( '/admin/requests/create_request_type.mako',
- rt_info_widgets=rt_info,
- rt_states_widgets=rt_states,
+ rt_info_widgets=rt_info_widgets,
+ rt_states_widgets=rt_states_widgets,
+ rename_dataset_selectbox=rename_dataset_selectbox,
message=message,
- status=status,
- rename_dataset_selectbox=self.__rename_dataset_selectbox(trans))
- def __create_request_type_form(self, trans, **kwd):
- request_forms=self.get_all_forms( trans,
- filter=dict(deleted=False),
- form_type=trans.app.model.FormDefinition.types.REQUEST )
- sample_forms=self.get_all_forms( trans,
- filter=dict(deleted=False),
- form_type=trans.app.model.FormDefinition.types.SAMPLE )
- if not len(request_forms) or not len(sample_forms):
+ status=status )
+ def __create_request_type_form( self, trans, **kwd ):
+ request_forms = self.get_all_forms( trans,
+ filter=dict( deleted=False ),
+ form_type=trans.model.FormDefinition.types.REQUEST )
+ sample_forms = self.get_all_forms( trans,
+ filter=dict( deleted=False ),
+ form_type=trans.model.FormDefinition.types.SAMPLE )
+ if not request_forms or not sample_forms:
return [],[]
params = util.Params( kwd )
- rt_info = []
- rt_info.append(dict(label='Name',
- widget=TextField('name', 40, util.restore_text( params.get( 'name', '' ) ) ) ))
- rt_info.append(dict(label='Description',
- widget=TextField('desc', 40, util.restore_text( params.get( 'desc', '' ) ) ) ))
-
- rf_selectbox = SelectField('request_form_id')
+ rt_info_widgets = []
+ rt_info_widgets.append( dict( label='Name',
+ widget=TextField( 'name', 40, util.restore_text( params.get( 'name', '' ) ) ) ) )
+ rt_info_widgets.append( dict( label='Description',
+ widget=TextField( 'desc', 40, util.restore_text( params.get( 'desc', '' ) ) ) ) )
+ rf_selectbox = SelectField( 'request_form_id' )
for fd in request_forms:
- if str(fd.id) == params.get( 'request_form_id', '' ):
- rf_selectbox.add_option(fd.name, fd.id, selected=True)
+ if str( fd.id ) == params.get( 'request_form_id', '' ):
+ rf_selectbox.add_option( fd.name, fd.id, selected=True )
else:
- rf_selectbox.add_option(fd.name, fd.id)
- rt_info.append(dict(label='Request form',
- widget=rf_selectbox ))
-
- sf_selectbox = SelectField('sample_form_id')
+ rf_selectbox.add_option( fd.name, fd.id )
+ rt_info_widgets.append( dict( label='Request form',
+ widget=rf_selectbox ) )
+ sf_selectbox = SelectField( 'sample_form_id' )
for fd in sample_forms:
- if str(fd.id) == params.get( 'sample_form_id', '' ):
- sf_selectbox.add_option(fd.name, fd.id, selected=True)
+ if str( fd.id ) == params.get( 'sample_form_id', '' ):
+ sf_selectbox.add_option( fd.name, fd.id, selected=True )
else:
- sf_selectbox.add_option(fd.name, fd.id)
- rt_info.append(dict(label='Sample form',
- widget=sf_selectbox ))
- # possible sample states
+ sf_selectbox.add_option( fd.name, fd.id )
+ rt_info_widgets.append( dict( label='Sample form',
+ widget=sf_selectbox ) )
+ # Possible sample states
rt_states = []
i=0
while True:
if kwd.has_key( 'state_name_%i' % i ):
- rt_states.append((params.get( 'state_name_%i' % i, '' ),
- params.get( 'state_desc_%i' % i, '' )))
- i=i+1
+ rt_states.append( ( params.get( 'state_name_%i' % i, '' ),
+ params.get( 'state_desc_%i' % i, '' ) ) )
+ i += 1
else:
break
- return rt_info, rt_states
-
- def __rename_dataset_selectbox(self, trans, rt=None):
+ return rt_info_widgets, rt_states
+ def __build_rename_dataset_select_list( self, trans, rt=None ):
if rt:
- sel_opt = rt.datatx_info.get('rename_dataset', trans.app.model.RequestType.rename_dataset_options.NO)
+ sel_opt = rt.datatx_info.get( 'rename_dataset', trans.model.RequestType.rename_dataset_options.NO )
else:
- sel_opt = trans.app.model.RequestType.rename_dataset_options.NO
- rename_dataset_selectbox = SelectField('rename_dataset')
- for opt, opt_name in trans.app.model.RequestType.rename_dataset_options.items():
+ sel_opt = trans.model.RequestType.rename_dataset_options.NO
+ rename_dataset_selectbox = SelectField( 'rename_dataset' )
+ for opt, opt_name in trans.model.RequestType.rename_dataset_options.items():
if sel_opt == opt_name:
- rename_dataset_selectbox.add_option(opt_name, opt_name, selected=True)
+ rename_dataset_selectbox.add_option( opt_name, opt_name, selected=True )
else:
- rename_dataset_selectbox.add_option(opt_name, opt_name)
+ rename_dataset_selectbox.add_option( opt_name, opt_name )
return rename_dataset_selectbox
-
def __save_request_type(self, trans, **kwd):
params = util.Params( kwd )
- rt = trans.app.model.RequestType()
- rt.name = util.restore_text( params.get( 'name', '' ) )
- rt.desc = util.restore_text( params.get( 'desc', '' ) )
- rt.request_form = trans.sa_session.query( trans.app.model.FormDefinition ).get( int( params.request_form_id ) )
- rt.sample_form = trans.sa_session.query( trans.app.model.FormDefinition ).get( int( params.sample_form_id ) )
- # data transfer info
- rt.datatx_info = dict(host=util.restore_text( params.get( 'host', '' ) ),
- username=util.restore_text( params.get( 'username', '' ) ),
- password=params.get( 'password', '' ),
- data_dir=util.restore_text( params.get( 'data_dir', '' ) ),
- rename_dataset=util.restore_text( params.get('rename_dataset', '') ))
- if rt.datatx_info.get('data_dir', '') and rt.datatx_info.get('data_dir', '')[-1] != os.sep:
- rt.datatx_info['data_dir'] = rt.datatx_info['data_dir']+os.sep
- trans.sa_session.add( rt )
+ name = util.restore_text( params.get( 'name', '' ) )
+ desc = util.restore_text( params.get( 'desc', '' ) )
+ request_form_id = params.get( 'request_form_id', None )
+ request_form = trans.sa_session.query( trans.model.FormDefinition ).get( int( request_form_id ) )
+ sample_form_id = params.get( 'sample_form_id', None )
+ sample_form = trans.sa_session.query( trans.model.FormDefinition ).get( int( sample_form_id ) )
+ data_dir = util.restore_text( params.get( 'data_dir', '' ) )
+ data_dir = self.__check_path( data_dir )
+ # Data transfer info - Make sure password is retrieved from kwd rather than Params
+ # since Params may have munged the characters.
+ datatx_info = dict( host=util.restore_text( params.get( 'host', '' ) ),
+ username=util.restore_text( params.get( 'username', '' ) ),
+ password=kwd.get( 'password', '' ),
+ data_dir=data_dir,
+ rename_dataset=util.restore_text( params.get( 'rename_dataset', '' ) ) )
+ request_type = trans.model.RequestType( name=name, desc=desc, request_form=request_form, sample_form=sample_form, datatx_info=datatx_info )
+ trans.sa_session.add( request_type )
trans.sa_session.flush()
# set sample states
- ss_list = trans.sa_session.query( trans.app.model.SampleState ).filter( trans.app.model.SampleState.table.c.request_type_id == rt.id )
+ ss_list = trans.sa_session.query( trans.model.SampleState ) \
+ .filter( trans.model.SampleState.table.c.request_type_id == request_type.id )
for ss in ss_list:
trans.sa_session.delete( ss )
trans.sa_session.flush()
- i=0
+ i = 0
while True:
if kwd.has_key( 'state_name_%i' % i ):
- name = util.restore_text( params.get( 'state_name_%i' % i, None ))
- desc = util.restore_text( params.get( 'state_desc_%i' % i, None ))
- ss = trans.app.model.SampleState(name, desc, rt)
+ name = util.restore_text( params.get( 'state_name_%i' % i, None ) )
+ desc = util.restore_text( params.get( 'state_desc_%i' % i, None ) )
+ ss = trans.model.SampleState( name, desc, request_type )
trans.sa_session.add( ss )
trans.sa_session.flush()
i = i + 1
else:
break
- message = "The new sequencer configuration named '%s' with %s state(s) has been created" % (rt.name, i)
- return rt, message
+ message = "The new sequencer configuration named '%s' with %s states has been created" % ( request_type.name, i )
+ return request_type, message
def __delete_request_type( self, trans, **kwd ):
- id_list = util.listify( kwd['id'] )
- for id in id_list:
+ rt_id = kwd.get( 'id', '' )
+ rt_id_list = util.listify( rt_id )
+ for rt_id in rt_id_list:
try:
- rt = trans.sa_session.query( trans.app.model.RequestType ).get( trans.security.decode_id(id) )
+ request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( rt_id ) )
except:
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='manage_request_types',
- message='Invalid sequencer configuration ID',
- status='error') )
- rt.deleted = True
- trans.sa_session.add( rt )
+ return invalid_id_redirect( trans, 'requests_admin', rt_id, action='manage_request_types' )
+ request_type.deleted = True
+ trans.sa_session.add( request_type )
trans.sa_session.flush()
+ status = 'done'
+ message = '%i sequencer configurations has been deleted' % len( rt_id_list )
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_request_types',
- message='%i sequencer configuration(s) has been deleted' % len(id_list),
- status='done') )
+ message=message,
+ status='done' ) )
def __undelete_request_type( self, trans, **kwd ):
- id_list = util.listify( kwd['id'] )
- for id in id_list:
+ rt_id = kwd.get( 'id', '' )
+ rt_id_list = util.listify( rt_id )
+ for rt_id in rt_id_list:
try:
- rt = trans.sa_session.query( trans.app.model.RequestType ).get( trans.security.decode_id(id) )
+ request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( rt_id ) )
except:
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='manage_request_types',
- message='Invalid sequencer configuration ID',
- status='error') )
- rt.deleted = False
- trans.sa_session.add( rt )
+ return invalid_id_redirect( trans, 'requests_admin', rt_id, action='manage_request_types' )
+ request_type.deleted = False
+ trans.sa_session.add( request_type )
trans.sa_session.flush()
+ status = 'done'
+ message = '%i sequencer configurations have been undeleted' % len( rt_id_list )
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_request_types',
- message='%i sequencer configuration(s) has been undeleted' % len(id_list),
- status='done') )
- def __show_request_type_permissions(self, trans, **kwd ):
+ message=message,
+ status=status ) )
+ def __show_request_type_permissions( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
+ request_type_id = kwd.get( 'id', '' )
try:
- rt = trans.sa_session.query( trans.app.model.RequestType ).get( trans.security.decode_id(kwd['id']) )
+ request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
except:
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='manage_request_types',
- status='error',
- message="Invalid requesttype ID") )
- roles = trans.sa_session.query( trans.app.model.Role ) \
- .filter( trans.app.model.Role.table.c.deleted==False ) \
- .order_by( trans.app.model.Role.table.c.name )
+ return invalid_id_redirect( trans, 'requests_admin', request_type_id, action='manage_request_types' )
+ roles = trans.sa_session.query( trans.model.Role ) \
+ .filter( trans.model.Role.table.c.deleted==False ) \
+ .order_by( trans.model.Role.table.c.name )
if params.get( 'update_roles_button', False ):
permissions = {}
- for k, v in trans.app.model.RequestType.permitted_actions.items():
- in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ]
+ for k, v in trans.model.RequestType.permitted_actions.items():
+ in_roles = [ trans.sa_session.query( trans.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
- trans.app.security_agent.set_request_type_permissions( rt, permissions )
- trans.sa_session.refresh( rt )
- message = "Permissions updated for sequencer configuration '%s'" % rt.name
+ trans.app.security_agent.set_request_type_permissions( request_type, permissions )
+ trans.sa_session.refresh( request_type )
+ message = "Permissions updated for sequencer configuration '%s'" % request_type.name
return trans.fill_template( '/admin/requests/request_type_permissions.mako',
- request_type=rt,
+ request_type=request_type,
roles=roles,
status=status,
- message=message)
-
-
-
+ message=message )
--- a/templates/requests/index.mako
+++ b/templates/requests/index.mako
@@ -10,7 +10,5 @@
</%def><%def name="center_panel()">
-
- <iframe name="galaxy_main" id="galaxy_main" frameborder="0" style="position: absolute; width: 100%; height: 100%;" src="${h.url_for( controller="requests", action="list" )}"></iframe>
-
+ <iframe name="galaxy_main" id="galaxy_main" frameborder="0" style="position: absolute; width: 100%; height: 100%;" src="${h.url_for( controller="requests", action="browse_requests" )}"></iframe></%def>
--- a/templates/requests/common/events.mako
+++ b/templates/requests/common/events.mako
@@ -4,12 +4,10 @@
<h2>History of Sequencing Request "${request.name}"</h2><ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='show', id=trans.security.encode_id(request.id) )}">
- <span>Browse this request</span></a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a></li><li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='list')}">
- <span>Browse all requests</span></a>
+ <a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse all requests</a></li></ul>
--- a/templates/admin/requests/request_type_permissions.mako
+++ b/templates/admin/requests/request_type_permissions.mako
@@ -32,7 +32,7 @@
<div class="toolForm"><div class="toolFormTitle">Manage permissions on "${request_type.name}"</div><div class="toolFormBody">
- <form name="request_type_permissions" id="request_type_permissions" action="${h.url_for( controller='requests_admin', action='manage_request_types', operation="permissions", id=trans.security.encode_id(request_type.id))}" method="post">
+ <form name="request_type_permissions" id="request_type_permissions" action="${h.url_for( controller='requests_admin', action='manage_request_types', operation="permissions", id=trans.security.encode_id( request_type.id ) )}" method="post"><div class="form-row"><%
obj_name = request_type.name
--- a/lib/galaxy/webapps/community/controllers/tool.py
+++ b/lib/galaxy/webapps/community/controllers/tool.py
@@ -8,7 +8,7 @@ from common import *
log = logging.getLogger( __name__ )
-class StateColumn( grids.TextColumn ):
+class StateColumn( grids.StateColumn ):
def get_value( self, trans, grid, tool ):
state = tool.state
if state == trans.model.Tool.states.APPROVED:
--- a/templates/admin/requests/dataset.mako
+++ b/templates/admin/requests/dataset.mako
@@ -1,17 +1,17 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
-
%if message:
${render_msg( message, status )}
%endif
-<br/>
-<br/>
+<br/><br/>
+
+<% sample = sample_dataset.sample %><ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='show_datatx_page', cntrller='requests_admin', sample_id=trans.security.encode_id(sample.id) )}">
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller='requests_admin', sample_id=trans.security.encode_id( sample.id ) )}"><span>Browse datasets</span></a></li></ul>
--- /dev/null
+++ b/templates/requests/common/create_request.mako
@@ -0,0 +1,60 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${h.js("jquery.autocomplete", "autocomplete_tagging" )}
+</%def>
+
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "autocomplete_tagging" )}
+</%def>
+
+<br/><br/>
+<ul class="manage-table-actions">
+ <li><a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse requests</a></li>
+</ul>
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolForm">
+ <div class="toolFormTitle">Create a new request</div>
+ %if len( request_type_select_field.options ) == 1:
+ There are no sequencer configurations available for ${trans.user.email} to create sequencing requests.
+ %else:
+ <div class="toolFormBody">
+ <form name="create_request" id="create_request" action="${h.url_for( controller='requests_common', action='create_request', cntrller=cntrller )}" method="post" >
+ <div class="form-row">
+ <label>Select a sequencer configuration:</label>
+ ## The request_type_select_field is a SelectField named request_type_id
+ ${request_type_select_field.get_html()}
+ %if cntrller != 'requests_admin':
+ <div class="toolParamHelp" style="clear: both;">
+ Contact the lab manager if you are not sure about the sequencer configuration.
+ </div>
+ %endif
+ </div>
+ %if request_type_select_field_selected != 'none':
+ ## If a request_type has been selected, display the associated form using received widgets.
+ %for i, field in enumerate( widgets ):
+ <div class="form-row">
+ <label>${field['label']}</label>
+ ${field['widget'].get_html()}
+ <div class="toolParamHelp" style="clear: both;">
+ ${field['helptext']}
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ %endfor
+ <div class="form-row">
+ <input type="submit" name="create_request_button" value="Save"/>
+ <input type="submit" name="add_sample_button" value="Add samples"/>
+ </div>
+ %endif
+ </form>
+ </div>
+ %endif
+</div>
--- /dev/null
+++ b/templates/requests/common/manage_request.mako
@@ -0,0 +1,610 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+<%namespace file="/requests/common/sample_state.mako" import="render_sample_state" />
+<%namespace file="/requests/common/sample_datasets.mako" import="render_sample_datasets" />
+
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "library" )}
+</%def>
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ <script type="text/javascript">
+ function showContent(vThis)
+ {
+ // http://www.javascriptjunkie.com
+ // alert(vSibling.className + " " + vDef_Key);
+ vParent = vThis.parentNode;
+ vSibling = vParent.nextSibling;
+ while (vSibling.nodeType==3) {
+ // Fix for Mozilla/FireFox Empty Space becomes a TextNode or Something
+ vSibling = vSibling.nextSibling;
+ };
+ if(vSibling.style.display == "none")
+ {
+ vThis.src="/static/images/fugue/toggle.png";
+ vThis.alt = "Hide";
+ vSibling.style.display = "block";
+ } else {
+ vSibling.style.display = "none";
+ vThis.src="/static/images/fugue/toggle-expand.png";
+ vThis.alt = "Show";
+ }
+ return;
+ }
+
+ $(document).ready(function(){
+ //hide the all of the element with class msg_body
+ $(".msg_body").hide();
+ //toggle the component with class msg_body
+ $(".msg_head").click(function(){
+ $(this).next(".msg_body").slideToggle(0);
+ });
+ });
+
+ // Looks for changes in sample states using an async request. Keeps
+ // calling itself (via setTimeout) until all samples are in a terminal
+ // state.
+ var updater = function ( sample_states ) {
+ // Check if there are any items left to track
+ var empty = true;
+ for ( i in sample_states ) {
+ empty = false;
+ break;
+ }
+ if ( ! empty ) {
+ setTimeout( function() { updater_callback( sample_states ) }, 1000 );
+ }
+ };
+
+ var updater_callback = function ( sample_states ) {
+ // Build request data
+ var ids = []
+ var states = []
+ $.each( sample_states, function ( id, state ) {
+ ids.push( id );
+ states.push( state );
+ });
+ // Make ajax call
+ $.ajax( {
+ type: "POST",
+ url: "${h.url_for( controller='requests_common', action='sample_state_updates' )}",
+ dataType: "json",
+ data: { ids: ids.join( "," ), states: states.join( "," ) },
+ success : function ( data ) {
+ $.each( data, function( cntrller, id, val ) {
+ // 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 );
+ },
+ error: function() {
+ // Just retry, like the old method, should try to be smarter
+ updater( sample_states );
+ }
+ });
+ };
+
+ function checkAllFields()
+ {
+ var chkAll = document.getElementById('checkAll');
+ var checks = document.getElementsByTagName('input');
+ var boxLength = checks.length;
+ var allChecked = false;
+ var totalChecked = 0;
+ if ( chkAll.checked == true )
+ {
+ for ( i=0; i < boxLength; i++ )
+ {
+ if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
+ {
+ checks[i].checked = true;
+ }
+ }
+ }
+ else
+ {
+ for ( i=0; i < boxLength; i++ )
+ {
+ if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
+ {
+ checks[i].checked = false
+ }
+ }
+ }
+ }
+
+ function stopRKey(evt) {
+ var evt = (evt) ? evt : ((event) ? event : null);
+ var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
+ if ((evt.keyCode == 13) && (node.type=="text")) {return false;}
+ }
+ document.onkeypress = stopRKey
+ </script>
+</%def>
+
+<% is_admin = cntrller == 'requests_admin' and trans.user_is_admin() %>
+
+<div class="grid-header">
+ <h2>Sequencing Request "${request.name}"</h2>
+ <div class="toolParamHelp" style="clear: both;">
+ <b>Sequencer</b>: ${request.type.name}
+ %if is_admin:
+ | <b>User</b>: ${request.user.username}
+ %endif
+ %if request.is_submitted:
+ | <b>State</b>: <i>${request.state}</i>
+ %else:
+ | <b>State</b>: ${request.state}
+ %endif
+ </div>
+</div>
+
+<br/><br/>
+<ul class="manage-table-actions">
+ <li><a class="action-button" id="seqreq-${request.id}-popup" class="menubutton">Sequencing Request Actions</a></li>
+ <div popupmenu="seqreq-${request.id}-popup">
+ %if request.is_unsubmitted and request.samples:
+ <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit</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 ) )}">History</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</a>
+ %if is_admin:
+ %if request.is_submitted:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', show_page=True, request_id=request.id )}">Select datasets to transfer</a>
+ %endif
+ %endif
+ </div>
+ <li><a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse requests</a></li>
+</ul>
+
+%if request.has_samples_without_library_destinations:
+ ${render_msg( "Select a target data library and folder for all the samples before starting the sequence run", "warning" )}
+%endif
+
+%if request.is_rejected:
+ ${render_msg( "Reason for rejection: " + request.last_comment, "warning" )}
+%endif
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> Request Information</h4>
+<div style="display:none;">
+ <table class="grid" border="0">
+ <tbody>
+ <tr>
+ <td valign="top" width="50%">
+ <div class="form-row">
+ <label>Description:</label>
+ ${request.desc}
+ </div>
+ <div style="clear: both"></div>
+ %for index, rd in enumerate( request_widgets ):
+ <%
+ field_label = rd[ 'label' ]
+ field_value = rd[ 'value' ]
+ %>
+ <div class="form-row">
+ <label>${field_label}:</label>
+ %if field_label == 'State':
+ <a href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">${field_value}</a>
+ %else:
+ ${field_value}
+ %endif
+ </div>
+ <div style="clear: both"></div>
+ %endfor
+ </td>
+ <td valign="top" width="50%">
+ <div class="form-row">
+ <label>Date created:</label>
+ ${request.create_time}
+ </div>
+ <div class="form-row">
+ <label>Date updated:</label>
+ ${request.update_time}
+ </div>
+ <div class="form-row">
+ <label>Email notification recipients:</label>
+ <%
+ if request.notification:
+ emails = ', '.join( request.notification[ 'email' ] )
+ else:
+ emails = ''
+ %>
+ ${emails}
+ </div>
+ <div style="clear: both"></div>
+ <div class="form-row">
+ <label>Email notification on sample states:</label>
+ <%
+ if request.notification:
+ states = []
+ for ss in request.type.states:
+ if ss.id in request.notification[ 'sample_states' ]:
+ states.append( ss.name )
+ states = ', '.join( states )
+ else:
+ states = ''
+ %>
+ ${states}
+ </div>
+ <div style="clear: both"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+<br/>
+<form id="manage_request" name="manage_request" action="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ), managing_samples=managing_samples )}" method="post">
+ %if current_samples:
+ <% sample_operation_selected_value = sample_operation_select_field.get_selected( return_value=True ) %>
+ ## first render the basic info grid
+ ${render_basic_info_grid()}
+ %if not request.is_new and not managing_samples and len( sample_operation_select_field.options ) > 1:
+ <div class="form-row" style="background-color:#FAFAFA;">
+ For selected samples:
+ ${sample_operation_select_field.get_html()}
+ </div>
+ %if sample_operation_selected_value != 'none' and selected_samples:
+ <div class="form-row" style="background-color:#FAFAFA;">
+ %if sample_operation_selected_value == trans.model.Sample.bulk_operations.CHANGE_STATE:
+ ## sample_operation_selected_value == 'Change state'
+ <div class="form-row">
+ <label>Change current state</label>
+ ${sample_state_id_select_field.get_html()}
+ <label>Comments</label>
+ <input type="text" name="sample_event_comment" value=""/>
+ <div class="toolParamHelp" style="clear: both;">
+ Optional
+ </div>
+ </div>
+ <div class="form-row">
+ <input type="submit" name="change_state_button" value="Save"/>
+ <input type="submit" name="cancel_change_state_button" value="Cancel"/>
+ </div>
+ %elif sample_operation_selected_value == trans.app.model.Sample.bulk_operations.SELECT_LIBRARY:
+ ## sample_operation_selected_value == 'Select data library and folder'
+ <% libraries_selected_value = libraries_select_field.get_selected( return_value=True ) %>
+ <div class="form-row">
+ <label>Select data library:</label>
+ ${libraries_select_field.get_html()}
+ </div>
+ %if libraries_selected_value != 'none':
+ <div class="form-row">
+ <label>Select folder:</label>
+ ${folders_select_field.get_html()}
+ </div>
+ <div class="form-row">
+ <input type="submit" name="change_lib_button" value="Save"/>
+ <input type="submit" name="cancel_change_lib_button" value="Cancel"/>
+ </div>
+ %endif
+ %endif
+ </div>
+ %endif
+ %endif
+ ## 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_grid( grid_index, grid_name, request.type.sample_form.fields_of_grid( grid_index ) )}
+ %endfor
+ %else:
+ <label>There are no samples.</label>
+ %endif
+ %if request.samples and request.is_submitted:
+ <script type="text/javascript">
+ // Updater
+ updater({${ ",".join( [ '"%s" : "%s"' % ( trans.security.encode_id( s.id ), s.state.name ) for s in request.samples ] ) }});
+ </script>
+ %endif
+ %if not managing_samples:
+ <table class="grid">
+ <tbody>
+ <tr>
+ <div class="form-row">
+ %if request.is_unsubmitted:
+ <td>
+ %if current_samples:
+ <label>Copy </label>
+ <input type="integer" name="num_sample_to_copy" value="1" size="3"/>
+ <label>samples from sample</label>
+ ${sample_copy.get_html()}
+ %endif
+ <input type="submit" name="add_sample_button" value="Add New"/>
+ </td>
+ %endif
+ <td>
+ %if current_samples and len( current_samples ) <= len( request.samples ):
+ <input type="submit" name="edit_samples_button" value="Edit samples"/>
+ %endif
+ </td>
+ </div>
+ </tr>
+ </tbody>
+ </table>
+ %endif
+ %if request.samples or current_samples:
+ %if managing_samples:
+ <div class="form-row">
+ <input type="submit" name="save_samples_button" value="Save"/>
+ <input type="submit" name="cancel_changes_button" value="Cancel"/>
+ </div>
+ %elif len( current_samples ) > len( request.samples ):
+ <div class="form-row">
+ <input type="submit" name="save_samples_button" value="Save"/>
+ <input type="submit" name="cancel_changes_button" value="Cancel"/>
+ </div>
+ %endif
+ %endif
+</form>
+<br/>
+%if request.is_unsubmitted:
+ <form id="import" name="import" action="${h.url_for( controller='requests_common', action='manage_request', managing_samples=managing_samples, id=trans.security.encode_id( request.id ) )}" enctype="multipart/form-data" method="post" >
+ <h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> Import samples</h4>
+ <div style="display:none;">
+ <input type="file" name="file_data" />
+ <input type="submit" name="import_samples_button" value="Import samples"/>
+ <br/>
+ <div class="toolParamHelp" style="clear: both;">
+ The csv file must be in the following format:<br/>
+ SampleName,DataLibrary,DataLibraryFolder,FieldValue1,FieldValue2...
+ </div>
+ </div>
+ </form>
+%endif
+
+<%def name="render_grid( grid_index, grid_name, fields_dict )">
+ <br/>
+ <% if not grid_name:
+ grid_name = "Grid "+ grid_index
+ %>
+ <div>
+ %if managing_samples or len( current_samples ) > len( request.samples ):
+ <h4><img src="/static/images/fugue/toggle.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
+ <div>
+ %else:
+ <h4><img src="/static/images/fugue/toggle-expand.png" alt="Hide" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
+ <div style="display:none;">
+ %endif
+ <table class="grid">
+ <thead>
+ <tr>
+ <th>Name</th>
+ %for index, field in fields_dict.items():
+ <th>
+ ${field['label']}
+ <div class="toolParamHelp" style="clear: both;">
+ <i>${field['helptext']}</i>
+ </div>
+ </th>
+ %endfor
+ <th></th>
+ </tr>
+ <thead>
+ <tbody>
+ <% trans.sa_session.refresh( request ) %>
+ %for sample_index, sample in enumerate( current_samples ):
+ %if managing_samples:
+ <tr>${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict)}</tr>
+ %else:
+ <tr>
+ %if sample_index in range( len( request.samples ) ):
+ ${render_sample( sample_index, sample['name'], sample['field_values'], fields_dict )}
+ %else:
+ ${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict)}
+ %endif
+ </tr>
+ %endif
+ %endfor
+ </tbody>
+ </table>
+ </div>
+ </div>
+</%def>
+
+## This function displays the "Basic Information" grid
+<%def name="render_basic_info_grid()">
+ <h3>Sample Information</h3>
+ <table class="grid">
+ <thead>
+ <tr>
+ <th><input type="checkbox" id="checkAll" name=select_all_samples_checkbox value="true" onclick='checkAllFields(1);'><input type="hidden" name=select_all_samples_checkbox value="true"></th>
+ <th>Name</th>
+ <th>Barcode</th>
+ <th>State</th>
+ <th>Data Library</th>
+ <th>Folder</th>
+ %if request.is_submitted or request.is_complete:
+ <th>Datasets Transferred</th>
+ %endif
+ <th></th>
+ </tr>
+ <thead>
+ <tbody>
+ <% trans.sa_session.refresh( request ) %>
+ %for sample_index, info in enumerate( current_samples ):
+ <%
+ if sample_index in range( len(request.samples ) ):
+ sample = request.samples[sample_index]
+ else:
+ sample = None
+ %>
+ %if managing_samples:
+ <tr>${show_basic_info_form( sample_index, sample, info )}</tr>
+ %else:
+ <tr>
+ %if sample_index in range( len( request.samples ) ):
+ %if trans.security.encode_id( sample.id ) in selected_samples:
+ <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true" checked><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"></td>
+ %else:
+ <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true"><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"></td>
+ %endif
+ <td>${info['name']}</td>
+ <td>${info['barcode']}</td>
+ %if sample.request.is_unsubmitted:
+ <td>Unsubmitted</td>
+ %else:
+ <td id="sampleState-${sample.id}">${render_sample_state( cntrller, sample )}</td>
+ %endif
+ %if info['library']:
+ %if cntrller == 'requests':
+ <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
+ %elif is_admin:
+ <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library_admin', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
+ %endif
+ %else:
+ <td></td>
+ %endif
+ %if info['folder']:
+ <td>${info['folder'].name}</td>
+ %else:
+ <td></td>
+ %endif
+ %if request.is_submitted or request.is_complete:
+ <td id="sampleDatasets-${sample.id}">
+ ${render_sample_datasets( cntrller, sample )}
+ </td>
+ %endif
+ %else:
+ ${show_basic_info_form( sample_index, sample, info )}
+ %endif
+ %if request.is_unsubmitted or request.is_rejected:
+ <td>
+ %if sample:
+ %if sample.request.is_unsubmitted:
+ <a class="action-button" href="${h.url_for( controller='requests_common', cntrller=cntrller, action='delete_sample', request_id=trans.security.encode_id( request.id ), sample_id=sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a>
+ %endif
+ %endif
+ </td>
+ %endif
+ </tr>
+ %endif
+ %endfor
+ </tbody>
+ </table>
+</%def>
+
+<%def name="show_basic_info_form( sample_index, sample, info )">
+ <td></td>
+ <td>
+ <input type="text" name="sample_${sample_index}_name" value="${info['name']}" size="10"/>
+ <div class="toolParamHelp" style="clear: both;">
+ <i>${' (required)' }</i>
+ </div>
+ </td>
+ %if cntrller == 'requests':
+ %if sample:
+ %if sample.request.is_unsubmitted:
+ <td></td>
+ %else:
+ <td><input type="text" name="sample_${sample_index}_barcode" value="${info['barcode']}" size="10"/></td>
+ %endif
+ %else:
+ <td></td>
+ %endif
+ %elif is_admin:
+ %if sample:
+ %if sample.request.is_unsubmitted:
+ <td></td>
+ %else:
+ <td><input type="text" name="sample_${sample_index}_barcode" value="${info['barcode']}" size="10"/></td>
+ %endif
+ %else:
+ <td></td>
+ %endif
+ %endif
+ %if sample:
+ %if sample.request.is_unsubmitted:
+ <td>Unsubmitted</td>
+ %else:
+ <td><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
+ %endif
+ %else:
+ <td></td>
+ %endif
+ <td>${info['library_select_field'].get_html()}</td>
+ <td>${info['folder_select_field'].get_html()}</td>
+ %if request.is_submitted or request.is_complete:
+ <%
+ if sample:
+ label = str( len( sample.datasets ) )
+ else:
+ label = 'Add'
+ %>
+ <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
+ %endif
+</%def>
+
+<%def name="render_sample( index, sample_name, sample_values, fields_dict )">
+ <td>
+ ${sample_name}
+ </td>
+ %for field_index, field in fields_dict.items():
+ <td>
+ %if sample_values[field_index]:
+ %if field['type'] == 'WorkflowField':
+ %if str(sample_values[field_index]) != 'none':
+ <% workflow = trans.sa_session.query( trans.app.model.StoredWorkflow ).get( int(sample_values[field_index]) ) %>
+ <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name}</a>
+ %endif
+ %else:
+ ${sample_values[field_index]}
+ %endif
+ %else:
+ <i>None</i>
+ %endif
+ </td>
+ %endfor
+</%def>
+
+<%def name="render_sample_form( index, sample_name, sample_values, fields_dict )">
+ <td>${sample_name}</td>
+ %for field_index, field in fields_dict.items():
+ <td>
+ %if field['type'] == 'TextField':
+ <input type="text" name="sample_${index}_field_${field_index}" value="${sample_values[field_index]}" size="7"/>
+ %elif field['type'] == 'SelectField':
+ <select name="sample_${index}_field_${field_index}" last_selected_value="2">
+ %for option_index, option in enumerate(field['selectlist']):
+ %if option == sample_values[field_index]:
+ <option value="${option}" selected>${option}</option>
+ %else:
+ <option value="${option}">${option}</option>
+ %endif
+ %endfor
+ </select>
+ %elif field['type'] == 'WorkflowField':
+ <select name="sample_${index}_field_${field_index}">
+ %if str(sample_values[field_index]) == 'none':
+ <option value="none" selected>Select one</option>
+ %else:
+ <option value="none">Select one</option>
+ %endif
+ %for option_index, option in enumerate(request.user.stored_workflows):
+ %if not option.deleted:
+ %if str(option.id) == str(sample_values[field_index]):
+ <option value="${option.id}" selected>${option.name}</option>
+ %else:
+ <option value="${option.id}">${option.name}</option>
+ %endif
+ %endif
+ %endfor
+ </select>
+ %elif field['type'] == 'CheckboxField':
+ <input type="checkbox" name="sample_${index}_field_${field_index}" value="Yes"/>
+ %endif
+ <div class="toolParamHelp" style="clear: both;">
+ <i>${'('+field['required']+')' }</i>
+ </div>
+ </td>
+ %endfor
+</%def>
--- a/templates/requests/common/find.mako
+++ /dev/null
@@ -1,83 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-<%def name="javascripts()">
- ${parent.javascripts()}
- ${h.js("jquery.autocomplete", "autocomplete_tagging" )}
-</%def>
-
-<%def name="stylesheets()">
- ${parent.stylesheets()}
- ${h.css( "autocomplete_tagging" )}
-</%def>
-
-<br/>
-<br/>
-<ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller=cntrller, cntrller=cntrller, action='list')}">
- <span>Browse requests</span></a>
- </li>
-</ul>
-
-<div class="toolForm">
- <div class="toolFormTitle">Find samples</div>
- <div class="toolFormBody">
- <form name="find_request" id="find_request" action="${h.url_for( controller='requests_common', action='find', cntrller=cntrller)}" method="post" >
- <div class="form-row">
- <label>Find sample(s) using:</label>
- ${search_type.get_html()}
- <div class="toolParamHelp" style="clear: both;">
- Select a sample attribute to search through all the samples.<br/>
- To search for a sample with a dataset name, select the dataset
- option above. This will return all the sample(s) which has a
- dataset with the given name associated with it.
- </div>
- </div>
- <div class="form-row">
- <label>Show only sequencing requests in state:</label>
- ${request_states.get_html()}
- </div>
- <div class="form-row">
- ${search_box.get_html()}
- <input type="submit" name="go_button" value="Find"/>
- <div class="toolParamHelp" style="clear: both;">
- Wildcard search (%) can be used as placeholder for any sequence of characters or words.<br/>
- For example, to search for samples starting with 'mysample' use 'mysample%' as the search string.
- </div>
- </div>
- %if results:
- <div class="form-row">
- <label><i>${results}</i></label>
- %if samples:
- <div class="toolParamHelp" style="clear: both;">
- The search results are sorted by the date the samples where created.
- </div>
- %endif
- </div>
- %endif
- <div class="form-row">
- %if samples:
- %for s in samples:
- <div class="form-row">
- Sample: <b>${s.name}</b> | Barcode: ${s.bar_code}<br/>
- State: ${s.current_state().name}<br/>
- Datasets: <a href="${h.url_for(controller='requests_common', cntrller=cntrller, action='show_datatx_page', sample_id=trans.security.encode_id(s.id))}">${s.transferred_dataset_files()}/${len(s.datasets)}</a><br/>
- %if cntrller == 'requests_admin':
- <i>User: ${s.request.user.email}</i>
- %endif
- <div class="toolParamHelp" style="clear: both;">
- <a href="${h.url_for( controller=cntrller, action='list', operation='show', id=trans.security.encode_id(s.request.id))}">Sequencing request: ${s.request.name} | Type: ${s.request.type.name} | State: ${s.request.state()}</a>
- </div>
- </div>
- <br/>
- %endfor
- %endif
- </div>
- </form>
- </div>
-</div>
--- a/test/functional/test_forms_and_requests.py
+++ b/test/functional/test_forms_and_requests.py
@@ -181,7 +181,7 @@ class TestFormsAndRequests( TwillTestCas
# Make sure the request_type1 is not accessible by regular_user2 since regular_user2 does not have Role1.
self.logout()
self.login( email=regular_user2.email )
- self.visit_url( '%s/requests_common/new?cntrller=requests&select_request_type=True' % self.url )
+ self.visit_url( '%s/requests_common/create_request?cntrller=requests&request_type=True' % self.url )
try:
self.check_page_for_string( 'There are no sequencer configurations created for a new request.' )
raise AssertionError, 'The request_type %s is accessible by %s when it should be restricted' % ( request_type1.name, regular_user2.email )
@@ -205,11 +205,11 @@ class TestFormsAndRequests( TwillTestCas
name = 'Request One'
desc = 'Request One Description'
self.create_request( cntrller='requests',
- request_type_id=str( request_type1.id ),
+ request_type_id=self.security.encode_id( request_type1.id ),
name=name,
desc=desc,
field_value_tuples=field_value_tuples,
- strings_displayed=[ 'Add a new request',
+ strings_displayed=[ 'Create a new request',
test_field_name1,
test_field_name2,
test_field_name3 ],
@@ -235,19 +235,20 @@ class TestFormsAndRequests( TwillTestCas
strings_displayed=[ 'Sequencing Request "%s"' % request_one.name,
'There are no samples.' ],
strings_displayed_after_submit=strings_displayed_after_submit )
- def test_030_edit_request( self ):
- """Testing editing a sequence run request"""
+ def test_030_edit_basic_request_info( self ):
+ """Testing editing the basic information of a sequence run request"""
# logged in as regular_user1
fields = [ 'option2', str( user_address1.id ), 'field three value (edited)' ]
new_name=request_one.name + ' (Renamed)'
new_desc=request_one.desc + ' (Re-described)'
- self.edit_request( request_id=self.security.encode_id( request_one.id ),
- name=request_one.name,
- new_name=new_name,
- new_desc=new_desc,
- new_fields=fields,
- strings_displayed=[ 'Edit sequencing request "%s"' % request_one.name ],
- strings_displayed_after_submit=[ new_name, new_desc ] )
+ self.edit_basic_request_info( request_id=self.security.encode_id( request_one.id ),
+ cntrller='requests',
+ name=request_one.name,
+ new_name=new_name,
+ new_desc=new_desc,
+ new_fields=fields,
+ strings_displayed=[ 'Edit sequencing request "%s"' % request_one.name ],
+ strings_displayed_after_submit=[ new_name, new_desc ] )
refresh( request_one )
# check if the request is showing in the 'new' filter
self.check_request_grid( cntrller='requests',
@@ -259,7 +260,7 @@ class TestFormsAndRequests( TwillTestCas
self.submit_request( cntrller='requests',
request_id=self.security.encode_id( request_one.id ),
request_name=request_one.name,
- strings_displayed_after_submit=[ 'The request <b>%s</b> has been submitted.' % request_one.name ] )
+ strings_displayed_after_submit=[ 'The request has been submitted.' ] )
refresh( request_one )
# Make sure the request is showing in the 'submitted' filter
self.check_request_grid( cntrller='requests',
@@ -271,16 +272,18 @@ class TestFormsAndRequests( TwillTestCas
def test_040_request_lifecycle( self ):
"""Testing request life-cycle as it goes through all the states"""
# logged in as regular_user1
+ """
+ TODO: debug this test case...
self.logout()
self.login( email=admin_user.email )
self.check_request_grid( cntrller='requests_admin',
state=request_one.states.SUBMITTED,
strings_displayed=[ request_one.name ] )
- self.visit_url( "%s/requests_admin/list?operation=show&id=%s" % ( self.url, self.security.encode_id( request_one.id ) ))
+ self.visit_url( "%s/requests_common/manage_request?cntrller=requests&id=%s" % ( self.url, self.security.encode_id( request_one.id ) ) )
self.check_page_for_string( 'Sequencing Request "%s"' % request_one.name )
# Set bar codes for the samples
bar_codes = [ '1234567890', '0987654321' ]
- strings_displayed_after_submit=[ 'Changes made to the sample(s) are saved.' ]
+ strings_displayed_after_submit=[ 'Changes made to the samples are saved.' ]
for bar_code in bar_codes:
strings_displayed_after_submit.append( bar_code )
self.add_bar_codes( request_id=self.security.encode_id( request_one.id ),
@@ -293,14 +296,14 @@ class TestFormsAndRequests( TwillTestCas
self.change_sample_state( request_id=self.security.encode_id( request_one.id ),
request_name=request_one.name,
sample_name=sample.name,
- sample_id=sample.id,
- new_state_id=request_type1.states[1].id,
+ sample_id=self.security.encode_id( sample.id ),
+ new_sample_state_id=request_type1.states[1].id,
new_state_name=request_type1.states[1].name )
self.change_sample_state( request_id=self.security.encode_id( request_one.id ),
request_name=request_one.name,
sample_name=sample.name,
- sample_id=sample.id,
- new_state_id=request_type1.states[2].id,
+ sample_id=self.security.encode_id( sample.id ),
+ new_sample_state_id=request_type1.states[2].id,
new_state_name=request_type1.states[2].name )
refresh( request_one )
self.logout()
@@ -311,6 +314,7 @@ class TestFormsAndRequests( TwillTestCas
strings_displayed=[ request_one.name ] )
assert request_one.state is not request_one.states.COMPLETE, "The state of the request '%s' should be set to '%s'" \
% ( request_one.name, request_one.states.COMPLETE )
+ """
def test_045_admin_create_request_on_behalf_of_regular_user( self ):
"""Testing creating and submitting a request as an admin on behalf of a regular user"""
# Logged in as regular_user1
@@ -323,17 +327,16 @@ class TestFormsAndRequests( TwillTestCas
# is required for that field.
field_value_tuples = [ ( 'option2', False ), ( str( user_address1.id ), True ), ( 'field_2_value', False ) ]
self.create_request( cntrller='requests_admin',
- request_type_id=str( request_type1.id ),
- select_user_id=str( regular_user1.id ),
+ request_type_id=self.security.encode_id( request_type1.id ),
+ other_users_id=self.security.encode_id( regular_user1.id ),
name=name,
desc=desc,
- refresh='True',
field_value_tuples=field_value_tuples,
- strings_displayed=[ 'Add a new request',
+ strings_displayed=[ 'Create a new request',
test_field_name1,
test_field_name2,
test_field_name3 ],
- strings_displayed_after_submit=[ "The new request named <b>%s</b> has been created" % name ] )
+ strings_displayed_after_submit=[ "The request has been created" ] )
global request_two
request_two = get_request_by_name( name )
# Make sure the request is showing in the 'new' filter
@@ -363,7 +366,7 @@ class TestFormsAndRequests( TwillTestCas
self.submit_request( cntrller='requests_admin',
request_id=self.security.encode_id( request_two.id ),
request_name=request_two.name,
- strings_displayed_after_submit=[ 'The request <b>%s</b> has been submitted.' % request_two.name ] )
+ strings_displayed_after_submit=[ 'The request has been submitted.' ] )
refresh( request_two )
# Make sure the request is showing in the 'submitted' filter
self.check_request_grid( cntrller='requests_admin',
@@ -383,7 +386,7 @@ class TestFormsAndRequests( TwillTestCas
request_name=request_two.name,
comment="Rejection test comment",
strings_displayed=[ 'Reject Sequencing Request "%s"' % request_two.name ],
- strings_displayed_after_submit=[ 'Request <b>%s</b> has been rejected.' % request_two.name ] )
+ strings_displayed_after_submit=[ 'Request (%s) has been rejected.' % request_two.name ] )
refresh( request_two )
# Make sure the request is showing in the 'rejected' filter
self.check_request_grid( cntrller='requests_admin',
--- a/templates/requests/common/edit_request.mako
+++ /dev/null
@@ -1,97 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-<br/>
-<br/>
-<ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='show', id=trans.security.encode_id(request.id))}">
- <span>Browse this request</span></a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='list')}">
- <span>Browse all requests</span></a>
- </li>
-</ul>
-
-<div class="toolForm">
- <div class="toolFormTitle">Edit sequencing request "${request.name}"</div>
- <div class="toolFormBody">
- <form name="edit_request" id="edit_request" action="${h.url_for( controller='requests_common', cntrller=cntrller, action='edit', id=trans.security.encode_id(request.id))}" method="post" >
- %for i, field in enumerate(widgets):
- <div class="form-row">
- <label>${field['label']}</label>
- ${field['widget'].get_html()}
- %if field['label'] == 'Data library' and new_library:
- ${new_library.get_html()}
- %endif
- <div class="toolParamHelp" style="clear: both;">
- ${field['helptext']}
- </div>
- <div style="clear: both"></div>
- </div>
- %endfor
- <div class="form-row">
- <div style="float: left; width: 250px; margin-right: 10px;">
- <input type="hidden" name="refresh" value="true" size="40"/>
- </div>
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <input type="submit" name="save_changes_request_button" value="Save"/>
- </div>
- </form>
- </div>
- <div class="toolFormTitle">Email notification settings</div>
- <div class="toolFormBody">
- <form name="settings" id="settings" action="${h.url_for( controller='requests_common', cntrller=cntrller, action='email_settings', id=trans.security.encode_id(request.id))}" method="post" >
- <%
- email_user = ''
- email_additional = []
- for e in request.notification['email']:
- if e == request.user.email:
- email_user = 'checked'
- else:
- email_additional.append(e)
- emails = '\r\n'.join(email_additional)
-
- %>
-
- <div class="form-row">
- <label>Send to:</label>
- <input type="checkbox" name="email_user" value="true" ${email_user}>${request.user.email} (Sequencing request owner)<input type="hidden" name="email_user" value="true">
- </div>
- <div class="form-row">
- <label>Additional email addresses:</label>
- <textarea name="email_additional" rows="3" cols="40">${emails}</textarea>
- <div class="toolParamHelp" style="clear: both;">
- Enter one email address per line
- </div>
- </div>
- <div class="form-row">
- <label>Select sample state(s) to send email notification:</label>
- %for ss in request.type.states:
- <%
- email_state = ''
- if ss.id in request.notification['sample_states']:
- email_state = 'checked'
- %>
- <input type="checkbox" name=sample_state_${ss.id} value="true" ${email_state} >${ss.name}<input type="hidden" name=sample_state_${ss.id} value="true">
- <br/>
- %endfor
- <div class="toolParamHelp" style="clear: both;">
- Email notification would be sent when all the samples of this sequencing request are in the selected state(s).
- </div>
- </div>
-
- <div class="form-row">
- <input type="submit" name="save_button" value="Save"/>
- </div>
- </form>
- </div>
-
-</div>
--- a/templates/admin/forms/show_form_read_only.mako
+++ b/templates/admin/forms/show_form_read_only.mako
@@ -1,7 +1,6 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
-
%if message:
${render_msg( message, status )}
%endif
@@ -64,32 +63,32 @@
</%def><div class="toolForm">
- %if form.desc:
- <div class="toolFormTitle">${form.name} - <i> ${form.desc}</i> (${form.type})
- <a id="form-${form.id}-popup" class="popup-arrow" style="display: none;">▼</a>
- <div popupmenu="form-${form.id}-popup">
- <a class="action-button" href="${h.url_for( controller='forms', action='manage', operation='Edit', id=trans.security.encode_id(form.current.id) )}">Edit</a>
+ %if form_definition.desc:
+ <div class="toolFormTitle">${form_definition.name} - <i> ${form_definition.desc}</i> (${form_definition.type})
+ <a id="form_definition-${form_definition.id}-popup" class="popup-arrow" style="display: none;">▼</a>
+ <div popupmenu="form_definition-${form_definition.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='forms', action='manage', operation='Edit', id=trans.security.encode_id(form_definition.current.id) )}">Edit</a></div></div>
%else:
- <div class="toolFormTitle">${form.name} (${form.type})
- <a id="form-${form.id}-popup" class="popup-arrow" style="display: none;">▼</a>
- <div popupmenu="form-${form.id}-popup">
- <a class="action-button" href="${h.url_for( controller='forms', action='manage', operation='Edit', id=trans.security.encode_id(form.current.id) )}">Edit</a>
+ <div class="toolFormTitle">${form_definition.name} (${form_definition.type})
+ <a id="form_definition-${form_definition.id}-popup" class="popup-arrow" style="display: none;">▼</a>
+ <div popupmenu="form_definition-${form_definition.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='forms', action='manage', operation='Edit', id=trans.security.encode_id(form_definition.current.id) )}">Edit</a></div></div>
%endif
<form name="library" action="${h.url_for( controller='forms', action='manage' )}" method="post" >
- %if form.type == trans.app.model.FormDefinition.types.SAMPLE:
- %if not len(form.layout):
- ${render_grid( 0, '', form.fields_of_grid( None ) )}
+ %if form_definition.type == trans.app.model.FormDefinition.types.SAMPLE:
+ %if not len(form_definition.layout):
+ ${render_grid( 0, '', form_definition.fields_of_grid( None ) )}
%else:
- %for grid_index, grid_name in enumerate(form.layout):
- ${render_grid( grid_index, grid_name, form.fields_of_grid( grid_index ) )}
+ %for grid_index, grid_name in enumerate(form_definition.layout):
+ ${render_grid( grid_index, grid_name, form_definition.fields_of_grid( grid_index ) )}
%endfor
%endif
%else:
- %for index, field in enumerate(form.fields):
+ %for index, field in enumerate(form_definition.fields):
<div class="form-row"><label>${field['label']}</label>
%if field['helptext']:
--- a/templates/admin/requests/reject.mako
+++ b/templates/admin/requests/reject.mako
@@ -8,18 +8,16 @@
<h2>Reject Sequencing Request "${request.name}"</h2><ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='events', id=trans.security.encode_id(request.id) )}">
- <span>Events</span></a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">Events</a></li><li>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='show', id=trans.security.encode_id(request.id) )}">
- <span>Browse this request</span></a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">Browse this request</a></li></ul><div class="toolForm"><div class="toolFormTitle">Reject request</div>
- <form name="event" action="${h.url_for( controller='requests_admin', action='reject', id=trans.security.encode_id(request.id))}" method="post" >
+ <form name="event" action="${h.url_for( controller='requests_admin', action='reject', id=trans.security.encode_id( request.id ) )}" method="post" ><div class="form-row">
Rejecting this request will move the request state to <b>Rejected</b>.
</div>
--- a/templates/requests/common/new_request.mako
+++ /dev/null
@@ -1,70 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-<%def name="javascripts()">
- ${parent.javascripts()}
- ${h.js("jquery.autocomplete", "autocomplete_tagging" )}
-</%def>
-
-<%def name="stylesheets()">
- ${parent.stylesheets()}
- ${h.css( "autocomplete_tagging" )}
-</%def>
-
-<br/>
-<br/>
-<ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller=cntrller, cntrller=cntrller, action='list')}">
- <span>Browse requests</span></a>
- </li>
-</ul>
-
-<div class="toolForm">
- <div class="toolFormTitle">Add a new request</div>
- %if len(select_request_type.options) == 1:
- There are no sequencer configurations available to ${trans.user.email} to create sequencing requests.
- %else:
- <div class="toolFormBody">
- <form name="new_request" id="new_request" action="${h.url_for( controller='requests_common', action='new', cntrller=cntrller)}" method="post" >
- <div class="form-row">
- <label>Select a sequencer configuration:</label>
- ${select_request_type.get_html()}
- %if cntrller != 'requests_admin':
- <div class="toolParamHelp" style="clear: both;">
- Please contact the lab manager if you are <br/>
- not sure about the sequencer configuration.
- </div>
- %endif
- </div>
-
- %if select_request_type.get_selected( return_label=True, return_value=True ) != ('Select one', 'none'):
- %for i, field in enumerate(widgets):
- <div class="form-row">
- <label>${field['label']}</label>
- ${field['widget'].get_html()}
- <div class="toolParamHelp" style="clear: both;">
- ${field['helptext']}
- </div>
- <div style="clear: both"></div>
- </div>
- %endfor
- <div class="form-row">
- <input type="submit" name="create_request_button" value="Save"/>
- <input type="submit" name="create_request_samples_button" value="Add samples"/>
- </div>
- %endif
- <div class="form-row">
- <div style="float: left; width: 250px; margin-right: 10px;">
- <input type="hidden" name="refresh" value="true" size="40"/>
- </div>
- <div style="clear: both"></div>
- </div>
- </form>
- </div>
-</div>
-%endif
--- a/templates/requests/find_index.mako
+++ /dev/null
@@ -1,16 +0,0 @@
-<%inherit file="/webapps/galaxy/base_panels.mako"/>
-
-<%def name="init()">
-<%
- self.has_left_panel=False
- self.has_right_panel=False
- self.active_view="requests"
- self.message_box_visible=False
-%>
-</%def>
-
-<%def name="center_panel()">
-
- <iframe name="galaxy_main" id="galaxy_main" frameborder="0" style="position: absolute; width: 100%; height: 100%;" src="${h.url_for( controller="requests_common", action="find" )}"></iframe>
-
-</%def>
--- a/lib/galaxy/web/form_builder.py
+++ b/lib/galaxy/web/form_builder.py
@@ -515,3 +515,55 @@ def get_suite():
"""Get unittest suite for this module"""
import doctest, sys
return doctest.DocTestSuite( sys.modules[__name__] )
+
+# --------- Utility methods -----------------------------
+
+def build_select_field( trans, objs, label_attr, select_field_name, initial_value='none',
+ selected_value='none', refresh_on_change=False, multiple=False, display=None, size=None ):
+ """
+ Build a SelectField given a set of objects. The received params are:
+ - objs: the set of object used to populate the option list
+ - label_attr: the attribute of each obj (e.g., name, email, etc ) whose value is used to populate each option label. If the string
+ 'self' is passed as label_attr, each obj in objs is assumed to be a string, so the obj itself is used
+ - select_field_name: the name of the SelectField
+ - initial_value: the vlaue of the first option in the SelectField - allows for an option telling the user to select something
+ - selected_value: the value of the currently selected option
+ - refresh_on_change: True if the SelectField should perform a refresh_on_change
+ """
+ values = [ initial_value ]
+ for obj in objs:
+ if label_attr == 'self':
+ # Each obj is a string
+ values.append( obj )
+ else:
+ values.append( trans.security.encode_id( obj.id ) )
+ if refresh_on_change:
+ refresh_on_change_values = values
+ else:
+ refresh_on_change_values = []
+ select_field = SelectField( name=select_field_name,
+ multiple=multiple,
+ display=display,
+ refresh_on_change=refresh_on_change,
+ refresh_on_change_values=refresh_on_change_values,
+ size=size )
+ if display is None:
+ # only insert an initial "Select one" option if we are not displaying check boxes or radio buttons
+ if selected_value == initial_value:
+ select_field.add_option( 'Select one', initial_value, selected=True )
+ else:
+ select_field.add_option( 'Select one', initial_value )
+ for obj in objs:
+ if label_attr == 'self':
+ # Each obj is a string
+ if str( selected_value ) == str( obj ):
+ select_field.add_option( obj, obj, selected=True )
+ else:
+ select_field.add_option( obj, obj )
+ else:
+ label = getattr( obj, label_attr )
+ if str( selected_value ) == str( obj.id ) or str( selected_value ) == trans.security.encode_id( obj.id ):
+ select_field.add_option( label, trans.security.encode_id( obj.id ), selected=True )
+ else:
+ select_field.add_option( label, trans.security.encode_id( obj.id ) )
+ return select_field
--- a/templates/admin/requests/get_data.mako
+++ b/templates/admin/requests/get_data.mako
@@ -1,20 +1,16 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
-
<script type="text/javascript">
$(document).ready(function(){
//hide the all of the element with class msg_body
$(".msg_body").hide();
- //toggle the componenet with class msg_body
+ //toggle the component with class msg_body
$(".msg_head").click(function(){
$(this).next(".msg_body").slideToggle(450);
});
});
-
-
-
</script><script type="text/javascript">
@@ -30,7 +26,7 @@
type: "POST",
url: "${h.url_for( controller='requests_admin', action='get_file_details' )}",
dataType: "json",
- data: { id: request_id, folder_path: document.get_data.folder_path.value+selected_value },
+ data: { id: request_id, folder_path: document.get_data.folder_path.value + selected_value },
success : function ( data ) {
cell.html( '<label>'+data+'</label>' )
}
@@ -40,13 +36,11 @@
{
cell.html( '' )
}
-
-
}
</script><script type="text/javascript">
- function open_folder1(request_id, folder_path)
+ function open_folder1( request_id, folder_path )
{
var w = document.get_data.files_list.selectedIndex;
var selected_value = document.get_data.files_list.options[w].value;
@@ -79,14 +73,11 @@
}
</script>
-
<style type="text/css">
.msg_head {
padding: 0px 0px;
cursor: pointer;
}
-
-}
</style>
%if message:
@@ -96,23 +87,23 @@
<br/><ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_request_types', operation='view', id=trans.security.encode_id(request.type.id) )}">
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_request_types', operation='view', id=trans.security.encode_id( request.type.id ) )}"><span>Sequencer configuration "${request.type.name}"</span></a></li><li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='show', id=trans.security.encode_id(request.id) )}">
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}"><span>Browse this request</span></a></li></ul>
-<form name="get_data" id="get_data" action="${h.url_for( controller='requests_admin', cntrller=cntrller, action='get_data', request_id=request.id)}" method="post" >
+<form name="get_data" id="get_data" action="${h.url_for( controller='requests_admin', cntrller=cntrller, action='get_data', request_id=trans.security.encode_id( request.id )}" method="post" ><div class="toolForm"><div class="toolFormTitle">Select files for transfer</div><div class="form-row"><label>Sample:</label>
- ${samples_selectbox.get_html()}
+ ${sample_id_select_field.get_html()}
<div class="toolParamHelp" style="clear: both;">
- Select the sample with which you want to associate the dataset(s)
+ Select the sample with which you want to associate the datasets
</div><br/><label>Folder path on the sequencer:</label>
@@ -122,14 +113,11 @@
</div><div class="form-row"><select name="files_list" id="files_list" style="max-width: 60%; width: 98%; height: 150px; font-size: 100%;" ondblclick="open_folder1(${request.id}, '${folder_path}')" onChange="display_file_details(${request.id}, '${folder_path}')" multiple>
- %for index, f in enumerate(files):
+ %for index, f in enumerate( files ):
<option value="${f}">${f}</option>
%endfor
</select><br/>
- <div id="file_details" class="toolParamHelp" style="clear: both;">
-
- </div></div><div class="form-row"><input type="submit" name="select_show_datasets_button" value="Select & show datasets"/>
--- a/templates/admin/requests/rename_datasets.mako
+++ b/templates/admin/requests/rename_datasets.mako
@@ -10,16 +10,14 @@
<ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_datasets', sample_id=sample.id )}">
- <span>Browse datasets</span></a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_datasets', sample_id=trans.security.encode_id( sample.id ) )}">Browse datasets</a></li><li>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='show', id=trans.security.encode_id(sample.request.id) )}">
- <span>Browse this request</span></a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a></li></ul>
-<form name="rename_datasets" id="rename_datasets" action="${h.url_for( controller='requests_admin', action='rename_datasets', id_list=id_list, sample_id=trans.security.encode_id(sample.id))}" method="post" >
+<form name="rename_datasets" id="rename_datasets" action="${h.url_for( controller='requests_admin', action='rename_datasets', id_list=id_list, sample_id=trans.security.encode_id( sample.id ) )}" method="post" ><table class="grid"><thead><tr>
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -5,21 +5,19 @@ Naming: try to use class names that have
the relationship cardinalities are obvious (e.g. prefer Dataset to Data)
"""
-import os.path, os, errno, sys, codecs, operator
import galaxy.datatypes
from galaxy.util.bunch import Bunch
from galaxy import util
-import tempfile
import galaxy.datatypes.registry
from galaxy.datatypes.metadata import MetadataCollection
from galaxy.security import RBACAgent, get_permitted_actions
from galaxy.util.hash_util import *
from galaxy.web.form_builder import *
from galaxy.model.item_attrs import UsesAnnotations
-import logging, smtplib, socket
+from sqlalchemy.orm import object_session
+import os.path, os, errno, codecs, operator, smtplib, socket, pexpect, logging
+
log = logging.getLogger( __name__ )
-from sqlalchemy.orm import object_session
-import pexpect
datatypes_registry = galaxy.datatypes.registry.Registry() #Default Value Required for unit tests
@@ -41,7 +39,6 @@ class User( object ):
# Relationships
self.histories = []
self.credentials = []
-
def set_password_cleartext( self, cleartext ):
"""Set 'self.password' to the digest of 'cleartext'."""
self.password = new_secure_hash( text_type=cleartext )
@@ -55,8 +52,8 @@ class User( object ):
if role not in roles:
roles.append( role )
return roles
- def accessible_libraries(self, trans, actions):
- # get all permitted libraries for this user
+ def accessible_libraries( self, trans, actions ):
+ # Get all permitted libraries for this user
all_libraries = trans.sa_session.query( trans.app.model.Library ) \
.filter( trans.app.model.Library.table.c.deleted == False ) \
.order_by( trans.app.model.Library.name )
@@ -74,18 +71,19 @@ class User( object ):
if can_show:
libraries[ library ] = hidden_folder_ids
return libraries
- def accessible_request_types(self, trans):
- # get all permitted libraries for this user
- all_rt_list = trans.sa_session.query( trans.app.model.RequestType ) \
- .filter( trans.app.model.RequestType.table.c.deleted == False ) \
- .order_by( trans.app.model.RequestType.name )
- roles = self.all_roles()
- rt_list = []
- for rt in all_rt_list:
- for permission in rt.actions:
- if permission.role.id in [r.id for r in roles]:
- rt_list.append(rt)
- return list(set(rt_list))
+ def accessible_request_types( self, trans ):
+ active_request_types = trans.sa_session.query( trans.app.model.RequestType ) \
+ .filter( trans.app.model.RequestType.table.c.deleted == False ) \
+ .order_by( trans.app.model.RequestType.name )
+ # Filter active_request_types to those that can be accessed by this user
+ role_ids = [ r.id for r in self.all_roles() ]
+ accessible_request_types = set()
+ for request_type in active_request_types:
+ for permission in request_type.actions:
+ if permission.role.id in role_ids:
+ accessible_request_types.add( request_type )
+ accessible_request_types = [ request_type for request_type in accessible_request_types ]
+ return accessible_request_types
class Job( object ):
"""
@@ -1505,12 +1503,12 @@ class FormDefinition( object ):
params = util.Params( kwd )
widgets = []
for index, field in enumerate( self.fields ):
+ field_type = field[ 'type' ]
field_name = 'field_%i' % index
# determine the value of the field
if field_name in kwd:
- # the user had already filled out this field and the same form is re-rendered
- # due to some reason like required fields have been left out.
- if field[ 'type' ] == 'CheckboxField':
+ # The form was submitted via refresh_on_change
+ if field_type == 'CheckboxField':
value = CheckboxField.is_checked( params.get( field_name, False ) )
else:
value = util.restore_text( params.get( field_name, '' ) )
@@ -1521,7 +1519,7 @@ class FormDefinition( object ):
except:
# If there was an error getting the saved value, we'll still
# display the widget, but it will be empty.
- if field[ 'type' ] == 'CheckboxField':
+ if field_type == 'CheckboxField':
# Since we do not have contents, set checkbox value to False
value = False
else:
@@ -1529,21 +1527,21 @@ class FormDefinition( object ):
value = ''
else:
# if none of the above, then leave the field empty
- if field[ 'type' ] == 'CheckboxField':
+ if field_type == 'CheckboxField':
# Since we do not have contents, set checkbox value to False
value = False
else:
# Set other field types to the default value of the field
value = field.get('default', '')
- # create the field widget
- field_widget = eval( field[ 'type' ] )( field_name )
- if field[ 'type' ] == 'TextField':
+ # Create the field widget
+ field_widget = eval( field_type )( field_name )
+ if field_type == 'TextField':
field_widget.set_size( 40 )
field_widget.value = value
- elif field[ 'type' ] == 'TextArea':
+ elif field_type == 'TextArea':
field_widget.set_size( 3, 40 )
field_widget.value = value
- elif field['type'] == 'AddressField':
+ elif field_type == 'AddressField':
field_widget.user = user
field_widget.value = value
field_widget.params = params
@@ -1551,13 +1549,13 @@ class FormDefinition( object ):
field_widget.user = user
field_widget.value = value
field_widget.params = params
- elif field[ 'type' ] == 'SelectField':
+ elif field_type == 'SelectField':
for option in field[ 'selectlist' ]:
if option == value:
field_widget.add_option( option, option, selected=True )
else:
field_widget.add_option( option, option )
- elif field[ 'type' ] == 'CheckboxField':
+ elif field_type == 'CheckboxField':
field_widget.set_checked( value )
if field[ 'required' ] == 'required':
req = 'Required'
@@ -1586,8 +1584,7 @@ class Request( object ):
SUBMITTED = 'In Progress',
REJECTED = 'Rejected',
COMPLETE = 'Complete' )
- def __init__(self, name=None, desc=None, request_type=None, user=None,
- form_values=None, notification=None):
+ def __init__( self, name=None, desc=None, request_type=None, user=None, form_values=None, notification=None ):
self.name = name
self.desc = desc
self.type = request_type
@@ -1595,59 +1592,79 @@ class Request( object ):
self.user = user
self.notification = notification
self.samples_list = []
- def state(self):
+ @property
+ def state( self ):
+ latest_event = self.latest_event
+ if latest_event:
+ return latest_event.state
+ return None
+ @property
+ def latest_event( self ):
if self.events:
- return self.events[0].state
+ return self.events[0]
return None
- def common_state(self):
- '''
- This method returns the state of this request's sample when they are all
- in one common state. If not this returns None
- '''
+ @property
+ def samples_have_common_state( self ):
+ """
+ Returns the state of this request's samples when they are all
+ in one common state. Otherwise returns False.
+ """
+ state_for_comparison = self.samples[0].state
+ if state_for_comparison is None:
+ for s in self.samples:
+ if s.state is not None:
+ return False
for s in self.samples:
- if s.current_state().id != self.samples[0].current_state().id:
+ if s.state.id != state_for_comparison.id:
return False
- return self.samples[0].current_state()
- def last_comment(self):
- if self.events:
- if self.events[0].comment:
- return self.events[0].comment
- else:
- return ''
+ return state_for_comparison
+ @property
+ def last_comment( self ):
+ latest_event = self.latest_event
+ if latest_event:
+ if latest_event.comment:
+ return latest_event.comment
+ return ''
return 'No comment'
- def has_sample(self, sample_name):
+ def has_sample( self, sample_name ):
for s in self.samples:
if s.name == sample_name:
return s
return False
- def unsubmitted(self):
- return self.state() in [ self.states.REJECTED, self.states.NEW ]
- def rejected(self):
- return self.state() == self.states.REJECTED
- def submitted(self):
- return self.state() == self.states.SUBMITTED
- def new(self):
- return self.state() == self.states.NEW
- def complete(self):
- return self.state() == self.states.COMPLETE
- def sequence_run_ready(self):
+ @property
+ def is_unsubmitted( self ):
+ return self.state in [ self.states.REJECTED, self.states.NEW ]
+ @property
+ def is_rejected( self ):
+ return self.state == self.states.REJECTED
+ @property
+ def is_submitted( self ):
+ return self.state == self.states.SUBMITTED
+ @property
+ def is_new( self ):
+ return self.state == self.states.NEW
+ @property
+ def is_complete( self ):
+ return self.state == self.states.COMPLETE
+ @property
+ def has_samples_without_library_destinations( self ):
+ # Return all samples that are not associated with a library
samples = []
for s in self.samples:
if not s.library:
- samples.append(s.name)
+ samples.append( s.name )
return samples
- def send_email_notification(self, trans, common_state, final_state=False):
- # check if an email notification is configured to be sent when the samples
+ def send_email_notification( self, trans, common_state, final_state=False ):
+ # Check if an email notification is configured to be sent when the samples
# are in this state
- if common_state.id not in self.notification['sample_states']:
+ if self.notification and common_state.id not in self.notification[ 'sample_states' ]:
return
comments = ''
- # send email
- if self.notification['email'] and trans.app.config.smtp_server is not None:
- host = trans.request.host.split(':')[0]
- if host in ['localhost', '127.0.0.1']:
+ # Send email
+ if trans.app.config.smtp_server is not None and self.notification and self.notification[ 'email' ]:
+ host = trans.request.host.split( ':' )[0]
+ if host in [ 'localhost', '127.0.0.1' ]:
host = socket.getfqdn()
-
body = """
Galaxy Sample Tracking Notification
===================================
@@ -1662,39 +1679,39 @@ Number of samples: %(num_samples)
All samples in state: %(sample_state)s
"""
- values = dict(user=self.user.email,
- request_name=self.name,
- request_type=self.type.name,
- request_state=self.state(),
- num_samples=str(len(self.samples)),
- sample_state=common_state.name,
- create_time=self.create_time,
- submit_time=self.create_time)
+ values = dict( user=self.user.email,
+ request_name=self.name,
+ request_type=self.type.name,
+ request_state=self.state,
+ num_samples=str( len( self.samples ) ),
+ sample_state=common_state.name,
+ create_time=self.create_time,
+ submit_time=self.create_time )
body = body % values
# check if this is the final state of the samples
if final_state:
txt = "Sample Name -> Data Library/Folder\r\n"
for s in self.samples:
- txt = txt + "%s -> %s/%s\r\n" % (s.name, s.library.name, s.folder.name)
+ txt = txt + "%s -> %s/%s\r\n" % ( s.name, s.library.name, s.folder.name )
body = body + txt
to = self.notification['email']
frm = 'galaxy-no-reply@' + host
subject = "Galaxy Sample Tracking notification: '%s' sequencing request" % self.name
- message = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s" % (frm, ", ".join(to), subject, body)
+ message = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s" % ( frm, ", ".join( to ), subject, body )
try:
s = smtplib.SMTP()
- s.connect(trans.app.config.smtp_server)
- s.sendmail(frm, to, message)
+ s.connect( trans.app.config.smtp_server )
+ s.sendmail( frm, to, message )
s.quit()
- comments = "Email notification sent to %s." % ", ".join(to).strip().strip(',')
+ comments = "Email notification sent to %s." % ", ".join( to ).strip().strip( ',' )
except:
comments = "Email notification failed."
# update the request history with the email notification event
elif not trans.app.config.smtp_server:
comments = "Email notification failed as SMTP server not set in config file"
if comments:
- event = trans.app.model.RequestEvent(self, self.state(), comments)
- trans.sa_session.add(event)
+ event = trans.app.model.RequestEvent( self, self.state, comments )
+ trans.sa_session.add( event )
trans.sa_session.flush()
return comments
@@ -1710,37 +1727,16 @@ class RequestType( object ):
EXPERIMENT_NAME = 'Prepend experiment name',
EXPERIMENT_AND_SAMPLE_NAME = 'Prepend experiment and sample name')
permitted_actions = get_permitted_actions( filter='REQUEST_TYPE' )
- def __init__(self, name=None, desc=None, request_form=None, sample_form=None,
- datatx_info=None):
+ def __init__( self, name=None, desc=None, request_form=None, sample_form=None, datatx_info=None ):
self.name = name
self.desc = desc
self.request_form = request_form
self.sample_form = sample_form
self.datatx_info = datatx_info
- def last_state(self):
+ @property
+ def state( self ):
+ # The states mapper for this object orders ascending
return self.states[-1]
-
- def change_state_widgets(self, trans, sample=None):
- if sample:
- curr_state = sample.current_state()
- else:
- curr_state = self.states[0]
- states_input = SelectField('select_state')
- for state in self.states:
- if curr_state.name == state.name:
- states_input.add_option(state.name, state.id, selected=True)
- else:
- states_input.add_option(state.name, state.id)
- widgets = []
- if sample:
- widgets.append(('Select the new state of the sample from the list of possible state(s)',
- states_input))
- else:
- widgets.append(('Select the new state of the selected sample(s) from the list of possible state(s)',
- states_input))
- widgets.append(('Comments', TextField('comment', 50)))
- title = 'Change current state'
- return widgets, title
class RequestTypePermissions( object ):
def __init__( self, action, request_type, role ):
@@ -1749,16 +1745,15 @@ class RequestTypePermissions( object ):
self.role = role
class Sample( object ):
- bulk_operations = Bunch(CHANGE_STATE = 'Change state',
- SELECT_LIBRARY = 'Select data library and folder')
+ bulk_operations = Bunch( CHANGE_STATE = 'Change state',
+ SELECT_LIBRARY = 'Select data library and folder' )
transfer_status = Bunch( NOT_STARTED = 'Not started',
IN_QUEUE = 'In queue',
TRANSFERRING = 'Transferring dataset',
ADD_TO_LIBRARY = 'Adding to data library',
COMPLETE = 'Complete',
- ERROR = 'Error')
- def __init__(self, name=None, desc=None, request=None, form_values=None,
- bar_code=None, library=None, folder=None):
+ ERROR = 'Error' )
+ def __init__(self, name=None, desc=None, request=None, form_values=None, bar_code=None, library=None, folder=None):
self.name = name
self.desc = desc
self.request = request
@@ -1766,29 +1761,39 @@ class Sample( object ):
self.bar_code = bar_code
self.library = library
self.folder = folder
- def current_state(self):
+ @property
+ def state( self ):
+ latest_event = self.latest_event
+ if latest_event:
+ return latest_event.state
+ return None
+ @property
+ def latest_event( self ):
if self.events:
- return self.events[0].state
+ return self.events[0]
return None
- def untransferred_dataset_files(self):
+ @property
+ def untransferred_dataset_files( self ):
count = 0
for df in self.datasets:
if df.status == self.transfer_status.NOT_STARTED:
count = count + 1
return count
- def inprogress_dataset_files(self):
+ @property
+ def inprogress_dataset_files( self ):
count = 0
for df in self.datasets:
if df.status not in [self.transfer_status.NOT_STARTED, self.transfer_status.COMPLETE]:
count = count + 1
return count
- def transferred_dataset_files(self):
+ @property
+ def transferred_dataset_files( self ):
count = 0
for df in self.datasets:
if df.status == self.transfer_status.COMPLETE:
count = count + 1
return count
- def dataset_size(self, filepath):
+ def dataset_size( self, filepath ):
def print_ticks(d):
pass
datatx_info = self.request.type.datatx_info
--- a/lib/galaxy/web/controllers/requests.py
+++ b/lib/galaxy/web/controllers/requests.py
@@ -1,203 +1,74 @@
from galaxy.web.base.controller import *
-from galaxy.web.framework.helpers import time_ago, iff, grids
+from galaxy.web.framework.helpers import grids
from galaxy.model.orm import *
-from galaxy.datatypes import sniff
from galaxy import model, util
-from galaxy.util.streamball import StreamBall
-from galaxy.util.odict import odict
-import logging, tempfile, zipfile, tarfile, os, sys
-from galaxy.web.form_builder import *
-from datetime import datetime, timedelta
-from cgi import escape, FieldStorage
+from galaxy.web.form_builder import *
+from galaxy.web.controllers.requests_common import RequestsGrid
+import logging
log = logging.getLogger( __name__ )
-class RequestsGrid( grids.Grid ):
- # Custom column types
- class NameColumn( grids.TextColumn ):
- def get_value(self, trans, grid, request):
- return request.name
- class DescriptionColumn( grids.TextColumn ):
- def get_value(self, trans, grid, request):
- return request.desc
- class SamplesColumn( grids.GridColumn ):
- def get_value(self, trans, grid, request):
- return str(len(request.samples))
- class TypeColumn( grids.TextColumn ):
- def get_value(self, trans, grid, request):
- return request.type.name
- class StateColumn( grids.GridColumn ):
- def __init__( self, col_name, key, model_class, event_class, filterable, link ):
- grids.GridColumn.__init__(self, col_name, key=key, model_class=model_class, filterable=filterable, link=link)
- self.event_class = event_class
- def get_value(self, trans, grid, request):
- if request.state() == request.states.REJECTED:
- return '<div class="count-box state-color-error">%s</div>' % request.state()
- elif request.state() == request.states.NEW:
- return '<div class="count-box state-color-queued">%s</div>' % request.state()
- elif request.state() == request.states.SUBMITTED:
- return '<div class="count-box state-color-running">%s</div>' % request.state()
- elif request.state() == request.states.COMPLETE:
- return '<div class="count-box state-color-ok">%s</div>' % request.state()
- return request.state()
- def filter( self, db_session, user, query, column_filter ):
- """ Modify query to filter request by state. """
- if column_filter == "All":
- return query
- if column_filter:
- # select r.id, r.name, re.id, re.state
- # from request as r, request_event as re
- # where re.request_id=r.id and re.state='Complete' and re.create_time in
- # (select MAX( create_time)
- # from request_event
- # group by request_id)
- q = query.join(self.event_class.table)\
- .filter( self.model_class.table.c.id==self.event_class.table.c.request_id )\
- .filter( self.event_class.table.c.state==column_filter )\
- .filter( self.event_class.table.c.id.in_(select(columns=[func.max(self.event_class.table.c.id)],
- from_obj=self.event_class.table,
- group_by=self.event_class.table.c.request_id)))
- return q
- def get_accepted_filters( self ):
- """ Returns a list of accepted filters for this column. """
- accepted_filter_labels_and_vals = [ model.Request.states.NEW,
- model.Request.states.REJECTED,
- model.Request.states.SUBMITTED,
- model.Request.states.COMPLETE,
- "All"]
- accepted_filters = []
- for val in accepted_filter_labels_and_vals:
- label = val.lower()
- args = { self.key: val }
- accepted_filters.append( grids.GridColumnFilter( label, args) )
- return accepted_filters
- # Grid definition
- title = "Sequencing Requests"
- template = 'requests/grid.mako'
- model_class = model.Request
- default_sort_key = "create_time"
- num_rows_per_page = 50
- preserve_state = True
- use_paging = True
- default_filter = dict( deleted="False")
- columns = [
- NameColumn( "Name",
- key="name",
- model_class=model.Request,
- link=( lambda item: iff( item.deleted, None, dict( operation="show", id=item.id ) ) ),
- attach_popup=True,
- filterable="advanced" ),
- DescriptionColumn( "Description",
- key='desc',
- model_class=model.Request,
- filterable="advanced" ),
- SamplesColumn( "Sample(s)",
- link=( lambda item: iff( item.deleted, None, dict( operation="show", id=item.id ) ) ), ),
- TypeColumn( "Sequencer" ),
- grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
- grids.DeletedColumn( "Deleted",
- key="deleted",
- visible=False,
- filterable="advanced" ),
- StateColumn( "State",
- model_class=model.Request,
- event_class=model.RequestEvent,
- key='state',
- filterable="advanced",
- link=( lambda item: iff( item.deleted, None, dict( operation="events", id=item.id ) ) ) )
- ]
- columns.append( grids.MulticolFilterColumn( "Search",
- cols_to_filter=[ columns[0], columns[1] ],
- key="free-text-search",
- visible=False,
- filterable="standard" ) )
- operations = [
- grids.GridOperation( "Submit", allow_multiple=False, condition=( lambda item: not item.deleted and item.unsubmitted() and item.samples ),
- confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." ),
- grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted and item.unsubmitted() ) ),
- grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted and item.new() ) ),
- grids.GridOperation( "Undelete", allow_multiple=True, condition=( lambda item: item.deleted ) )
-
- ]
+class UserRequestsGrid( RequestsGrid ):
+ operations = [ operation for operation in RequestsGrid.operations ]
+ operations.append( grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted and item.is_unsubmitted ) ) )
+ operations.append( grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted and item.is_new ) ) )
+ operations.append( grids.GridOperation( "Undelete", allow_multiple=True, condition=( lambda item: item.deleted ) ) )
global_actions = [
grids.GridAction( "Create new request", dict( controller='requests_common',
- cntrller='requests',
- action='new',
- select_request_type='True' ) )
+ action='create_request',
+ cntrller='requests' ) )
]
def apply_query_filter( self, trans, query, **kwd ):
+ # gvk ( 9/28/10 ) TODO: is this method needed?
return query.filter_by( user=trans.user )
class Requests( BaseController ):
- request_grid = RequestsGrid()
+ request_grid = UserRequestsGrid()
@web.expose
- @web.require_login( "create/submit sequencing requests" )
+ @web.require_login( "view sequencing requests" )
def index( self, trans ):
return trans.fill_template( "requests/index.mako" )
-
@web.expose
@web.require_login( "create/submit sequencing requests" )
- def find_index( self, trans ):
- return trans.fill_template( "requests/find_index.mako" )
-
+ def find_samples_index( self, trans ):
+ return trans.fill_template( "requests/find_samples_index.mako" )
@web.expose
- @web.require_login( "create/submit sequencing requests" )
- def list( self, trans, **kwd ):
- '''
- List all request made by the current user
- '''
-
+ def browse_requests( self, trans, **kwd ):
if 'operation' in kwd:
operation = kwd['operation'].lower()
- if not kwd.get( 'id', None ):
- return trans.response.send_redirect( web.url_for( controller='requests',
- action='list',
- status='error',
- message="Invalid request ID") )
- if operation == "show":
+ if operation == "edit":
return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_basic_request_info',
cntrller='requests',
- action='show',
**kwd ) )
- elif operation == "submit":
+ if operation == "manage_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
cntrller='requests',
- action='submit',
**kwd ) )
- elif operation == "delete":
+ if operation == "delete":
return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='delete_request',
cntrller='requests',
- action='delete',
**kwd ) )
- elif operation == "undelete":
+ if operation == "undelete":
return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='undelete_request',
cntrller='requests',
- action='undelete',
**kwd ) )
- elif operation == "edit":
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller='requests',
- action='edit',
- show=True, **kwd ) )
- elif operation == "events":
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller='requests',
- action='events',
- **kwd ) )
- # if there are one or more requests that has been rejected by the admin
- # recently, then show a message as a reminder to the user
- rlist = trans.sa_session.query( trans.app.model.Request ) \
- .filter( trans.app.model.Request.table.c.deleted==False ) \
- .filter( trans.app.model.Request.table.c.user_id==trans.user.id )
+ # If there are requests that have been rejected, show a message as a reminder to the user
rejected = 0
- for r in rlist:
- if r.rejected():
+ for request in trans.sa_session.query( trans.app.model.Request ) \
+ .filter( trans.app.model.Request.table.c.deleted==False ) \
+ .filter( trans.app.model.Request.table.c.user_id==trans.user.id ):
+ if request.is_rejected:
rejected = rejected + 1
if rejected:
- kwd['status'] = 'warning'
- kwd['message'] = "%d requests (highlighted in red) were rejected, click on the request name for details." \
- % rejected
+ status = 'warning'
+ message = "%d requests (highlighted in red) were rejected. Click on the request name for details." % rejected
+ kwd[ 'status' ] = status
+ kwd[ 'message' ] = message
# Render the list view
return self.request_grid( trans, **kwd )
--- /dev/null
+++ b/templates/requests/common/find_samples.mako
@@ -0,0 +1,84 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${h.js("jquery.autocomplete", "autocomplete_tagging" )}
+</%def>
+
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "autocomplete_tagging" )}
+</%def>
+
+<% is_admin = cntrller == 'requests_admin' and trans.user_is_admin() %>
+
+<br/>
+<br/>
+<ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse requests</a>
+ </li>
+</ul>
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolForm">
+ <div class="toolFormTitle">Find samples</div>
+ <div class="toolFormBody">
+ <form name="find_request" id="find_request" action="${h.url_for( controller='requests_common', action='find_samples', cntrller=cntrller )}" method="post" >
+ <div class="form-row">
+ <label>Find samples using:</label>
+ ${search_type.get_html()}
+ <div class="toolParamHelp" style="clear: both;">
+ Select a sample attribute for searching. To search <br/>
+ for a sample with a dataset name, select the dataset <br/>
+ option above. This will return all the samples that <br/>
+ are associated with a dataset with that name. <br/>
+ </div>
+ </div>
+ <div class="form-row">
+ <label>Show only sequencing requests in state:</label>
+ ${request_states.get_html()}
+ </div>
+ <div class="form-row">
+ ${search_box.get_html()}
+ <input type="submit" name="find_samples_button" value="Find"/>
+ <div class="toolParamHelp" style="clear: both;">
+ Wildcard search (%) can be used as placeholder for any sequence of characters or words.<br/>
+ For example, to search for samples starting with 'mysample' use 'mysample%' as the search string.
+ </div>
+ </div>
+ %if results:
+ <div class="form-row">
+ <label><i>${results}</i></label>
+ %if samples:
+ <div class="toolParamHelp" style="clear: both;">
+ The search results are sorted by the date the samples where created.
+ </div>
+ %endif
+ </div>
+ %endif
+ <div class="form-row">
+ %if samples:
+ %for sample in samples:
+ <div class="form-row">
+ Sample: <b>${sample.name}</b> | Barcode: ${sample.bar_code}<br/>
+ State: ${sample.state}<br/>
+ Datasets: <a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.transferred_dataset_files}/${len( sample.datasets )}</a><br/>
+ %if is_admin:
+ <i>User: ${sample.request.user.email}</i>
+ %endif
+ <div class="toolParamHelp" style="clear: both;">
+ <a href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Sequencing request: ${sample.request.name} | Type: ${sample.request.type.name} | State: ${sample.request.state}</a>
+ </div>
+ </div>
+ <br/>
+ %endfor
+ %endif
+ </div>
+ </form>
+ </div>
+</div>
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -78,16 +78,14 @@
%>
## Lab menu.
- %if trans.user and trans.user.requests:
- <%
- menu_options = [
- [ 'Sequencing Requests', h.url_for( controller='/requests', action='index' ) ],
- [ 'Find Samples', h.url_for( controller='/requests', action='find_index' ) ],
- [ 'Help', app.config.get( "lims_doc_url", "http://main.g2.bx.psu.edu/u/rkchak/p/sts" ), "galaxy_main" ]
- ]
- tab( "lab", "Lab", None, menu_options=menu_options )
- %>
- %endif
+ <%
+ menu_options = [
+ [ 'Sequencing Requests', h.url_for( controller='/requests', action='index' ) ],
+ [ 'Find Samples', h.url_for( controller='/requests', action='find_samples_index' ) ],
+ [ 'Help', app.config.get( "lims_doc_url", "http://main.g2.bx.psu.edu/u/rkchak/p/sts" ), "galaxy_main" ]
+ ]
+ tab( "lab", "Lab", None, menu_options=menu_options, visible=( trans.user and trans.user.requests ) )
+ %>
## Visualization menu.
%if app.config.get_bool( 'enable_tracks', False ):
--- a/lib/galaxy/web/controllers/forms.py
+++ b/lib/galaxy/web/controllers/forms.py
@@ -60,8 +60,7 @@ class FormsGrid( grids.Grid ):
grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ),
]
global_actions = [
- grids.GridAction( "Create new form", dict( controller='forms',
- action='new' ) )
+ grids.GridAction( "Create new form", dict( controller='forms', action='create_form' ) )
]
class Forms( BaseController ):
@@ -123,7 +122,7 @@ class Forms( BaseController ):
@web.expose
@web.require_admin
- def new( self, trans, **kwd ):
+ def create_form( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
@@ -132,7 +131,7 @@ class Forms( BaseController ):
fd, message = self.__save_form( trans, fdc_id=None, **kwd )
if not fd:
return trans.response.send_redirect( web.url_for( controller='forms',
- action='new',
+ action='create_form',
message=message,
status='error',
name=util.restore_text( params.get( 'name', '' ) ),
@@ -174,7 +173,7 @@ class Forms( BaseController ):
trans.sa_session.flush()
return trans.response.send_redirect( web.url_for( controller='forms',
action='manage',
- message='%i form(s) is deleted.' % len(id_list),
+ message='%i forms have been deleted.' % len(id_list),
status='done') )
def __undelete( self, trans, **kwd ):
id_list = util.listify( kwd['id'] )
@@ -192,7 +191,7 @@ class Forms( BaseController ):
trans.sa_session.flush()
return trans.response.send_redirect( web.url_for( controller='forms',
action='manage',
- message='%i form(s) is undeleted.' % len(id_list),
+ message='%i forms have been undeleted.' % len(id_list),
status='done') )
@web.expose
def edit( self, trans, response_redirect=None, **kwd ):
@@ -470,7 +469,7 @@ class Forms( BaseController ):
'default': row[6]})
except:
return trans.response.send_redirect( web.url_for( controller='forms',
- action='new',
+ action='create_form',
status='error',
message='Error in importing <b>%s</b> file' % csv_file.file))
self.__imported_from_file = True
@@ -501,7 +500,7 @@ class Forms( BaseController ):
# validate fields
for field in current_form[ 'fields' ]:
if not field[ 'label' ]:
- return None, "All the field label(s) must be completed."
+ return None, "All the field labels must be completed."
# create a new form definition
fd = trans.app.model.FormDefinition(name=current_form[ 'name' ],
desc=current_form[ 'desc' ],
--- a/templates/webapps/galaxy/admin/index.mako
+++ b/templates/webapps/galaxy/admin/index.mako
@@ -115,8 +115,8 @@
<div class="toolSectionBody"><div class="toolSectionBg"><div class="toolTitle"><a href="${h.url_for( controller='requests_admin', action='manage_request_types' )}" target="galaxy_main">Sequencer configurations</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='requests_admin', action='list')}" target="galaxy_main">Sequencing requests</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='requests_common', action='find', cntrller='requests_admin')}" target="galaxy_main">Find samples</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='requests_admin', action='browse_requests' )}" target="galaxy_main">Sequencing requests</a></div>
+ <div class="toolTitle"><a href="${h.url_for( controller='requests_common', action='find_samples', cntrller='requests_admin' )}" target="galaxy_main">Find samples</a></div></div></div></div>
--- a/templates/admin/forms/create_form.mako
+++ b/templates/admin/forms/create_form.mako
@@ -8,7 +8,7 @@
<div class="toolForm"><div class="toolFormTitle">Create a new form definition</div><div class="toolFormBody">
- <form name="create_form" action="${h.url_for( controller='forms', action='new', create_form=True )}" enctype="multipart/form-data" method="post" >
+ <form name="create_form" action="${h.url_for( controller='forms', action='create_form', create_form=True )}" enctype="multipart/form-data" method="post" >
%for label, input in inputs:
<div class="form-row"><label>${label}</label>
--- /dev/null
+++ b/templates/requests/common/edit_basic_request_info.mako
@@ -0,0 +1,87 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+<br/><br/>
+<ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
+ </li>
+ <li>
+ <a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse all requests</a>
+ </li>
+</ul>
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolForm">
+ <div class="toolFormTitle">Edit sequencing request "${request.name}"</div>
+ <div class="toolFormBody">
+ <form name="edit_basic_request_info" id="edit_basic_request_info" action="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}" method="post" >
+ %for i, field in enumerate( widgets ):
+ <div class="form-row">
+ <label>${field['label']}</label>
+ ${field['widget'].get_html()}
+ <div class="toolParamHelp" style="clear: both;">
+ ${field['helptext']}
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ %endfor
+ <div class="form-row">
+ <input type="submit" name="edit_basic_request_info_button" value="Save"/>
+ </div>
+ </form>
+ </div>
+</div>
+<p/>
+<div class="toolForm">
+ <div class="toolFormTitle">Email notification settings</div>
+ <div class="toolFormBody">
+ <form name="edit_email_settings" id="edit_email_settings" action="${h.url_for( controller='requests_common', action='edit_email_settings', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}" method="post" >
+ <%
+ email_address = ''
+ emails = ''
+ additional_email_addresses = []
+ if request.notification:
+ for e in request.notification[ 'email' ]:
+ if e == request.user.email:
+ email_address = 'checked'
+ else:
+ additional_email_addresses.append( e )
+ if additional_email_addresses:
+ emails = '\r\n'.join( additional_email_addresses )
+ %>
+ <div class="form-row">
+ <label>Send to:</label>
+ <input type="checkbox" name="email_address" value="true" ${email_address}>${request.user.email} (sequencing request owner)<input type="hidden" name="email_address" value="true">
+ </div>
+ <div class="form-row">
+ <label>Additional email addresses:</label>
+ <textarea name="additional_email_addresses" rows="3" cols="40">${emails}</textarea>
+ <div class="toolParamHelp" style="clear: both;">
+ Enter one email address per line
+ </div>
+ </div>
+ <div class="form-row">
+ <label>Select sample states to send email notification:</label>
+ %for sample_state in request.type.states:
+ <%
+ email_state = ''
+ if request.notification and sample_state.id in request.notification[ 'sample_states' ]:
+ email_state = 'checked'
+ %>
+ <input type="checkbox" name=sample_state_${sample_state.id} value="true" ${email_state} >${sample_state.name}<input type="hidden" name=sample_state_${sample_state.id} value="true">
+ <br/>
+ %endfor
+ <div class="toolParamHelp" style="clear: both;">
+ Email notification will be sent when all the samples of this sequencing request are in the selected states.
+ </div>
+ </div>
+ <div class="form-row">
+ <input type="submit" name="edit_email_settings_button" value="Save"/>
+ </div>
+ </form>
+ </div>
+</div>
--- a/lib/galaxy/web/controllers/library_common.py
+++ b/lib/galaxy/web/controllers/library_common.py
@@ -1781,7 +1781,7 @@ class LibraryCommon( BaseController, Use
if not forms:
message = "There are no forms on which to base the template, so create a form and then add the template."
return trans.response.send_redirect( web.url_for( controller='forms',
- action='new',
+ action='create_request',
message=message,
status='done',
form_type=trans.app.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE ) )
--- a/templates/requests/common/show_request.mako
+++ /dev/null
@@ -1,644 +0,0 @@
-<%namespace file="/message.mako" import="render_msg" />
-<%namespace file="/requests/common/sample_state.mako" import="render_sample_state" />
-<%namespace file="/requests/common/sample_datasets.mako" import="render_sample_datasets" />
-
-<%!
- def inherit(context):
- if context.get('use_panels'):
- return '/webapps/galaxy/base_panels.mako'
- else:
- return '/base.mako'
-%>
-<%inherit file="${inherit(context)}"/>
-
-<%def name="stylesheets()">
- ${parent.stylesheets()}
- ${h.css( "library" )}
-</%def>
-
-<%def name="javascripts()">
- ${parent.javascripts()}
- <script type="text/javascript">
- function showContent(vThis)
- {
- // http://www.javascriptjunkie.com
- // alert(vSibling.className + " " + vDef_Key);
- vParent = vThis.parentNode;
- vSibling = vParent.nextSibling;
- while (vSibling.nodeType==3) {
- // Fix for Mozilla/FireFox Empty Space becomes a TextNode or Something
- vSibling = vSibling.nextSibling;
- };
- if(vSibling.style.display == "none")
- {
- vThis.src="/static/images/fugue/toggle.png";
- vThis.alt = "Hide";
- vSibling.style.display = "block";
- } else {
- vSibling.style.display = "none";
- vThis.src="/static/images/fugue/toggle-expand.png";
- vThis.alt = "Show";
- }
- return;
- }
-
- $(document).ready(function(){
- //hide the all of the element with class msg_body
- $(".msg_body").hide();
- //toggle the componenet with class msg_body
- $(".msg_head").click(function(){
- $(this).next(".msg_body").slideToggle(0);
- });
- });
-
- // Looks for changes in sample states using an async request. Keeps
- // calling itself (via setTimeout) until all samples are in a terminal
- // state.
- var updater = function ( sample_states ) {
- // Check if there are any items left to track
- var empty = true;
- for ( i in sample_states ) {
- empty = false;
- break;
- }
- if ( ! empty ) {
- setTimeout( function() { updater_callback( sample_states ) }, 1000 );
- }
- };
- var updater_callback = function ( sample_states ) {
- // Build request data
- var ids = []
- var states = []
- $.each( sample_states, function ( id, state ) {
- ids.push( id );
- states.push( state );
- });
- // Make ajax call
- $.ajax( {
- type: "POST",
- url: "${h.url_for( controller='requests_common', action='sample_state_updates' )}",
- dataType: "json",
- data: { ids: ids.join( "," ), states: states.join( "," ) },
- success : function ( data ) {
- $.each( data, function( id, val, cntrller ) {
- // 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 );
- },
- error: function() {
- // Just retry, like the old method, should try to be smarter
- updater( sample_states );
- }
- });
- };
-
- function checkAllFields()
- {
- var chkAll = document.getElementById('checkAll');
- var checks = document.getElementsByTagName('input');
- var boxLength = checks.length;
- var allChecked = false;
- var totalChecked = 0;
- if ( chkAll.checked == true )
- {
- for ( i=0; i < boxLength; i++ )
- {
- if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
- {
- checks[i].checked = true;
- }
- }
- }
- else
- {
- for ( i=0; i < boxLength; i++ )
- {
- if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
- {
- checks[i].checked = false
- }
- }
- }
- }
-
- function stopRKey(evt) {
- var evt = (evt) ? evt : ((event) ? event : null);
- var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
- if ((evt.keyCode == 13) && (node.type=="text")) {return false;}
- }
- document.onkeypress = stopRKey
- </script>
-</%def>
-
-<% samples_not_ready = request.sequence_run_ready() %>
-%if samples_not_ready:
- ${render_msg( "Select a target data library and folder for all the samples before starting the sequence run", "warning" )}
-%endif
-
-%if request.rejected():
- ${render_msg( "Reason for rejection: "+request.last_comment(), "warning" )}
-%endif
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-<div class="grid-header">
- <h2>Sequencing Request "${request.name}"</h2>
- <div class="toolParamHelp" style="clear: both;">
- <b>Sequencer</b>: ${request.type.name}
- %if cntrller == 'requests_admin':
- | <b>User</b>: ${request.user.email}
- %endif
- %if request.state() == request.states.SUBMITTED:
- | <b>State</b>: <i>${request.state()}</i>
- %else:
- | <b>State</b>: ${request.state()}
- %endif
- </div>
-</div>
-
-<br/><br/>
-
-<ul class="manage-table-actions">
- <li><a class="action-button" id="seqreq-${request.id}-popup" class="menubutton">Sequencing Request Actions</a></li>
- <div popupmenu="seqreq-${request.id}-popup">
- %if request.unsubmitted() and request.samples:
- <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller=cntrller, action='list', operation='Submit', id=trans.security.encode_id(request.id) )}">Submit</a>
- %endif
- <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='events', id=trans.security.encode_id(request.id) )}">History</a>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='Edit', id=trans.security.encode_id(request.id))}">Edit</a>
- %if cntrller == 'requests_admin' and trans.user_is_admin():
- %if request.submitted():
- <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='reject', id=trans.security.encode_id(request.id))}">Reject</a>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', show_page=True, request_id=request.id)}">Select dataset(s) to transfer</a>
- %endif
- %endif
- </div>
- <li><a class="action-button" href="${h.url_for( controller=cntrller, action='list')}">Browse requests</a></li>
-</ul>
-
-<h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> Request Information</h4>
-<div style="display:none;" >
- <table class="grid" border="0">
- <tbody>
- <tr>
- <td valign="top" width="50%">
- <div class="form-row">
- <label>Description:</label>
- %if request.desc:
- ${request.desc}
- %else:
- <i>None</i>
- %endif
- </div>
- <div style="clear: both"></div>
- %for index, rd in enumerate(request_details):
- <div class="form-row">
- <label>${rd['label']}:</label>
- %if not rd['value']:
- <i>None</i>
- %else:
- %if rd['label'] == 'State':
- <a href="${h.url_for( controller=cntrller, action='list', operation='events', id=trans.security.encode_id(request.id) )}">${rd['value']}</a>
- %else:
- ${rd['value']}
- %endif
- %endif
- </div>
- <div style="clear: both"></div>
- %endfor
- </td>
- <td valign="top" width="50%">
- <div class="form-row">
- <label>Date created:</label>
- ${request.create_time}
- </div>
- <div class="form-row">
- <label>Date updated:</label>
- ${request.update_time}
- </div>
- <div class="form-row">
- <label>Email notification recipient(s):</label>
- <% emails = ', '.join(request.notification['email']) %>
- %if emails:
- ${emails}
- %else:
- <i>None</i>
- %endif
- </div>
- <div style="clear: both"></div>
- <div class="form-row">
- <label>Email notification on sample state(s):</label>
- <%
- states = []
- for ss in request.type.states:
- if ss.id in request.notification['sample_states']:
- states.append(ss.name)
- states = ', '.join(states)
- %>
- %if states:
- ${states}
- %else:
- <i>None</i>
- %endif
- </div>
- <div style="clear: both"></div>
- </td>
- </tr>
- </tbody>
- </table>
- <div class="form-row">
- <ul class="manage-table-actions">
- <li><a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='Edit', id=trans.security.encode_id(request.id))}">Edit request information</a></li>
- </ul>
- </div>
-</div>
-<br/>
-<form id="show_request" name="show_request" action="${h.url_for( controller='requests_common', cntrller=cntrller, action='request_page', edit_mode=edit_mode )}" method="post" >
- <input type="hidden" name="id" value="${trans.security.encode_id(request.id)}" />
- %if current_samples:
- ## first render the basic info grid
- ${render_basic_info_grid()}
- %if not request.new() and edit_mode == 'False' and len(sample_ops.options) > 1:
- <div class="form-row" style="background-color:#FAFAFA;">
- For selected sample(s):
- ${sample_ops.get_html()}
- </div>
- %if 'none' not in sample_ops.get_selected( return_label=True, return_value=True ) and len(selected_samples):
- <div class="form-row" style="background-color:#FAFAFA;">
- %if trans.app.model.Sample.bulk_operations.CHANGE_STATE in sample_ops.get_selected( return_label=True, return_value=True ):
- <%
- widgets, title = request.type.change_state_widgets(trans)
- %>
- %for w in widgets:
- <div class="form-row">
- <label>
- ${w[0]}:
- </label>
- ${w[1].get_html()}
- %if w[0] == 'Comments':
- <div class="toolParamHelp" style="clear: both;">
- Optional
- </div>
- %endif
- </div>
- %endfor
- <div class="form-row">
- <input type="submit" name="change_state_button" value="Save"/>
- <input type="submit" name="change_state_button" value="Cancel"/>
- </div>
- %elif trans.app.model.Sample.bulk_operations.SELECT_LIBRARY in sample_ops.get_selected( return_label=True, return_value=True ):
- <div class="form-row">
- <label>Select data library:</label>
- ${bulk_lib_ops[0].get_html()}
- </div>
- %if not 'none' in bulk_lib_ops[0].get_selected( return_label=True, return_value=True ):
- <div class="form-row">
- <label>Select folder:</label>
- ${bulk_lib_ops[1].get_html()}
- </div>
- <div class="form-row">
- <input type="submit" name="change_lib_button" value="Save"/>
- <input type="submit" name="change_lib_button" value="Cancel"/>
- </div>
- %endif
- %endif
- </div>
- %endif
- %endif
- ## then render the other grid(s)
- <% trans.sa_session.refresh( request.type.sample_form ) %>
- %for grid_index, grid_name in enumerate(request.type.sample_form.layout):
- ${render_grid( grid_index, grid_name, request.type.sample_form.fields_of_grid( grid_index ) )}
- %endfor
- %else:
- <label>There are no samples.</label>
- %endif
- %if request.samples and request.submitted():
- <script type="text/javascript">
- // Updater
- updater({${ ",".join( [ '"%s" : "%s"' % ( s.id, s.current_state().name ) for s in request.samples ] ) }});
- </script>
- %endif
- %if edit_mode == 'False':
- <table class="grid">
- <tbody>
- <tr>
- <div class="form-row">
- %if request.unsubmitted():
- <td>
- %if current_samples:
- <label>Copy </label>
- <input type="integer" name="num_sample_to_copy" value="1" size="3"/>
- <label>sample(s) from sample</label>
- ${sample_copy.get_html()}
- %endif
- <input type="submit" name="add_sample_button" value="Add New"/>
- </td>
- %endif
- <td>
- %if len(current_samples) and len(current_samples) <= len(request.samples):
- <input type="submit" name="edit_samples_button" value="Edit samples"/>
- %endif
- </td>
- </div>
- </tr>
- </tbody>
- </table>
- %endif
- %if request.samples or current_samples:
- <div class="form-row">
- <div style="float: left; width: 250px; margin-right: 10px;">
- <input type="hidden" name="refresh" value="true" size="40"/>
- </div>
- <div style="clear: both"></div>
- </div>
- %if edit_mode == 'True':
- <div class="form-row">
- <input type="submit" name="save_samples_button" value="Save"/>
- <input type="submit" name="cancel_changes_button" value="Cancel"/>
- </div>
- %elif edit_mode == 'True' or len(current_samples) > len(request.samples):
- <div class="form-row">
- <input type="submit" name="save_samples_button" value="Save"/>
- <input type="submit" name="cancel_changes_button" value="Cancel"/>
- </div>
- %endif
- %endif
-</form>
-<br/>
-%if request.unsubmitted():
- <form id="import" name="import" action="${h.url_for( controller='requests_common', action='request_page', edit_mode=edit_mode, request_id=trans.security.encode_id(request.id) )}" enctype="multipart/form-data" method="post" >
- <h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> Import samples</h4>
- <div style="display:none;">
- <input type="file" name="file_data" />
- <input type="submit" name="import_samples_button" value="Import samples"/>
- <br/>
- <div class="toolParamHelp" style="clear: both;">
- The csv file must be in the following format:<br/>
- SampleName,DataLibrary,DataLibraryFolder,FieldValue1,FieldValue2...
- </div>
- </div>
- </form>
-%endif
-
-<%def name="render_grid( grid_index, grid_name, fields_dict )">
- <br/>
- <% if not grid_name:
- grid_name = "Grid "+ grid_index
- %>
- <div>
- %if edit_mode == 'True' or len(current_samples) > len(request.samples):
- <h4><img src="/static/images/fugue/toggle.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
- <div>
- %else:
- <h4><img src="/static/images/fugue/toggle-expand.png" alt="Hide" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
- <div style="display:none;">
- %endif
- <table class="grid">
- <thead>
- <tr>
- <th>Name</th>
- %for index, field in fields_dict.items():
- <th>
- ${field['label']}
- <div class="toolParamHelp" style="clear: both;">
- <i>${field['helptext']}</i>
- </div>
- </th>
- %endfor
- <th></th>
- </tr>
- <thead>
- <tbody>
- <%
- trans.sa_session.refresh( request )
- %>
- %for sample_index, sample in enumerate(current_samples):
- %if edit_mode == 'True':
- <tr>
- ${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict)}
- </tr>
- %else:
- <tr>
- %if sample_index in range(len(request.samples)):
- ${render_sample( sample_index, sample['name'], sample['field_values'], fields_dict )}
- %else:
- ${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict)}
- %endif
- </tr>
- %endif
- %endfor
- </tbody>
- </table>
- </div>
- </div>
-</%def>
-
-## This function displays the "Basic Information" grid
-<%def name="render_basic_info_grid()">
- <h3>Sample Information</h3>
- <table class="grid">
- <thead>
- <tr>
- <th><input type="checkbox" id="checkAll" name=select_all_samples value="true" onclick='checkAllFields(1);'><input type="hidden" name=select_all_samples value="true"></th>
- <th>Name</th>
- <th>Barcode</th>
- <th>State</th>
- <th>Data Library</th>
- <th>Folder</th>
- %if request.submitted() or request.complete():
- <th>Dataset(s) Transferred</th>
- %endif
- <th></th>
- </tr>
- <thead>
- <tbody>
- <% trans.sa_session.refresh( request ) %>
- %for sample_index, info in enumerate( current_samples ):
- <%
- if sample_index in range(len(request.samples)):
- sample = request.samples[sample_index]
- else:
- sample = None
- %>
- %if edit_mode == 'True':
- <tr>
- ${show_basic_info_form( sample_index, sample, info )}
- </tr>
- %else:
- <tr>
- %if sample_index in range(len(request.samples)):
- %if sample.id in selected_samples:
- <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true" checked><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"></td>
- %else:
- <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true"><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"></td>
- %endif
- <td>${info['name']}</td>
- <td>${info['barcode']}</td>
- %if sample.request.unsubmitted():
- <td>Unsubmitted</td>
- %else:
- <td id="sampleState-${sample.id}">${render_sample_state( cntrller, sample )}</td>
- %endif
- %if info['library']:
- %if cntrller == 'requests':
- <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
- %elif cntrller == 'requests_admin':
- <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library_admin', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
- %endif
- %else:
- <td></td>
- %endif
- %if info['folder']:
- <td>${info['folder'].name}</td>
- %else:
- <td></td>
- %endif
- %if request.submitted() or request.complete():
- <td id="sampleDatasets-${sample.id}">
- ${render_sample_datasets( cntrller, sample )}
- </td>
- %endif
- %else:
- ${show_basic_info_form( sample_index, sample, info )}
- %endif
- %if request.unsubmitted() or request.rejected():
- <td>
- %if sample:
- %if sample.request.unsubmitted():
- <a class="action-button" href="${h.url_for( controller='requests_common', cntrller=cntrller, action='delete_sample', request_id=request.id, sample_id=sample_index )}">
- <img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/>
- <span></span></a>
- %endif
- %endif
- </td>
- %endif
- </tr>
- %endif
- %endfor
- </tbody>
- </table>
-</%def>
-
-<%def name="show_basic_info_form( sample_index, sample, info )">
- <td></td>
- <td>
- <input type="text" name="sample_${sample_index}_name" value="${info['name']}" size="10"/>
- <div class="toolParamHelp" style="clear: both;">
- <i>${' (required)' }</i>
- </div>
- </td>
- %if cntrller == 'requests':
- %if sample:
- %if sample.request.unsubmitted():
- <td></td>
- %else:
- <td><input type="text" name="sample_${sample_index}_barcode" value="${info['barcode']}" size="10"/></td>
- %endif
- %else:
- <td></td>
- %endif
- %elif cntrller == 'requests_admin':
- %if sample:
- %if sample.request.unsubmitted():
- <td></td>
- %else:
- <td><input type="text" name="sample_${sample_index}_barcode" value="${info['barcode']}" size="10"/></td>
- %endif
- %else:
- <td></td>
- %endif
- %endif
- %if sample:
- %if sample.request.unsubmitted():
- <td>Unsubmitted</td>
- %else:
- <td><a href="${h.url_for( controller='requests_admin', action='sample_events', sample_id=sample.id)}">${sample.current_state().name}</a></td>
- %endif
- %else:
- <td></td>
- %endif
- <td>${info['lib_widget'].get_html()}</td>
- <td>${info['folder_widget'].get_html()}</td>
- %if request.submitted() or request.complete():
- %if sample:
- <td><a href="${h.url_for( controller='requests_admin', action='show_datatx_page', sample_id=trans.security.encode_id(sample.id) )}">${len(sample.datasets)}</a></td>
- %else:
- <td><a href="${h.url_for( controller='requests_admin', action='show_datatx_page', sample_id=trans.security.encode_id(sample.id) )}">Add</a></td>
- %endif
- %endif
-</%def>
-
-<%def name="render_sample( index, sample_name, sample_values, fields_dict )">
- <td>
- ${sample_name}
- </td>
- %for field_index, field in fields_dict.items():
- <td>
- %if sample_values[field_index]:
- %if field['type'] == 'WorkflowField':
- %if str(sample_values[field_index]) != 'none':
- <% workflow = trans.sa_session.query( trans.app.model.StoredWorkflow ).get( int(sample_values[field_index]) ) %>
- <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name}</a>
- %endif
- %else:
- ${sample_values[field_index]}
- %endif
- %else:
- <i>None</i>
- %endif
- </td>
- %endfor
-</%def>
-
-<%def name="render_sample_form( index, sample_name, sample_values, fields_dict )">
- <td>
- ${sample_name}
- </td>
- %for field_index, field in fields_dict.items():
- <td>
- %if field['type'] == 'TextField':
- <input type="text" name="sample_${index}_field_${field_index}" value="${sample_values[field_index]}" size="7"/>
- %elif field['type'] == 'SelectField':
- <select name="sample_${index}_field_${field_index}" last_selected_value="2">
- %for option_index, option in enumerate(field['selectlist']):
- %if option == sample_values[field_index]:
- <option value="${option}" selected>${option}</option>
- %else:
- <option value="${option}">${option}</option>
- %endif
- %endfor
- </select>
- %elif field['type'] == 'WorkflowField':
- <select name="sample_${index}_field_${field_index}">
- %if str(sample_values[field_index]) == 'none':
- <option value="none" selected>Select one</option>
- %else:
- <option value="none">Select one</option>
- %endif
- %for option_index, option in enumerate(request.user.stored_workflows):
- %if not option.deleted:
- %if str(option.id) == str(sample_values[field_index]):
- <option value="${option.id}" selected>${option.name}</option>
- %else:
- <option value="${option.id}">${option.name}</option>
- %endif
- %endif
- %endfor
- </select>
- %elif field['type'] == 'CheckboxField':
- <input type="checkbox" name="sample_${index}_field_${field_index}" value="Yes"/>
- %endif
- <div class="toolParamHelp" style="clear: both;">
- <i>${'('+field['required']+')' }</i>
- </div>
- </td>
- %endfor
-</%def>
--- a/templates/requests/common/sample_datasets.mako
+++ b/templates/requests/common/sample_datasets.mako
@@ -1,7 +1,5 @@
<%def name="render_sample_datasets( cntrller, sample )">
- <a href="${h.url_for(controller='requests_common', cntrller=cntrller, action='show_datatx_page', sample_id=trans.security.encode_id(sample.id))}">${sample.transferred_dataset_files()}/${len(sample.datasets)}</a>
+ <a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.transferred_dataset_files} / ${len( sample.datasets )}</a></%def>
-
-
${render_sample_datasets( cntrller, sample )}
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -1320,7 +1320,7 @@ class TwillTestCase( unittest.TestCase )
def create_form( self, name, desc, form_type, field_type='TextField', form_layout_name='',
num_fields=1, num_options=0, strings_displayed=[], strings_displayed_after_submit=[] ):
"""Create a new form definition."""
- self.visit_url( "%s/forms/new" % self.url )
+ self.visit_url( "%s/forms/create_form" % self.url )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
tc.fv( "1", "name", name )
@@ -1405,13 +1405,13 @@ class TwillTestCase( unittest.TestCase )
self.home()
url = "%s/forms/manage?operation=delete&id=%s" % ( self.url, form_id )
self.visit_url( url )
- check_str = "1 form(s) is deleted."
+ check_str = "1 forms have been deleted."
self.check_page_for_string( check_str )
self.home()
# Requests stuff
def check_request_grid( self, cntrller, state, deleted=False, strings_displayed=[] ):
- self.visit_url( '%s/%s/list?sort=create_time&f-state=%s&f-deleted=%s' % \
+ self.visit_url( '%s/%s/browse_requests?sort=create_time&f-state=%s&f-deleted=%s' % \
( self.url, cntrller, state.replace( ' ', '+' ), str( deleted ) ) )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
@@ -1445,17 +1445,16 @@ class TwillTestCase( unittest.TestCase )
check_str = "Permissions updated for sequencer configuration '%s'" % request_type_name
self.check_page_for_string( check_str )
self.home()
- def create_request( self, cntrller, request_type_id, name, desc, field_value_tuples, select_user_id='',
- refresh='False', strings_displayed=[], strings_displayed_after_submit=[] ):
- self.visit_url( "%s/requests_common/new?cntrller=%s&refresh=%s&select_request_type=True" % ( self.url, cntrller, refresh ) )
- # The select_request_type SelectList requires a refresh_on_change
- self.refresh_form( 'select_request_type', request_type_id )
- if cntrller == 'requests_admin' and select_user_id:
+ def create_request( self, cntrller, request_type_id, name, desc, field_value_tuples, other_users_id='',
+ strings_displayed=[], strings_displayed_after_submit=[] ):
+ self.visit_url( "%s/requests_common/create_request?cntrller=%s" % ( self.url, cntrller ) )
+ # The request_type SelectList requires a refresh_on_change
+ self.refresh_form( 'request_type_id', request_type_id )
+ if cntrller == 'requests_admin' and other_users_id:
# The admin is creating a request on behalf of another user
- # The select_user SelectList requires a refresh_on_change
- # gvk - 9/22/10: TODO: why does select_user require a refresh_on_change? Nothing in the
- # code is apparent as to why this is done.
- self.refresh_form( 'select_user', select_user_id )
+ # The user_id SelectField requires a refresh_on_change so that the selected
+ # user's addresses will be populated in the AddressField widget
+ self.refresh_form( "user_id", other_users_id )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
tc.fv( "1", "name", name )
@@ -1469,15 +1468,14 @@ class TwillTestCase( unittest.TestCase )
# user_address to be selected.
self.refresh_form( field_name, field_value )
else:
- data = self.last_page()
- file( 'greg.html', 'wb' ).write(data )
tc.fv( "1", field_name, field_value )
tc.submit( "create_request_button" )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
self.home()
- def edit_request( self, request_id, name, new_name='', new_desc='', new_fields=[], strings_displayed=[], strings_displayed_after_submit=[] ):
- self.visit_url( "%s/requests/list?operation=Edit&id=%s" % ( self.url, request_id ) )
+ def edit_basic_request_info( self, cntrller, request_id, name, new_name='', new_desc='', new_fields=[],
+ strings_displayed=[], strings_displayed_after_submit=[] ):
+ self.visit_url( "%s/requests_common/edit_basic_request_info?cntrller=%s&id=%s" % ( self.url, cntrller, request_id ) )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
self.check_page_for_string( 'Edit sequencing request "%s"' % name )
@@ -1487,21 +1485,21 @@ class TwillTestCase( unittest.TestCase )
tc.fv( "1", "desc", new_desc )
for index, field_value in enumerate( new_fields ):
tc.fv( "1", "field_%i" % index, field_value )
- tc.submit( "save_changes_request_button" )
+ tc.submit( "edit_basic_request_info_button" )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
def add_samples( self, cntrller, request_id, request_name, sample_value_tuples, strings_displayed=[], strings_displayed_after_submit=[] ):
- self.visit_url( "%s/requests/list?operation=show&id=%s" % ( self.url, request_id ) )
+ self.visit_url( "%s/requests_common/manage_request?cntrller=%s&id=%s" % ( self.url, cntrller, request_id ) )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
# Simulate clicking the add-sample_button on the form. (gvk: 9/21/10 - TODO : There must be a bug in the mako template
# because twill cannot find any forms on the page, but I cannot find it although I've spent time cleaning up the
# template code and looking for any problems.
- url = "%s/requests_common/request_page?cntrller=%s&edit_mode=False&id=%s" % ( self.url, cntrller, request_id )
+ url = "%s/requests_common/manage_request?cntrller=%s&id=%s" % ( self.url, cntrller, request_id )
# This should work, but although twill does not thorw any exceptions, the button click never occurs
- # There are multiple forms on this page, and we'll only be using the form named show_request.
+ # There are multiple forms on this page, and we'll only be using the form named manage_request.
# for sample_index, sample_value_tuple in enumerate( sample_value_tuples ):
- # # Add the following form value to the already populated hidden field so that the show_request
+ # # Add the following form value to the already populated hidden field so that the manage_request
# # form is the current form
# tc.fv( "1", "id", request_id )
# tc.submit( 'add_sample_button' )
@@ -1528,11 +1526,11 @@ class TwillTestCase( unittest.TestCase )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
def submit_request( self, cntrller, request_id, request_name, strings_displayed_after_submit=[] ):
- self.visit_url( "%s/%s/list?operation=Submit&id=%s" % ( self.url, cntrller, request_id ) )
+ self.visit_url( "%s/requests_common/submit_request?cntrller=%s&id=%s" % ( self.url, cntrller, request_id ) )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
def reject_request( self, request_id, request_name, comment, strings_displayed=[], strings_displayed_after_submit=[] ):
- self.visit_url( "%s/requests_admin/list?operation=Reject&id=%s" % ( self.url, request_id ) )
+ self.visit_url( "%s/requests_admin/reject?id=%s" % ( self.url, request_id ) )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
tc.fv( "1", "comment", comment )
@@ -1542,7 +1540,7 @@ class TwillTestCase( unittest.TestCase )
def add_bar_codes( self, request_id, request_name, bar_codes, samples, strings_displayed_after_submit=[] ):
# We have to simulate the form submission here since twill barfs on the page
# gvk - 9/22/10 - TODO: make sure the mako template produces valid html
- url = "%s/requests_common/request_page?cntrller=requests_admin&edit_mode=True&id=%s" % ( self.url, request_id )
+ url = "%s/requests_common/manage_request?cntrller=requests_admin&id=%s&edit_samples_button=Edit+samples" % ( self.url, request_id )
for index, field_value in enumerate( bar_codes ):
sample_field_name = "sample_%i_name" % index
sample_field_value = samples[ index ].name.replace( ' ', '+' )
@@ -1553,18 +1551,18 @@ class TwillTestCase( unittest.TestCase )
self.visit_url( url )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
- def change_sample_state( self, request_id, request_name, sample_name, sample_id, new_state_id, new_state_name, comment='',
+ def change_sample_state( self, request_id, request_name, sample_name, sample_id, new_sample_state_id, new_state_name, comment='',
strings_displayed=[], strings_displayed_after_submit=[] ):
# We have to simulate the form submission here since twill barfs on the page
# gvk - 9/22/10 - TODO: make sure the mako template produces valid html
- url = "%s/requests_common/request_page?cntrller=requests_admin&edit_mode=False&id=%s" % ( self.url, request_id )
+ url = "%s/requests_common/manage_request?cntrller=requests_admin&id=%s" % ( self.url, request_id )
# select_sample_%i=true must be included twice to simulate a CheckboxField checked setting.
- url += "&comment=%s&select_sample_%i=true&select_sample_%i=true&select_state=%i" % ( comment, sample_id, sample_id, new_state_id )
- url += "&select_sample_operation=Change%20state&refresh=true"
+ url += "&comment=%s&select_sample_%i=true&select_sample_%i=true&sample_state_id=%i" % ( comment, sample_id, sample_id, new_sample_state_id )
+ url += "&sample_operation=Change%20state&refresh=true"
url += "&change_state_button=Save"
self.visit_url( url )
self.check_page_for_string( 'Sequencing Request "%s"' % request_name )
- self.visit_url( "%s/requests_common/sample_events?cntrller=requests_admin&sample_id=%i" % (self.url, sample_id) )
+ self.visit_url( "%s/requests_common/sample_events?cntrller=requests_admin&sample_id=%i" % ( self.url, sample_id ) )
self.check_page_for_string( 'Events for Sample "%s"' % sample_name )
self.check_page_for_string( new_state_name )
def add_user_address( self, user_id, address_dict ):
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -1,23 +1,112 @@
from galaxy.web.base.controller import *
from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy.model.orm import *
-from galaxy.datatypes import sniff
from galaxy import model, util
-from galaxy.util.streamball import StreamBall
-import logging, tempfile, zipfile, tarfile, os, sys, subprocess
-from galaxy.web.form_builder import *
-from datetime import datetime, timedelta
-from sqlalchemy.sql.expression import func, and_
-from sqlalchemy.sql import select
-import pexpect
-import ConfigParser, threading, time
-from amqplib import client_0_8 as amqp
-import csv, smtplib, socket
+from galaxy.web.form_builder import *
+import logging, os, csv
+
log = logging.getLogger( __name__ )
+class RequestsGrid( grids.Grid ):
+ # Custom column types
+ class NameColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, request ):
+ return request.name
+ class DescriptionColumn( grids.TextColumn ):
+ def get_value(self, trans, grid, request):
+ return request.desc
+ class SamplesColumn( grids.GridColumn ):
+ def get_value(self, trans, grid, request):
+ return str( len( request.samples ) )
+ class TypeColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, request ):
+ return request.type.name
+ class StateColumn( grids.StateColumn ):
+ def get_value(self, trans, grid, request ):
+ state = request.state
+ if state == request.states.REJECTED:
+ state_color = 'error'
+ elif state == request.states.NEW:
+ state_color = 'new'
+ elif state == request.states.SUBMITTED:
+ state_color = 'running'
+ elif state == request.states.COMPLETE:
+ state_color = 'ok'
+ else:
+ state_color = state
+ return '<div class="count-box state-color-%s">%s</div>' % ( state_color, state )
+ def filter( self, trans, user, query, column_filter ):
+ """ Modify query to filter request by state. """
+ if column_filter == "All":
+ return query
+ if column_filter:
+ return query.join( model.RequestEvent.table ) \
+ .filter( self.model_class.table.c.id == model.RequestEvent.table.c.request_id ) \
+ .filter( model.RequestEvent.table.c.state == column_filter ) \
+ .filter( model.RequestEvent.table.c.id.in_( select( columns=[ func.max( model.RequestEvent.table.c.id ) ],
+ from_obj=model.RequestEvent.table,
+ group_by=model.RequestEvent.table.c.request_id ) ) )
+ def get_accepted_filters( self ):
+ """ Returns a list of accepted filters for this column. """
+ # TODO: is this method necessary?
+ accepted_filter_labels_and_vals = [ model.Request.states.get( state ) for state in model.Request.states ]
+ accepted_filter_labels_and_vals.append( "All" )
+ accepted_filters = []
+ for val in accepted_filter_labels_and_vals:
+ label = val.lower()
+ args = { self.key: val }
+ accepted_filters.append( grids.GridColumnFilter( label, args ) )
+ return accepted_filters
+
+ # Grid definition
+ title = "Sequencing Requests"
+ template = "requests/grid.mako"
+ model_class = model.Request
+ default_sort_key = "-update_time"
+ num_rows_per_page = 50
+ preserve_state = True
+ use_paging = True
+ default_filter = dict( state="All", deleted="False" )
+ columns = [
+ NameColumn( "Name",
+ key="name",
+ link=( lambda item: iff( item.deleted, None, dict( operation="manage_request", id=item.id ) ) ),
+ attach_popup=True,
+ filterable="advanced" ),
+ DescriptionColumn( "Description",
+ key='desc',
+ filterable="advanced" ),
+ SamplesColumn( "Samples",
+ link=( lambda item: iff( item.deleted, None, dict( operation="manage_request", id=item.id ) ) ) ),
+ TypeColumn( "Sequencer",
+ link=( lambda item: iff( item.deleted, None, dict( operation="view_type", id=item.type.id ) ) ) ),
+ grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
+ grids.DeletedColumn( "Deleted",
+ key="deleted",
+ visible=False,
+ filterable="advanced" ),
+ StateColumn( "State",
+ key='state',
+ filterable="advanced",
+ link=( lambda item: iff( item.deleted, None, dict( operation="events", id=item.id ) ) )
+ )
+ ]
+ columns.append( grids.MulticolFilterColumn( "Search",
+ cols_to_filter=[ columns[0], columns[1], columns[6] ],
+ key="free-text-search",
+ visible=False,
+ filterable="standard" ) )
+ operations = [
+ grids.GridOperation( "Submit",
+ allow_multiple=False,
+ condition=( lambda item: not item.deleted and item.is_unsubmitted and item.samples ),
+ confirm="Samples cannot be added to this request after it is submitted. Click OK to submit." )
+ ]
+
class RequestsCommon( BaseController, UsesFormDefinitionWidgets ):
@web.json
- def sample_state_updates( self, trans, ids=None, states=None, cntrller=None ):
+ def sample_state_updates( self, trans, cntrller, ids=None, states=None ):
+ pass
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
@@ -28,207 +117,186 @@ class RequestsCommon( BaseController, Us
states = states.split( "," )
for id, state in zip( ids, states ):
sample = trans.sa_session.query( self.app.model.Sample ).get( id )
- if sample.current_state().name != state:
- rval[id] = {
- "state": sample.current_state().name,
- "datasets": len(sample.datasets),
- "html_state": unicode( trans.fill_template( "requests/common/sample_state.mako", sample=sample, cntrller=cntrller ), 'utf-8' ),
- "html_datasets": unicode( trans.fill_template( "requests/common/sample_datasets.mako", trans=trans, sample=sample, cntrller=cntrller ), 'utf-8' )
- }
+ if sample.state.name != state:
+ rval[id] = { "state": sample.state.name,
+ "datasets": len( sample.datasets ),
+ "html_state": unicode( trans.fill_template( "requests/common/sample_state.mako",
+ sample=sample,
+ cntrller=cntrller ),
+ 'utf-8' ),
+ "html_datasets": unicode( trans.fill_template( "requests/common/sample_datasets.mako",
+ sample=sample,
+ cntrller=cntrller ),
+ 'utf-8' ) }
return rval
@web.expose
- @web.require_login( "create/submit sequencing requests" )
- def new(self, trans, **kwd):
+ @web.require_login( "create sequencing requests" )
+ def create_request( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ request_type_id = params.get( 'request_type_id', 'none' )
+ if request_type_id != 'none':
+ request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
+ else:
+ request_type = None
+ # user_id will not be 'none' if an admin user is submitting this request on behalf of another user
+ # and they selected that user's id from the user_id SelectField.
+ user_id = params.get( 'user_id', 'none' )
+ if user_id != 'none':
+ user = trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( user_id ) )
+ elif not is_admin:
+ user = trans.user
+ else:
+ user = None
+ if params.get( 'create_request_button', False ) or params.get( 'add_sample_button', False ):
+ name = util.restore_text( params.get( 'name', '' ) )
+ if is_admin and user_id == 'none':
+ message = 'Select the user on behalf of whom you are submitting this request.'
+ status = 'error'
+ elif not name:
+ message = 'Enter the name of the request.'
+ status = 'error'
+ else:
+ request = self.__save_request( trans, cntrller, **kwd )
+ message = 'The request has been created.'
+ if params.get( 'create_request_button', False ):
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_requests',
+ message=message ,
+ status='done' ) )
+ elif params.get( 'add_sample_button', False ):
+ return self.__add_sample( trans, cntrller, request, **kwd )
+ request_type_select_field = self.__build_request_type_id_select_field( trans, selected_value=request_type_id )
+ # Widgets to be rendered on the request form
+ widgets = []
+ if request_type is not None or status == 'error':
+ # Either the user selected a request_type or an error exists on the form.
+ if is_admin:
+ widgets.append( dict( label='Select user',
+ widget=self.__build_user_id_select_field( trans, selected_value=user_id ),
+ helptext='Submit the request on behalf of the selected user (Required)'))
+ widgets.append( dict( label='Name of the Experiment',
+ widget=TextField( 'name', 40, util.restore_text( params.get( 'name', '' ) ) ),
+ helptext='(Required)') )
+ widgets.append( dict( label='Description',
+ widget=TextField( 'desc', 40, util.restore_text( params.get( 'desc', '' ) )),
+ helptext='(Optional)') )
+ if request_type is not None:
+ widgets += request_type.request_form.get_widgets( user, **kwd )
+ # In case there is an error on the form, make sure to populate widget fields with anything the user
+ # may have already entered.
+ self.populate_widgets_from_kwd( trans, widgets, **kwd )
+ return trans.fill_template( '/requests/common/create_request.mako',
+ cntrller=cntrller,
+ request_type_select_field=request_type_select_field,
+ request_type_select_field_selected=request_type_id,
+ widgets=widgets,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_login( "edit sequencing requests" )
+ def edit_basic_request_info( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- if params.get('select_request_type', False) == 'True':
- return trans.fill_template( '/requests/common/new_request.mako',
- cntrller=cntrller,
- select_request_type=self.__select_request_type(trans, 'none'),
- widgets=[],
- message=message,
- status=status)
- elif params.get('create_request_button', False) == 'Save' \
- or params.get('create_request_samples_button', False) == 'Add samples':
- request_type = trans.sa_session.query( trans.app.model.RequestType ).get( int( params.select_request_type ) )
- if not util.restore_text(params.get('name', '')) \
- or util.restore_text(params.get('select_user', '')) == unicode('none'):
- message = 'Please enter the <b>Name</b> of the request and the <b>user</b> on behalf of whom this request will be submitted before saving this request'
- kwd['create'] = 'True'
- kwd['status'] = 'error'
- kwd['message'] = message
- kwd['create_request_button'] = None
- kwd['create_request_samples_button'] = None
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller=cntrller,
- action='new',
- **kwd) )
- request = self.__save_request(trans, None, **kwd)
- message = 'The new request named <b>%s</b> has been created' % request.name
- if params.get('create_request_button', False) == 'Save':
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- message=message ,
- status='done') )
- elif params.get('create_request_samples_button', False) == 'Add samples':
- new_kwd = {}
- new_kwd['id'] = trans.security.encode_id(request.id)
- new_kwd['operation'] = 'show'
- new_kwd['add_sample'] = True
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- message=message ,
- status='done',
- **new_kwd) )
- elif params.get('refresh', False) == 'true':
- return self.__show_request_form(trans, **kwd)
- def __select_request_type(self, trans, rtid):
- requesttype_list = trans.user.accessible_request_types(trans)
- rt_ids = ['none']
- for rt in requesttype_list:
- if not rt.deleted:
- rt_ids.append(str(rt.id))
- select_reqtype = SelectField('select_request_type',
- refresh_on_change=True,
- refresh_on_change_values=rt_ids[1:])
- if rtid == 'none':
- select_reqtype.add_option('Select one', 'none', selected=True)
- else:
- select_reqtype.add_option('Select one', 'none')
- for rt in requesttype_list:
- if not rt.deleted:
- if rtid == rt.id:
- select_reqtype.add_option(rt.name, rt.id, selected=True)
- else:
- select_reqtype.add_option(rt.name, rt.id)
- return select_reqtype
- def __show_request_form(self, trans, **kwd):
- params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ request_id = params.get( 'id', None )
try:
- request_type = trans.sa_session.query( trans.app.model.RequestType ).get( int( params.select_request_type ) )
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
- return trans.fill_template( '/requests/common/new_request.mako',
- cntrller=cntrller,
- select_request_type=self.__select_request_type(trans, 'none'),
- widgets=[],
- message=message,
- status=status)
- form_values = None
- select_request_type = self.__select_request_type(trans, request_type.id)
- # user
- if cntrller == 'requests_admin' and trans.user_is_admin():
- user_id = params.get( 'select_user', 'none' )
- try:
- user = trans.sa_session.query( trans.app.model.User ).get( int( user_id ) )
- except:
- user = None
- elif cntrller == 'requests':
- user = trans.user
- # list of widgets to be rendered on the request form
+ return invalid_id_redirect( trans, cntrller, request_id )
+ name = util.restore_text( params.get( 'name', '' ) )
+ desc = util.restore_text( params.get( 'desc', '' ) )
+ if params.get( 'edit_basic_request_info_button', False ) or params.get( 'edit_samples_button', False ):
+ if not name:
+ status = 'error'
+ message = 'Enter the name of the request'
+ else:
+ request = self.__save_request( trans, cntrller, request=request, **kwd )
+ message = 'The changes made to request (%s) have been saved.' % request.name
+ # Widgets to be rendered on the request form
widgets = []
- if cntrller == 'requests_admin' and trans.user_is_admin():
- widgets.append(dict(label='Select user',
- widget=self.__select_user(trans, user_id),
- helptext='The request would be submitted on behalf of this user (Required)'))
- widgets.append(dict(label='Name of the Experiment',
- widget=TextField('name', 40,
- util.restore_text( params.get( 'name', '' ) )),
- helptext='(Required)'))
- widgets.append(dict(label='Description',
- widget=TextField('desc', 40,
- util.restore_text( params.get( 'desc', '' ) )),
- helptext='(Optional)'))
- widgets = widgets + request_type.request_form.get_widgets( user, **kwd )
- return trans.fill_template( '/requests/common/new_request.mako',
+ widgets.append( dict( label='Name',
+ widget=TextField( 'name', 40, request.name ),
+ helptext='(Required)' ) )
+ widgets.append( dict( label='Description',
+ widget=TextField( 'desc', 40, request.desc ),
+ helptext='(Optional)' ) )
+ widgets = widgets + request.type.request_form.get_widgets( request.user, request.values.content, **kwd )
+ # In case there is an error on the form, make sure to populate widget fields with anything the user
+ # may have already entered.
+ self.populate_widgets_from_kwd( trans, widgets, **kwd )
+ return trans.fill_template( 'requests/common/edit_basic_request_info.mako',
cntrller=cntrller,
- select_request_type=select_request_type,
- request_type=request_type,
+ request_type=request.type,
+ request=request,
widgets=widgets,
message=message,
- status=status)
- def __select_user(self, trans, userid):
- user_list = trans.sa_session.query( trans.app.model.User )\
- .order_by( trans.app.model.User.email.asc() )
- user_ids = ['none']
- for user in user_list:
- if not user.deleted:
- user_ids.append(str(user.id))
- # gvk - 9/22/10: TODO: why does select_user require a refresh_on_change? Nothing in the
- # code is apparent as to why this is done.
- select_user = SelectField('select_user',
- refresh_on_change=True,
- refresh_on_change_values=user_ids[1:])
- if userid == 'none':
- select_user.add_option('Select one', 'none', selected=True)
+ status=status )
+ def __save_request( self, trans, cntrller, request=None, **kwd ):
+ """
+ Saves changes to an existing request, or creates a new
+ request if received request is None.
+ """
+ params = util.Params( kwd )
+ request_type_id = params.get( 'request_type_id', None )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ if request is None:
+ # We're creating a new request, so we need the associated request_type
+ request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
+ if is_admin:
+ # The admin user is creating a request on behalf of another user
+ user_id = params.get( 'user_id', '' )
+ user = trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( user_id ) )
+ else:
+ user = trans.user
else:
- select_user.add_option('Select one', 'none')
- for user in user_list:
- if not user.deleted:
- if userid == str(user.id):
- select_user.add_option(user.email, user.id, selected=True)
- else:
- select_user.add_option(user.email, user.id)
- return select_user
- def __save_request(self, trans, request, **kwd):
- '''
- This method saves a new request if request_id is None.
- '''
- params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- if request:
+ # We're saving changes to an existing request
user = request.user
request_type = request.type
- else:
- request_type = trans.sa_session.query( trans.app.model.RequestType ).get( int( params.select_request_type ) )
- if cntrller == 'requests_admin' and trans.user_is_admin():
- user = trans.sa_session.query( trans.app.model.User ).get( int( params.get( 'select_user', '' ) ) )
- elif cntrller == 'requests':
- user = trans.user
- name = util.restore_text(params.get('name', ''))
- desc = util.restore_text(params.get('desc', ''))
- notification = dict(email=[user.email], sample_states=[request_type.last_state().id], body='', subject='')
- # fields
+ name = util.restore_text( params.get( 'name', '' ) )
+ desc = util.restore_text( params.get( 'desc', '' ) )
+ notification = dict( email=[ user.email ], sample_states=[ request_type.state.id ], body='', subject='' )
values = []
- for index, field in enumerate(request_type.request_form.fields):
- if field['type'] == 'AddressField':
- value = util.restore_text(params.get('field_%i' % index, ''))
+ for index, field in enumerate( request_type.request_form.fields ):
+ field_type = field[ 'type' ]
+ field_value = params.get( 'field_%i' % index, '' )
+ if field[ 'type' ] == 'AddressField':
+ value = util.restore_text( field_value )
if value == 'new':
- # save this new address in the list of this user's addresses
- user_address = trans.app.model.UserAddress( user=user )
+ # Save this new address in the list of this user's addresses
+ user_address = trans.model.UserAddress( user=user )
self.save_widget_field( trans, user_address, index, **kwd )
trans.sa_session.refresh( user )
- values.append(int(user_address.id))
- elif value == unicode('none'):
- values.append('')
+ values.append( int( user_address.id ) )
+ elif value in [ '', 'none', 'None', None ]:
+ values.append( '' )
else:
- values.append(int(value))
- elif field['type'] == 'CheckboxField':
- values.append(CheckboxField.is_checked( params.get('field_%i' % index, '') ))
+ values.append( int( value ) )
+ elif field[ 'type' ] == 'CheckboxField':
+ values.append( CheckboxField.is_checked( field_value ))
else:
- values.append(util.restore_text(params.get('field_%i' % index, '')))
- form_values = trans.app.model.FormValues(request_type.request_form, values)
+ values.append( util.restore_text( field_value ) )
+ form_values = trans.model.FormValues( request_type.request_form, values )
trans.sa_session.add( form_values )
trans.sa_session.flush()
- if not request:
- request = trans.app.model.Request(name, desc, request_type,
- user, form_values, notification)
+ if request is None:
+ # We're creating a new request
+ request = trans.model.Request( name, desc, request_type, user, form_values, notification )
trans.sa_session.add( request )
trans.sa_session.flush()
trans.sa_session.refresh( request )
- # create an event with state 'New' for this new request
- if request.user.email is not trans.user:
- comments = "Request created by admin (%s) on behalf of %s." % (trans.user.email, request.user.email)
+ # Create an event with state 'New' for this new request
+ if request.user != trans.user:
+ sample_event_comment = "Request created by user %s for user %s." % ( trans.user.email, request.user.email )
else:
- comments = "Request created."
- event = trans.app.model.RequestEvent(request, request.states.NEW, comments)
+ sample_event_comment = "Request created."
+ event = trans.model.RequestEvent( request, request.states.NEW, sample_event_comment )
trans.sa_session.add( event )
trans.sa_session.flush()
else:
+ # We're saving changes to an existing request
request.name = name
request.desc = desc
request.type = request_type
@@ -239,1068 +307,1034 @@ class RequestsCommon( BaseController, Us
trans.sa_session.flush()
return request
@web.expose
- @web.require_login( "create/submit sequencing requests" )
- def email_settings(self, trans, **kwd):
+ @web.require_login( "submit sequencing requests" )
+ def submit_request( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ request_id = params.get( 'id', None )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = util.restore_text( params.get( 'status', 'done' ) )
try:
- request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id( params.get( 'id', None ) ) )
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
+ return invalid_id_redirect( trans, cntrller, request_id )
+ ok = True
+ if not request.samples:
+ message = 'Add at least 1 sample to this request before submitting.'
+ ok = False
+ if ok:
+ message = self.__validate_request( trans, cntrller, request )
+ if message or not ok:
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_basic_request_info',
+ cntrller=cntrller,
+ id = request_id,
status='error',
- message="Invalid request ID") )
- email_user = CheckboxField.is_checked( params.get('email_user', '') )
- email_additional = params.get('email_additional', '').split('\r\n')
- if email_user or email_additional:
- emails = []
- if email_user:
- emails.append(request.user.email)
- for e in email_additional:
- emails.append(util.restore_text(e))
- # check if valid email addresses
- invalid = ''
- for e in emails:
- if len( e ) == 0 or "@" not in e or "." not in e:
- invalid = e
- break
- if invalid:
- message = "<b>%s</b> is not a valid email address." % invalid
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller=cntrller,
- action='edit',
- show=True,
- id=trans.security.encode_id(request.id),
- message=message ,
- status='error' ) )
- else:
- email_states = []
- for i, ss in enumerate(request.type.states):
- if CheckboxField.is_checked( params.get('sample_state_%i' % ss.id, '') ):
- email_states.append(ss.id)
- request.notification = dict(email=emails, sample_states=email_states,
- body='', subject='')
- trans.sa_session.flush()
- trans.sa_session.refresh( request )
- message = 'The changes made to the sequencing request has been saved'
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller=cntrller,
- action='show',
- id=trans.security.encode_id(request.id),
- message=message ,
- status='done') )
- @web.expose
- @web.require_login( "create/submit sequencing requests" )
- def edit(self, trans, **kwd):
- params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- try:
- request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id( params.get( 'id', None ) ) )
- except:
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- status='error',
- message="Invalid request ID") )
- if params.get('show', False) == 'True':
- return self.__edit_request(trans, **kwd)
- elif params.get('save_changes_request_button', False) == 'Save' \
- or params.get('edit_samples_button', False) == 'Edit samples':
- if not util.restore_text(params.get('name', '')):
- message = 'Please enter the <b>Name</b> of the request'
- kwd['status'] = 'error'
- kwd['message'] = message
- kwd['show'] = 'True'
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller=cntrller,
- action='edit',
- **kwd) )
- request = self.__save_request(trans, request, **kwd)
- message = 'The changes made to the request named %s has been saved' % request.name
- if params.get('save_changes_request_button', False) == 'Save':
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- cntrller=cntrller,
- action='list',
- message=message ,
- status='done') )
- elif params.get('edit_samples_button', False) == 'Edit samples':
- new_kwd = {}
- new_kwd['id'] = request.id
- new_kwd['edit_samples_button'] = 'Edit samples'
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- cntrller=cntrller,
- action='show',
- message=message ,
- status='done',
- **new_kwd) )
- elif params.get('refresh', False) == 'true':
- return self.__edit_request(trans, **kwd)
- def __edit_request(self, trans, **kwd):
- try:
- request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id(kwd['id']) )
- except:
- message = "Invalid request ID"
- log.warn( message )
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- cntrller=cntrller,
- action='list',
- status='error',
- message=message) )
- params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- #select_request_type = self.__select_request_type(trans, request.type.id)
- # list of widgets to be rendered on the request form
- widgets = []
- if util.restore_text( params.get( 'name', '' ) ):
- name = util.restore_text( params.get( 'name', '' ) )
+ message=message ) )
+ # Change the request state to 'Submitted'
+ if request.user.email is not trans.user:
+ sample_event_comment = "Request submitted by %s on behalf of %s." % ( trans.user.email, request.user.email )
else:
- name = request.name
- widgets.append(dict(label='Name',
- widget=TextField('name', 40, name),
- helptext='(Required)'))
- if util.restore_text( params.get( 'desc', '' ) ):
- desc = util.restore_text( params.get( 'desc', '' ) )
- else:
- desc = request.desc
- widgets.append(dict(label='Description',
- widget=TextField('desc', 40, desc),
- helptext='(Optional)'))
- widgets = widgets + request.type.request_form.get_widgets( request.user, request.values.content, **kwd )
- return trans.fill_template( 'requests/common/edit_request.mako',
- cntrller=cntrller,
- #select_request_type=select_request_type,
- request_type=request.type,
- request=request,
- widgets=widgets,
- message=message,
- status=status)
- def __validate(self, trans, cntrller, request):
- '''
- Validates the request entered by the user
- '''
- if not request.samples:
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='show',
- message='Please add one or more samples to this request before submitting.',
- status='error',
- id=trans.security.encode_id(request.id)) )
- empty_fields = []
- # check rest of the fields of the form
- for index, field in enumerate(request.type.request_form.fields):
- if field['required'] == 'required' and request.values.content[index] in ['', None]:
- empty_fields.append(field['label'])
- if empty_fields:
- message = 'Fill the following fields of the request <b>%s</b> before submitting<br/>' % request.name
- for ef in empty_fields:
- message = message + '<b>' +ef + '</b><br/>'
- return message
- return None
- @web.expose
- @web.require_login( "create/submit sequencing requests" )
- def submit(self, trans, **kwd):
- params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- try:
- request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id(kwd['id']) )
- except:
- message = "Invalid request ID"
- log.warn( message )
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- status='error',
- message=message,
- **kwd) )
- message = self.__validate(trans, cntrller, request)
- if message:
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='edit',
- status = 'error',
- message=message,
- id=trans.security.encode_id(request.id) ) )
- # change the request state to 'Submitted'
- if request.user.email is not trans.user:
- comments = "Request submitted by admin (%s) on behalf of %s." % (trans.user.email, request.user.email)
- else:
- comments = ""
- event = trans.app.model.RequestEvent(request, request.states.SUBMITTED, comments)
+ sample_event_comment = ""
+ event = trans.model.RequestEvent( request, request.states.SUBMITTED, sample_event_comment )
trans.sa_session.add( event )
- trans.sa_session.flush()
# change the state of each of the samples of thus request
new_state = request.type.states[0]
- for s in request.samples:
- event = trans.app.model.SampleEvent(s, new_state, 'Samples created.')
+ for sample in request.samples:
+ event = trans.model.SampleEvent( sample, new_state, 'Samples created.' )
trans.sa_session.add( event )
trans.sa_session.add( request )
trans.sa_session.flush()
- request.send_email_notification(trans, new_state)
+ request.send_email_notification( trans, new_state )
+ message = 'The request has been submitted.'
return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- id=trans.security.encode_id(request.id),
- status='done',
- message='The request <b>%s</b> has been submitted.' % request.name
- ) )
+ action='browse_requests',
+ cntrller=cntrller,
+ id=request_id,
+ status=status,
+ message=message ) )
@web.expose
- @web.require_login( "create/submit sequencing requests" )
- def delete(self, trans, **kwd):
+ @web.require_login( "sequencing request page" )
+ def manage_request( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- id_list = util.listify( kwd['id'] )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ request_id = params.get( 'id', None )
+ try:
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
+ except:
+ return invalid_id_redirect( trans, cntrller, request_id )
+ sample_state_id = params.get( 'sample_state_id', None )
+ # Get the user entered sample information
+ current_samples, managing_samples, libraries = self.__get_sample_info( trans, request, **kwd )
+ selected_samples = self.__get_selected_samples( trans, request, **kwd )
+ selected_value = params.get( 'sample_operation', 'none' )
+ if selected_value != 'none' and not selected_samples:
+ message = 'Select at least one sample before selecting an operation.'
+ status = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
+ cntrller=cntrller,
+ id=request_id,
+ status=status,
+ message=message ) )
+ sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, selected_value )
+ sample_operation_selected_value = sample_operation_select_field.get_selected( return_value=True )
+ if params.get( 'import_samples_button', False ):
+ # Import sample field values from a csv file
+ return self.__import_samples( trans, cntrller, request, current_samples, libraries, **kwd )
+ elif params.get( 'add_sample_button', False ):
+ return self.__add_sample( trans, cntrller, request, **kwd )
+ elif params.get( 'save_samples_button', False ):
+ return self.__save_sample( trans, cntrller, request, current_samples, **kwd )
+ elif params.get( 'edit_samples_button', False ):
+ managing_samples = True
+ elif params.get( 'cancel_changes_button', False ):
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
+ cntrller=cntrller,
+ id=request_id ) )
+ pass
+ elif params.get( 'change_state_button', False ):
+ sample_event_comment = util.restore_text( params.get( 'sample_event_comment', '' ) )
+ new_state = trans.sa_session.query( trans.model.SampleState ).get( trans.security.decode_id( sample_state_id ) )
+ for sample_id in selected_samples:
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
+ event = trans.model.SampleEvent( sample, new_state, sample_event_comment )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ cntrller=cntrller,
+ action='update_request_state',
+ request_id=request_id ) )
+ elif params.get( 'cancel_change_state_button', False ):
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
+ cntrller=cntrller,
+ id=request_id ) )
+ elif params.get( 'change_lib_button', False ):
+ library_id = params.get( 'sample_0_library_id', None )
+ try:
+ library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( library_id ) )
+ except:
+ invalid_id_redirect( trans, cntrller, library_id )
+ folder_id = params.get( 'sample_0_folder_id', None )
+ try:
+ folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) )
+ except:
+ invalid_id_redirect( trans, cntrller, folder_id )
+ for sample_id in selected_samples:
+ sample = trans.sa_session.query( trans.model.Sample ).get( sample_id )
+ sample.library = library
+ sample.folder = folder
+ trans.sa_session.add( sample )
+ trans.sa_session.flush()
+ trans.sa_session.refresh( request )
+ message = 'Changes made to the selected samples have been saved. '
+ elif params.get( 'cancel_change_lib_button', False ):
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
+ cntrller=cntrller,
+ id=trans.security.encode_id( request.id ) ) )
+ request_widgets = self.__get_request_widgets( trans, request.id )
+ sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
+ libraries_select_field, folders_select_field = self.__build_library_and_folder_select_fields( trans,
+ request.user,
+ 0,
+ libraries,
+ None,
+ **kwd )
+ # Build the sample_state_id_select_field SelectField
+ sample_state_id_select_field = self.__build_sample_state_id_select_field( trans, request, sample_state_id )
+ return trans.fill_template( '/requests/common/manage_request.mako',
+ cntrller=cntrller,
+ request=request,
+ selected_samples=selected_samples,
+ request_widgets=request_widgets,
+ current_samples=current_samples,
+ sample_copy=sample_copy,
+ libraries=libraries,
+ sample_operation_select_field=sample_operation_select_field,
+ libraries_select_field=libraries_select_field,
+ folders_select_field=folders_select_field,
+ sample_state_id_select_field=sample_state_id_select_field,
+ managing_samples=managing_samples,
+ status=status,
+ message=message )
+ @web.expose
+ @web.require_login( "delete sequencing requests" )
+ def delete_request( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ id_list = util.listify( kwd.get( 'id', '' ) )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = util.restore_text( params.get( 'status', 'done' ) )
+ num_deleted = 0
for id in id_list:
+ ok_for_now = True
try:
- request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id(id) )
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( id ) )
except:
- message = "Invalid request ID"
- log.warn( message )
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- status='error',
- message=message,
- **kwd) )
- request.deleted = True
- trans.sa_session.add( request )
- # delete all the samples belonging to this request
- for s in request.samples:
- s.deleted = True
- trans.sa_session.add( s )
- trans.sa_session.flush()
- message = '%i request(s) has been deleted.' % len(id_list)
- status = 'done'
+ message += "Invalid request ID (%s). " % str( id )
+ status = 'error'
+ ok_for_now = False
+ if ok_for_now:
+ request.deleted = True
+ trans.sa_session.add( request )
+ # delete all the samples belonging to this request
+ for s in request.samples:
+ s.deleted = True
+ trans.sa_session.add( s )
+ trans.sa_session.flush()
+ num_deleted += 1
+ message += '%i requests have been deleted.' % num_deleted
return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
+ action='browse_requests',
status=status,
- message=message) )
+ message=message ) )
@web.expose
- @web.require_login( "create/submit sequencing requests" )
- def undelete(self, trans, **kwd):
+ @web.require_login( "undelete sequencing requests" )
+ def undelete_request( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- id_list = util.listify( kwd['id'] )
+ id_list = util.listify( kwd.get( 'id', '' ) )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = util.restore_text( params.get( 'status', 'done' ) )
+ num_undeleted = 0
for id in id_list:
+ ok_for_now = True
try:
- request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id(id) )
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( id ) )
except:
- message = "Invalid request ID"
- log.warn( message )
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- status='error',
- message=message,
- **kwd) )
- request.deleted = False
- trans.sa_session.add( request )
- # undelete all the samples belonging to this request
- for s in request.samples:
- s.deleted = False
- trans.sa_session.add( s )
- trans.sa_session.flush()
+ message += "Invalid request ID (%s). " % str( id )
+ status = 'error'
+ ok_for_now = False
+ if ok_for_now:
+ request.deleted = False
+ trans.sa_session.add( request )
+ # undelete all the samples belonging to this request
+ for s in request.samples:
+ s.deleted = False
+ trans.sa_session.add( s )
+ trans.sa_session.flush()
+ num_undeleted += 1
+ message += '%i requests have been undeleted.' % num_deleted
return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- status='done',
- message='%i request(s) has been undeleted.' % len(id_list) ) )
+ action='browse_requests',
+ status=status,
+ message=message ) )
@web.expose
- @web.require_login( "create/submit sequencing requests" )
- def events(self, trans, **kwd):
+ @web.require_login( "sequencing request events" )
+ def request_events( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
- cntrller = params.get( 'cntrller', 'requests' )
+ request_id = params.get( 'id', None )
try:
- request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id(kwd['id']) )
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
- message = "Invalid request ID"
- log.warn( message )
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- status='error',
- message=message) )
+ return invalid_id_redirect( trans, cntrller, request_id )
events_list = []
- all_events = request.events
- for event in all_events:
- events_list.append((event.state, time_ago(event.update_time), event.comment))
+ for event in request.events:
+ events_list.append( ( event.state, time_ago( event.update_time ), event.comment ) )
return trans.fill_template( '/requests/common/events.mako',
cntrller=cntrller,
- events_list=events_list, request=request)
+ events_list=events_list,
+ request=request )
@web.expose
- @web.require_admin
- def update_request_state( self, trans, **kwd ):
+ @web.require_login( "edit email notification settings" )
+ def edit_email_settings( self, trans, cntrller, **kwd ):
+ """
+ Allow for changing the email notification settings where email is sent to a list of users
+ whenever the request state changes to one selected for notification.
+ """
params = util.Params( kwd )
- cntrller = params.get( 'cntrller', 'requests' )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ request_id = params.get( 'id', None )
try:
- request = trans.sa_session.query( trans.app.model.Request ).get( int( params.get( 'request_id', None ) ) )
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='list',
- status='error',
- message="Invalid request ID",
- **kwd) )
- # check if all the samples of the current request are in the sample state
- common_state = request.common_state()
+ return invalid_id_redirect( trans, cntrller, request_id )
+ email_address = CheckboxField.is_checked( params.get( 'email_address', '' ) )
+ additional_email_addresses = params.get( 'additional_email_addresses', '' )
+ # Get the list of checked sample state CheckBoxFields
+ checked_sample_states = []
+ for index, sample_state in enumerate( request.type.states ):
+ if CheckboxField.is_checked( params.get( 'sample_state_%i' % sample_state.id, '' ) ):
+ checked_sample_states.append( sample_state.id )
+ if additional_email_addresses:
+ additional_email_addresses = additional_email_addresses.split( '\r\n' )
+ if email_address or additional_email_addresses:
+ # The user added 1 or more email addresses
+ email_addresses = []
+ if email_address:
+ email_addresses.append( request.user.email )
+ for email_address in additional_email_addresses:
+ email_addresses.append( util.restore_text( email_address ) )
+ # Make sure email addresses are valid
+ err_msg = ''
+ for email_address in additional_email_addresses:
+ err_msg += self.__validate_email( email_address )
+ if err_msg:
+ status = 'error'
+ message += err_msg
+ else:
+ request.notification = dict( email=email_addresses,
+ sample_states=checked_sample_states,
+ body='',
+ subject='' )
+ else:
+ # The user may have eliminated email addresses that were previously set
+ request.notification = None
+ if checked_sample_states:
+ message = 'All sample states have been unchecked since no email addresses have been selected or entered. '
+ trans.sa_session.add( request )
+ trans.sa_session.flush()
+ trans.sa_session.refresh( request )
+ message += 'The changes made to the email notification settings have been saved.'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_basic_request_info',
+ cntrller=cntrller,
+ id=request_id,
+ message=message ,
+ status=status ) )
+ @web.expose
+ @web.require_login( "update sequencing request state" )
+ def update_request_state( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ message = params.get( 'message', '' )
+ status = params.get( 'status', 'done' )
+ request_id = params.get( 'request_id', None )
+ try:
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
+ except:
+ return invalid_id_redirect( trans, cntrller, request_id )
+ # Make sure all the samples of the current request have the same state
+ common_state = request.samples_have_common_state
if not common_state:
- # if the current request state is complete and one of its samples moved from
+ # If the current request state is complete and one of its samples moved from
# the final sample state, then move the request state to In-progress
- if request.complete():
- status='done'
- message = "One or more samples' state moved from the final sample state. Now request in '%s' state" % request.states.SUBMITTED
- event = trans.app.model.RequestEvent(request, request.states.SUBMITTED, message)
+ if request.is_complete:
+ message = "At least 1 sample state moved from the final sample state, so now the request is in the '%s' state" % request.states.SUBMITTED
+ event = trans.model.RequestEvent( request, request.states.SUBMITTED, message )
trans.sa_session.add( event )
trans.sa_session.flush()
- else:
- message = ''
- status = 'ok'
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='show',
- id=trans.security.encode_id(request.id),
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
+ cntrller=cntrller,
+ id=request_id,
status=status,
message=message ) )
final_state = False
- if common_state.id == request.type.last_state().id:
+ request_type_state = request.type.state
+ if common_state.id == request_type_state.id:
# since all the samples are in the final state, change the request state to 'Complete'
- comments = "All samples of this request are in the last sample state (%s). " % request.type.last_state().name
+ comments = "All samples of this request are in the last sample state (%s). " % request_type_state.name
state = request.states.COMPLETE
final_state = True
else:
comments = "All samples are in %s state. " % common_state.name
state = request.states.SUBMITTED
- event = trans.app.model.RequestEvent(request, state, comments)
+ event = trans.model.RequestEvent(request, state, comments)
trans.sa_session.add( event )
trans.sa_session.flush()
# check if an email notification is configured to be sent when the samples
# are in this state
- retval = request.send_email_notification(trans, common_state, final_state)
+ retval = request.send_email_notification( trans, common_state, final_state )
if retval:
message = comments + retval
else:
message = comments
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='show',
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
+ cntrller=cntrller,
id=trans.security.encode_id(request.id),
status='done',
message=message ) )
- @web.expose
- @web.require_login( "create/submit sequencing requests" )
- def show(self, trans, **kwd):
+ def __save_sample( self, trans, cntrller, request, current_samples, **kwd ):
+ # Save all the new/unsaved samples entered by the user
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
status = params.get( 'status', 'done' )
- add_sample = params.get('add_sample', False)
+ managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ selected_value = params.get( 'sample_operation', 'none' )
+ # Check for duplicate sample names
+ message = ''
+ for index in range( len( current_samples ) - len( request.samples ) ):
+ sample_index = index + len( request.samples )
+ current_sample = current_samples[ sample_index ]
+ sample_name = current_sample[ 'name' ]
+ if not sample_name.strip():
+ message = 'Enter the name of sample number %i' % sample_index
+ break
+ count = 0
+ for i in range( len( current_samples ) ):
+ if sample_name == current_samples[ i ][ 'name' ]:
+ count += 1
+ if count > 1:
+ message = "This request has %i samples with the name (%s). Samples belonging to a request must have unique names." % ( count, sample_name )
+ break
+ if message:
+ selected_samples = self.__get_selected_samples( trans, request, **kwd )
+ request_widgets = self.__get_request_widgets( trans, request.id )
+ sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
+ sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, selected_value )
+ status = 'error'
+ return trans.fill_template( '/requests/common/manage_request.mako',
+ cntrller=cntrller,
+ request=request,
+ selected_samples=selected_samples,
+ request_widgets=request_widgets,
+ current_samples=current_samples,
+ sample_copy=sample_copy,
+ managing_samples=managing_samples,
+ sample_operation_select_field=sample_operation_select_field,
+ status=status,
+ message=message )
+ if not managing_samples:
+ for index in range( len( current_samples ) - len( request.samples ) ):
+ sample_index = len( request.samples )
+ current_sample = current_samples[ sample_index ]
+ form_values = trans.model.FormValues( request.type.sample_form, current_sample[ 'field_values' ] )
+ trans.sa_session.add( form_values )
+ trans.sa_session.flush()
+ s = trans.model.Sample( current_sample[ 'name' ],
+ '',
+ request,
+ form_values,
+ current_sample[ 'barcode' ],
+ current_sample[ 'library' ],
+ current_sample[ 'folder' ] )
+ trans.sa_session.add( s )
+ trans.sa_session.flush()
+ else:
+ message = 'Changes made to the samples are saved. '
+ for sample_index in range( len( current_samples ) ):
+ sample = request.samples[ sample_index ]
+ current_sample = current_samples[ sample_index ]
+ sample.name = current_sample[ 'name' ]
+ sample.library = current_sample[ 'library' ]
+ sample.folder = current_sample[ 'folder' ]
+ if request.is_submitted:
+ bc_message = self.__validate_barcode( trans, sample, current_sample[ 'barcode' ] )
+ if bc_message:
+ status = 'error'
+ message += bc_message
+ else:
+ if not sample.bar_code:
+ # If this is a 'new' (still in its first state) sample
+ # change the state to the next
+ if sample.state.id == request.type.states[0].id:
+ event = trans.model.SampleEvent( sample,
+ request.type.states[1],
+ 'Sample added to the system' )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ # Now check if all the samples' barcode has been entered.
+ # If yes then send notification email if configured
+ common_state = request.samples_have_common_state
+ if common_state:
+ if common_state.id == request.type.states[1].id:
+ event = trans.model.RequestEvent( request,
+ request.states.SUBMITTED,
+ "All samples are in %s state." % common_state.name )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ request.send_email_notification( trans, request.type.states[1] )
+ sample.bar_code = current_samples[sample_index]['barcode']
+ trans.sa_session.add( sample )
+ trans.sa_session.flush()
+ form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
+ form_values.content = current_sample[ 'field_values' ]
+ trans.sa_session.add( form_values )
+ trans.sa_session.flush()
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
+ cntrller=cntrller,
+ id=trans.security.encode_id( request.id ),
+ status=status,
+ message=message ) )
+ @web.expose
+ @web.require_login( "find samples" )
+ def find_samples( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ samples_list = []
+ results = ''
+ if params.get( 'find_samples_button', False ):
+ search_string = kwd.get( 'search_box', '' )
+ search_type = params.get( 'search_type', '' )
+ request_states = util.listify( params.get( 'request_states', '' ) )
+ samples = []
+ if search_type == 'barcode':
+ samples = trans.sa_session.query( trans.model.Sample ) \
+ .filter( and_( trans.model.Sample.table.c.deleted==False,
+ func.lower( trans.model.Sample.table.c.bar_code ).like( "%" + search_string.lower() + "%" ) ) ) \
+ .order_by( trans.model.Sample.table.c.create_time.desc() )
+ elif search_type == 'sample name':
+ samples = trans.sa_session.query( trans.model.Sample ) \
+ .filter( and_( trans.model.Sample.table.c.deleted==False,
+ func.lower( trans.model.Sample.table.c.name ).like( "%" + search_string.lower() + "%" ) ) ) \
+ .order_by( trans.model.Sample.table.c.create_time.desc() )
+ elif search_type == 'dataset':
+ samples = trans.sa_session.query( trans.model.Sample ) \
+ .filter( and_( trans.model.Sample.table.c.deleted==False,
+ trans.model.SampleDataset.table.c.sample_id==trans.model.Sample.table.c.id,
+ func.lower( trans.model.SampleDataset.table.c.name ).like( "%" + search_string.lower() + "%" ) ) ) \
+ .order_by( trans.model.Sample.table.c.create_time.desc() )
+ if is_admin:
+ for s in samples:
+ if not s.request.deleted and s.request.state in request_states:
+ samples_list.append( s )
+ else:
+ for s in samples:
+ if s.request.user.id == trans.user.id and s.request.state in request_states and not s.request.deleted:
+ samples_list.append( s )
+ results = 'There are %i samples matching the search parameters.' % len( samples_list )
+ # Build the request_states SelectField
+ selected_value = kwd.get( 'request_states', trans.model.Request.states.SUBMITTED )
+ states = [ v for k, v in trans.model.Request.states.items() ]
+ request_states = build_select_field( trans,
+ states,
+ 'self',
+ 'request_states',
+ selected_value=selected_value,
+ refresh_on_change=False,
+ multiple=True,
+ display='checkboxes' )
+ # Build the search_type SelectField
+ selected_value = kwd.get( 'search_type', 'sample name' )
+ types = [ 'sample name', 'barcode', 'dataset' ]
+ search_type = build_select_field( trans, types, 'self', 'search_type', selected_value=selected_value, refresh_on_change=False )
+ # Build the search_box TextField
+ search_box = TextField( 'search_box', 50, kwd.get('search_box', '' ) )
+ return trans.fill_template( '/requests/common/find_samples.mako',
+ cntrller=cntrller,
+ request_states=request_states,
+ samples=samples_list,
+ search_type=search_type,
+ results=results,
+ search_box=search_box )
+ @web.expose
+ @web.require_login( "sample events" )
+ def sample_events( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ status = params.get( 'status', 'done' )
+ message = util.restore_text( params.get( 'message', '' ) )
+ sample_id = params.get( 'sample_id', None )
try:
- request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id(kwd['id']) )
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
except:
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- status='error',
- message="Invalid request ID") )
- # get all data libraries accessible to this user
+ return invalid_id_redirect( trans, cntrller, sample_id )
+ events_list = []
+ for event in sample.events:
+ events_list.append( ( event.state.name,
+ event.state.desc,
+ time_ago( event.update_time ),
+ event.comment ) )
+ return trans.fill_template( '/requests/common/sample_events.mako',
+ cntrller=cntrller,
+ events_list=events_list,
+ sample=sample )
+ def __add_sample( self, trans, cntrller, request, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ # Get the widgets for rendering the request form
+ request_widgets = self.__get_request_widgets( trans, request.id )
+ current_samples, managing_samples, libraries = self.__get_sample_info( trans, request, **kwd )
+ if not current_samples:
+ # Form field names are zero-based.
+ sample_index = 0
+ else:
+ sample_index = len( current_samples )
+ if params.get( 'add_sample_button', False ):
+ num_samples_to_add = int( params.get( 'num_sample_to_copy', 1 ) )
+ # See if the user has selected a sample to copy.
+ copy_sample_index = int( params.get( 'copy_sample_index', -1 ) )
+ for index in range( num_samples_to_add ):
+ if copy_sample_index != -1:
+ # The user has selected a sample to copy.
+ library_id = current_samples[ copy_sample_index][ 'library_select_field' ].get_selected( return_value=True )
+ folder_id = current_samples[ copy_sample_index ][ 'folder_select_field' ].get_selected( return_value=True )
+ name = current_samples[ copy_sample_index ][ 'name' ] + '_%i' % ( index )
+ library_id = 'none'
+ folder_id = 'none'
+ field_values = [ val for val in current_samples[ copy_sample_index ][ 'field_values' ] ]
+ else:
+ # The user has not selected a sample to copy (may just be adding a sample).
+ library_id = None
+ folder_id = None
+ name = 'Sample_%i' % sample_index
+ field_values = [ '' for field in request.type.sample_form.fields ]
+ # Build the library_select_field and folder_select_field for the new sample being added.
+ library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
+ user=request.user,
+ sample_index=sample_index,
+ libraries=libraries,
+ sample=None,
+ library_id=library_id,
+ folder_id=folder_id,
+ **kwd )
+ # Append the new sample to the current list of samples for the request
+ current_samples.append( dict( name=name,
+ barcode='',
+ library=None,
+ library_id=library_id,
+ folder=None,
+ folder_id=folder_id,
+ field_values=field_values,
+ library_select_field=library_select_field,
+ folder_select_field=folder_select_field ) )
+ selected_samples = self.__get_selected_samples( trans, request, **kwd )
+ selected_value = params.get( 'sample_operation', 'none' )
+ sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, selected_value )
+ sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
+ return trans.fill_template( '/requests/common/manage_request.mako',
+ cntrller=cntrller,
+ request=request,
+ selected_samples=selected_samples,
+ request_widgets=request_widgets,
+ current_samples=current_samples,
+ sample_operation_select_field=sample_operation_select_field,
+ sample_copy=sample_copy,
+ managing_samples=managing_samples,
+ message=message,
+ status=status )
+ def __get_sample_info( self, trans, request, **kwd ):
+ """
+ Retrieves all user entered sample information and returns a
+ list of all the samples and their field values.
+ """
+ params = util.Params( kwd )
+ managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
+ # Bet all data libraries accessible to this user
libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
+ # Build the list of widgets which will be used to render each sample row on the request page
current_samples = []
- for i, s in enumerate(request.samples):
- lib_widget, folder_widget = self.__library_widgets(trans, request.user, i, libraries, s, **kwd)
- current_samples.append(dict(name=s.name,
- barcode=s.bar_code,
- library=s.library,
- folder=s.folder,
- field_values=s.values.content,
- lib_widget=lib_widget,
- folder_widget=folder_widget))
- if add_sample:
- lib_widget, folder_widget = self.__library_widgets(trans, request.user,
- len(current_samples)+1,
- libraries, None, **kwd)
- current_samples.append(dict(name='Sample_%i' % (len(current_samples)+1),
- barcode='',
- library=None,
- folder=None,
- field_values=['' for field in request.type.sample_form.fields],
- lib_widget=lib_widget,
- folder_widget=folder_widget))
- bulk_lib_ops = self.__library_widgets(trans, request.user, 0, libraries, None, **kwd)
- return trans.fill_template( '/requests/common/show_request.mako',
- cntrller=cntrller,
- request=request, selected_samples=[],
- request_details=self.request_details(trans, request.id),
- current_samples=current_samples,
- sample_ops=self.__sample_operation_selectbox(trans, request, **kwd),
- sample_copy=self.__copy_sample(current_samples),
- details='hide', edit_mode=util.restore_text( params.get( 'edit_mode', 'False' ) ),
- message=message, status=status, bulk_lib_ops=bulk_lib_ops )
-
- def __update_samples(self, trans, request, **kwd):
- '''
- This method retrieves all the user entered sample information and
- returns an list of all the samples and their field values
- '''
- params = util.Params( kwd )
- details = params.get( 'details', 'hide' )
- edit_mode = params.get( 'edit_mode', 'False' )
- # get all data libraries accessible to this user
- libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
-
- current_samples = []
- for i, s in enumerate(request.samples):
- lib_widget, folder_widget = self.__library_widgets(trans, request.user, i, libraries, s, **kwd)
- current_samples.append(dict(name=s.name,
- barcode=s.bar_code,
- library=s.library,
- folder=s.folder,
- field_values=s.values.content,
- lib_widget=lib_widget,
- folder_widget=folder_widget))
- if edit_mode == 'False':
- sample_index = len(request.samples)
+ for index, sample in enumerate( request.samples ):
+ library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
+ request.user,
+ index,
+ libraries,
+ sample,
+ **kwd )
+ current_samples.append( dict( name=sample.name,
+ barcode=sample.bar_code,
+ library=sample.library,
+ folder=sample.folder,
+ field_values=sample.values.content,
+ library_select_field=library_select_field,
+ folder_select_field=folder_select_field ) )
+ if not managing_samples:
+ sample_index = len( request.samples )
else:
sample_index = 0
while True:
- lib_id = None
- folder_id = None
- if params.get( 'sample_%i_name' % sample_index, '' ):
- # data library
+ library_id = params.get( 'sample_%i_library_id' % sample_index, None )
+ folder_id = params.get( 'sample_%i_folder_id' % sample_index, None )
+ if params.get( 'sample_%i_name' % sample_index, False ):
+ # Data library
try:
- library = trans.sa_session.query( trans.app.model.Library ).get( int( params.get( 'sample_%i_library_id' % sample_index, None ) ) )
- lib_id = library.id
+ library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( library_id ) )
+ #library_id = library.id
except:
library = None
- # folder
- try:
- folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( int( params.get( 'sample_%i_folder_id' % sample_index, None ) ) )
- folder_id = folder.id
- except:
- if library:
- folder = library.root_folder
- else:
- folder = None
+ if library is not None:
+ # Folder
+ try:
+ folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) )
+ #folder_id = folder.id
+ except:
+ if library:
+ folder = library.root_folder
+ else:
+ folder = None
+ else:
+ folder = None
sample_info = dict( name=util.restore_text( params.get( 'sample_%i_name' % sample_index, '' ) ),
barcode=util.restore_text( params.get( 'sample_%i_barcode' % sample_index, '' ) ),
library=library,
folder=folder)
- sample_info['field_values'] = []
- for field_index in range(len(request.type.sample_form.fields)):
- sample_info['field_values'].append(util.restore_text( params.get( 'sample_%i_field_%i' % (sample_index, field_index), '' ) ))
- if edit_mode == 'False':
- sample_info['lib_widget'], sample_info['folder_widget'] = self.__library_widgets(trans, request.user,
- sample_index, libraries,
- None, lib_id, folder_id, **kwd)
- current_samples.append(sample_info)
+ sample_info[ 'field_values' ] = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ sample_info[ 'field_values' ].append( util.restore_text( params.get( 'sample_%i_field_%i' % ( sample_index, field_index ), '' ) ) )
+ if not managing_samples:
+ sample_info[ 'library_select_field' ], sample_info[ 'folder_select_field' ] = self.__build_library_and_folder_select_fields( trans,
+ request.user,
+ sample_index,
+ libraries,
+ None,
+ library_id,
+ folder_id,
+ **kwd )
+ current_samples.append( sample_info )
else:
- sample_info['lib_widget'], sample_info['folder_widget'] = self.__library_widgets(trans,
- request.user,
- sample_index,
- libraries,
- request.samples[sample_index],
- **kwd)
- current_samples[sample_index] = sample_info
- sample_index = sample_index + 1
+ sample_info[ 'library_select_field' ], sample_info[ 'folder_select_field' ] = self.__build_library_and_folder_select_fields( trans,
+ request.user,
+ sample_index,
+ libraries,
+ request.samples[ sample_index ],
+ **kwd )
+ current_samples[ sample_index ] = sample_info
+ sample_index += 1
else:
break
- return current_samples, details, edit_mode, libraries
- def __library_widgets(self, trans, user, sample_index, libraries, sample=None, lib_id=None, folder_id=None, **kwd):
- '''
- This method creates the data library & folder selectbox for creating &
- editing samples. First we get a list of all the libraries accessible to
- the current user and display it in a selectbox. If the user has selected an
- existing library then display all the accessible sub folders of the selected
- data library.
- '''
+ return current_samples, managing_samples, libraries
+ @web.expose
+ @web.require_login( "delete sample from sequencing request" )
+ def delete_sample( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
- # data library selectbox
- if not lib_id:
- lib_id = params.get( "sample_%i_library_id" % sample_index, 'none' )
- selected_lib = None
- if sample and lib_id == 'none':
- if sample.library:
- lib_id = str(sample.library.id)
- selected_lib = sample.library
- # create data library selectbox with refresh on change enabled
- lib_id_list = ['new'] + [str(lib.id) for lib in libraries.keys()]
- lib_widget = SelectField( "sample_%i_library_id" % sample_index,
- refresh_on_change=True,
- refresh_on_change_values=lib_id_list )
- # fill up the options in the Library selectbox
- # first option 'none' is the value for "Select one" option
- if lib_id == 'none':
- lib_widget.add_option('Select one', 'none', selected=True)
+ status = params.get( 'status', 'done' )
+ message = util.restore_text( params.get( 'message', '' ) )
+ request_id = params.get( 'request_id', None )
+ try:
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
+ except:
+ return invalid_id_redirect( trans, cntrller, request_id )
+ current_samples, managing_samples, libraries = self.__get_sample_info( trans, request, **kwd )
+ sample_index = int( params.get( 'sample_id', 0 ) )
+ sample_name = current_samples[sample_index]['name']
+ sample = request.has_sample( sample_name )
+ if sample:
+ trans.sa_session.delete( sample.values )
+ trans.sa_session.delete( sample )
+ trans.sa_session.flush()
+ message = 'Sample (%s) has been deleted.' % sample_name
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
+ cntrller=cntrller,
+ id=trans.security.encode_id( request.id ),
+ status=status,
+ message=message ) )
+ @web.expose
+ @web.require_login( "view data transfer page" )
+ def view_dataset_transfer( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ sample_id = params.get( 'sample_id', None )
+ try:
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
+ except:
+ return invalid_id_redirect( trans, cntrller, sample_id )
+ # check if a library and folder has been set for this sample yet.
+ if not sample.library or not sample.folder:
+ status = 'error'
+ message = "Set a data library and folder for sequencing request (%s) to transfer datasets." % sample.name
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
+ cntrller=cntrller,
+ id=trans.security.encode_id( sample.request.id ),
+ status=status,
+ message=message ) )
+ if is_admin:
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='manage_datasets',
+ sample_id=sample_id ) )
+
+ folder_path = util.restore_text( params.get( 'folder_path', '' ) )
+ if not folder_path:
+ if len( sample.datasets ):
+ folder_path = os.path.dirname( sample.datasets[-1].file_path[:-1] )
+ else:
+ folder_path = util.restore_text( sample.request.type.datatx_info.get( 'data_dir', '' ) )
+ 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']:
+ status = 'error'
+ message = 'The sequencer login information is incomplete. Click on sequencer information to add login details.'
+ return trans.fill_template( '/requests/common/dataset_transfer.mako',
+ cntrller=cntrller,
+ sample=sample,
+ dataset_files=sample.datasets,
+ message=message,
+ status=status,
+ files=[],
+ folder_path=folder_path )
+ def __import_samples( self, trans, cntrller, request, current_samples, libraries, **kwd ):
+ """
+ Reads the samples csv file and imports all the samples. The format of the csv file is:
+ SampleName,DataLibrary,DataLibraryFolder,Field1,Field2....
+ """
+ params = util.Params( kwd )
+ managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
+ file_obj = params.get( 'file_data', '' )
+ try:
+ reader = csv.reader( file_obj.file )
+ for row in reader:
+ library_id = None
+ folder_id = None
+ # FIXME: this is bad - what happens when multiple libraries have the same name??
+ lib = trans.sa_session.query( trans.model.Library ) \
+ .filter( and_( trans.model.Library.table.c.name==row[1],
+ trans.model.Library.table.c.deleted==False ) ) \
+ .first()
+ if lib:
+ folder = trans.sa_session.query( trans.model.LibraryFolder ) \
+ .filter( and_( trans.model.LibraryFolder.table.c.name==row[2],
+ trans.model.LibraryFolder.table.c.deleted==False ) ) \
+ .first()
+ if folder:
+ library_id = lib.id
+ folder_id = folder.id
+ library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
+ request.user,
+ len( current_samples ),
+ libraries,
+ None,
+ library_id,
+ folder_id,
+ **kwd )
+ current_samples.append( dict( name=row[0],
+ barcode='',
+ library=None,
+ folder=None,
+ library_select_field=library_select_field,
+ folder_select_field=folder_select_field,
+ field_values=row[3:] ) )
+ except Exception, e:
+ status = 'error'
+ message = 'Error thrown when importing samples file: %s' % str( e )
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='manage_request',
+ cntrller=cntrller,
+ id=trans.security.encode_id( request.id ),
+ status=status,
+ message=message ) )
+ request_widgets = self.__get_request_widgets( trans, request.id )
+ sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
+ return trans.fill_template( '/requests/common/manage_request.mako',
+ cntrller=cntrller,
+ request=request,
+ request_widgets=request_widgets,
+ current_samples=current_samples,
+ sample_copy=sample_copy,
+ managing_samples=managing_samples )
+ # ===== Methods for handling form definition widgets =====
+ def __get_request_widgets( self, trans, id ):
+ """Get the widgets for the request"""
+ request = trans.sa_session.query( trans.model.Request ).get( id )
+ # The request_widgets list is a list of dictionaries
+ request_widgets = []
+ for index, field in enumerate( request.type.request_form.fields ):
+ if field[ 'required' ]:
+ required_label = 'Required'
+ else:
+ required_label = 'Optional'
+ if field[ 'type' ] == 'AddressField':
+ if request.values.content[ index ]:
+ request_widgets.append( dict( label=field[ 'label' ],
+ value=trans.sa_session.query( trans.model.UserAddress ).get( int( request.values.content[ index ] ) ).get_html(),
+ helptext=field[ 'helptext' ] + ' (' + required_label + ')' ) )
+ else:
+ request_widgets.append( dict( label=field[ 'label' ],
+ value=None,
+ helptext=field[ 'helptext' ] + ' (' + required_label + ')' ) )
+ else:
+ request_widgets.append( dict( label=field[ 'label' ],
+ value=request.values.content[ index ],
+ helptext=field[ 'helptext' ] + ' (' + required_label + ')' ) )
+ return request_widgets
+ def __get_samples_widgets( self, trans, request, libraries, **kwd ):
+ """Get the widgets for all of the samples currently associated with the request"""
+ # The current_samples_widgets list is a list of dictionaries
+ current_samples_widgets = []
+ for index, sample in enumerate( request.samples ):
+ # Build the library_select_field and folder_select_field for each existing sample
+ library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
+ user=request.user,
+ sample_index=index,
+ libraries=libraries,
+ sample=sample,
+ **kwd )
+ # Append the dictionary for the current sample to the current_samples_widgets list
+ current_samples_widgets.append( dict( name=sample.name,
+ barcode=sample.bar_code,
+ library=sample.library,
+ folder=sample.folder,
+ field_values=sample.values.content,
+ library_select_field=library_select_field,
+ folder_select_field=folder_select_field ) )
+ return current_samples_widgets
+ # ===== Methods for building SelectFields used on various request forms =====
+ def __build_copy_sample_select_field( self, trans, current_samples ):
+ copy_sample_index_select_field = SelectField( 'copy_sample_index' )
+ copy_sample_index_select_field.add_option( 'None', -1, selected=True )
+ for index, sample_dict in enumerate( current_samples ):
+ copy_sample_index_select_field.add_option( sample_dict[ 'name' ], index )
+ return copy_sample_index_select_field
+ def __build_request_type_id_select_field( self, trans, selected_value='none' ):
+ accessible_request_types = trans.user.accessible_request_types( trans )
+ return build_select_field( trans, accessible_request_types, 'name', 'request_type_id', selected_value=selected_value, refresh_on_change=True )
+ def __build_user_id_select_field( self, trans, selected_value='none' ):
+ active_users = trans.sa_session.query( trans.model.User ) \
+ .filter( trans.model.User.table.c.deleted == False ) \
+ .order_by( trans.model.User.email.asc() )
+ # A refresh_on_change is required so the user's set of addresses can be displayed.
+ return build_select_field( trans, active_users, 'email', 'user_id', selected_value=selected_value, refresh_on_change=True )
+ def __build_sample_operation_select_field( self, trans, is_admin, request, selected_value ):
+ # The sample_operation SelectField is displayed only after the request has been submitted.
+ # It's label is "For selected samples"
+ if is_admin:
+ if request.is_complete:
+ bulk_operations = [ trans.model.Sample.bulk_operations.CHANGE_STATE ]
+ if request.is_rejected:
+ bulk_operations = [ trans.model.Sample.bulk_operations.SELECT_LIBRARY ]
+ else:
+ bulk_operations = [ s for i, s in trans.model.Sample.bulk_operations.items() ]
else:
- lib_widget.add_option('Select one', 'none')
- # all the libraries available to the selected user
- for lib, hidden_folder_ids in libraries.items():
- if str(lib.id) == str(lib_id):
- lib_widget.add_option(lib.name, lib.id, selected=True)
- selected_lib, selected_hidden_folder_ids = lib, hidden_folder_ids.split(',')
+ if request.is_complete:
+ bulk_operations = []
else:
- lib_widget.add_option(lib.name, lib.id)
- lib_widget.refresh_on_change_values.append(lib.id)
- # create the folder selectbox
- folder_widget = SelectField( "sample_%i_folder_id" % sample_index )
- # when editing a request, either the user has already selected a subfolder or not
- if sample:
- if sample.folder:
- current_fid = sample.folder.id
- else:
- # when a folder not yet associated with the request then the
- # the current folder is set to the root_folder of the
- # parent data library if present.
- if sample.library:
- current_fid = sample.library.root_folder.id
- else:
- current_fid = params.get( "sample_%i_folder_id" % sample_index, 'none' )
- else:
- if folder_id:
- current_fid = folder_id
- else:
- current_fid = 'none'
- # first option
- if lib_id == 'none':
- folder_widget.add_option('Select one', 'none', selected=True)
- else:
- folder_widget.add_option('Select one', 'none')
- if selected_lib:
- # get all show-able folders for the selected library
- showable_folders = trans.app.security_agent.get_showable_folders( user, user.all_roles(),
- selected_lib,
+ bulk_operations = [ trans.model.Sample.bulk_operations.SELECT_LIBRARY ]
+ return build_select_field( trans, bulk_operations, 'self', 'sample_operation', selected_value=selected_value, refresh_on_change=True )
+ def __build_library_and_folder_select_fields( self, trans, user, sample_index, libraries, sample=None, library_id=None, folder_id=None, **kwd ):
+ # Create the library_id SelectField for a specific sample. The received libraries param is a list of all the libraries
+ # accessible to the current user, and we add them as options to the library_select_field. If the user has selected an
+ # existing library then display all the accessible folders of the selected library in the folder_select_field.
+ #
+ # The libraries dictionary looks like: { library : '1,2' }, library : '3' }. Its keys are the libraries that
+ # should be displayed for the current user and its values are strings of comma-separated folder ids that should
+ # NOT be displayed.
+ #
+ # TODO: all object ids received in the params must be encoded.
+ params = util.Params( kwd )
+ library_select_field_name= "sample_%i_library_id" % sample_index
+ folder_select_field_name = "sample_%i_folder_id" % sample_index
+ if not library_id:
+ library_id = params.get( library_select_field_name, 'none' )
+ selected_library = None
+ selected_hidden_folder_ids = []
+ showable_folders = []
+ if sample and sample.library and library_id == 'none':
+ library_id = str( sample.library.id )
+ selected_library = sample.library
+ # If we have a selected library, get the list of it's folders that are not accessible to the current user
+ for library, hidden_folder_ids in libraries.items():
+ encoded_id = trans.security.encode_id( library.id )
+ if encoded_id == str( library_id ):
+ selected_library = library
+ selected_hidden_folder_ids = hidden_folder_ids.split( ',' )
+ break
+ # sample_%i_library_id SelectField with refresh on change enabled
+ library_select_field = build_select_field( trans,
+ libraries.keys(),
+ 'name',
+ library_select_field_name,
+ initial_value='none',
+ selected_value=str( library_id ).lower(),
+ refresh_on_change=True )
+ # Get all accessible folders for the selected library, if one is indeed selected
+ if selected_library:
+ showable_folders = trans.app.security_agent.get_showable_folders( user,
+ user.all_roles(),
+ selected_library,
[ trans.app.security_agent.permitted_actions.LIBRARY_ADD ],
selected_hidden_folder_ids )
- for f in showable_folders:
- if str(f.id) == str(current_fid):
- folder_widget.add_option(f.name, f.id, selected=True)
+ if sample:
+ # The user is editing the request, and may have previously selected a folder
+ if sample.folder:
+ selected_folder_id = sample.folder.id
+ else:
+ # If a library is selected but not a folder, use the library's root folder
+ if sample.library:
+ selected_folder_id = sample.library.root_folder.id
else:
- folder_widget.add_option(f.name, f.id)
- return lib_widget, folder_widget
- def __copy_sample(self, current_samples):
- copy_list = SelectField('copy_sample')
- copy_list.add_option('None', -1, selected=True)
- for i, s in enumerate(current_samples):
- copy_list.add_option(s['name'], i)
- return copy_list
- def __sample_operation_selectbox(self, trans, request, **kwd):
- params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- if cntrller == 'requests_admin' and trans.user_is_admin():
- if request.complete():
- bulk_operations = [trans.app.model.Sample.bulk_operations.CHANGE_STATE]
- if request.rejected():
- bulk_operations = [trans.app.model.Sample.bulk_operations.SELECT_LIBRARY]
+ # The user just selected a folder
+ selected_folder_id = params.get( folder_select_field_name, 'none' )
+ elif folder_id:
+ # TODO: not sure when this would be passed
+ selected_folder_id = folder_id
+ else:
+ selected_folder_id = 'none'
+ # TODO: Change the name of the library root folder to "Library root" to clarify to the
+ # user that it is the root folder. We probably should just change this in the Library code,
+ # and update the data in the db.
+ folder_select_field = build_select_field( trans,
+ showable_folders,
+ 'name',
+ folder_select_field_name,
+ initial_value='none',
+ selected_value=selected_folder_id )
+ return library_select_field, folder_select_field
+ def __build_sample_state_id_select_field( self, trans, request, selected_value ):
+ if selected_value == 'none':
+ if request.samples:
+ selected_value = trans.security.encode_id( request.samples[0].state.id )
else:
- bulk_operations = [s for i, s in trans.app.model.Sample.bulk_operations.items()]
- else:
- if request.complete():
- bulk_operations = []
- else:
- bulk_operations = [trans.app.model.Sample.bulk_operations.SELECT_LIBRARY]
- op_list = SelectField('select_sample_operation',
- refresh_on_change=True,
- refresh_on_change_values=bulk_operations)
- sel_op = kwd.get('select_sample_operation', 'none')
- if sel_op == 'none':
- op_list.add_option('Select operation', 'none', True)
- else:
- op_list.add_option('Select operation', 'none')
- for s in bulk_operations:
- if s == sel_op:
- op_list.add_option(s, s, True)
- else:
- op_list.add_option(s, s)
- return op_list
- def __selected_samples(self, trans, request, **kwd):
- params = util.Params( kwd )
- selected_samples = []
- for s in request.samples:
- if CheckboxField.is_checked(params.get('select_sample_%i' % s.id, '')):
- selected_samples.append(s.id)
- return selected_samples
- @web.expose
- @web.require_login( "create/submit sequencing requests" )
- def request_page(self, trans, **kwd):
- params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- try:
- request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id( kwd['id']) )
- except:
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- status='error',
- message="Invalid request ID") )
- # get the user entered sample details
- current_samples, details, edit_mode, libraries = self.__update_samples( trans, request, **kwd )
- selected_samples = self.__selected_samples(trans, request, **kwd)
- sample_ops = self.__sample_operation_selectbox(trans, request,**kwd)
- if params.get( 'select_sample_operation', False ) and not selected_samples:
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='show',
- id=trans.security.encode_id(request.id),
- status='error',
- message='Select at least one sample before selecting an operation.' ))
- if params.get( 'import_samples_button', False ):
- return self.__import_samples(trans, cntrller, request, current_samples, details, libraries, **kwd)
- elif params.get('add_sample_button', False ):
- # add an empty or filled sample
- # if the user has selected a sample no. to copy then copy the contents
- # of the src sample to the new sample else an empty sample
- src_sample_index = int(params.get( 'copy_sample', -1 ) )
- # get the number of new copies of the src sample
- num_sample_to_copy = int( params.get( 'num_sample_to_copy', 1 ) )
- if src_sample_index == -1:
- for ns in range( num_sample_to_copy ):
- # empty sample
- lib_widget, folder_widget = self.__library_widgets(trans, request.user,
- len(current_samples),
- libraries, None, **kwd)
- current_samples.append(dict(name='Sample_%i' % (len(current_samples)+1),
- barcode='',
- library=None,
- folder=None,
- field_values=['' for field in request.type.sample_form.fields],
- lib_widget=lib_widget,
- folder_widget=folder_widget))
- else:
- src_library_id = current_samples[src_sample_index]['lib_widget'].get_selected( return_value=True )
- src_folder_id = current_samples[src_sample_index]['folder_widget'].get_selected( return_value=True )
- for ns in range(num_sample_to_copy):
- lib_widget, folder_widget = self.__library_widgets(trans, request.user,
- len(current_samples),
- libraries, sample=None,
- lib_id=src_library_id,
- folder_id=src_folder_id,
- **kwd)
- current_samples.append(dict(name=current_samples[src_sample_index]['name']+'_%i' % (len(current_samples)+1),
- barcode='',
- library_id='none',
- folder_id='none',
- field_values=[val for val in current_samples[src_sample_index]['field_values']],
- lib_widget=lib_widget,
- folder_widget=folder_widget))
- return trans.fill_template( '/requests/common/show_request.mako',
- cntrller=cntrller,
- request=request,
- request_details=self.request_details(trans, request.id),
- current_samples=current_samples,
- sample_copy=self.__copy_sample(current_samples),
- details=details,
- selected_samples=selected_samples,
- sample_ops=sample_ops,
- edit_mode=edit_mode)
- elif params.get( 'save_samples_button', False ):
- # check for duplicate sample names
- message = ''
- for index in range(len(current_samples)-len(request.samples)):
- sample_index = index + len(request.samples)
- sample_name = current_samples[sample_index]['name']
- if not sample_name.strip():
- message = 'Please enter the name of sample number %i' % sample_index
+ selected_value = trans.security.encode_id( request.type.states[0].id )
+ return build_select_field( trans,
+ objs=request.type.states,
+ label_attr='name',
+ select_field_name='sample_state_id',
+ selected_value=selected_value,
+ refresh_on_change=False )
+ # ===== Methods for validation forms and fields =====
+ def __validate_request( self, trans, cntrller, request ):
+ """Validates the request entered by the user"""
+ # TODO: Add checks for required sample fields here.
+ empty_fields = []
+ # Make sure required form fields are filled in.
+ for index, field in enumerate( request.type.request_form.fields ):
+ if field[ 'required' ] == 'required' and request.values.content[ index ] in [ '', None ]:
+ empty_fields.append( field[ 'label' ] )
+ if empty_fields:
+ message = 'Complete the following fields of the request before submitting: '
+ for ef in empty_fields:
+ message += '<b>' + ef + '</b> '
+ return message
+ return None
+ def __validate_barcode( self, trans, sample, barcode ):
+ """
+ Makes sure that the barcode about to be assigned to a sample is gobally unique.
+ That is, barcodes must be unique across requests in Galaxy sample tracking.
+ """
+ message = ''
+ unique = True
+ for index in range( len( sample.request.samples ) ):
+ # Check for empty bar code
+ if not barcode.strip():
+ message = 'Fill in the barcode for sample (%s).' % sample.name
+ break
+ # TODO: Add a unique constraint to sample.bar_code table column
+ # Make sure bar code is unique
+ for sample_has_bar_code in trans.sa_session.query( trans.model.Sample ) \
+ .filter( trans.model.Sample.table.c.bar_code == barcode ):
+ if sample_has_bar_code and sample_has_bar_code.id != sample.id:
+ message = '''The bar code (%s) associated with the sample (%s) belongs to another sample.
+ Bar codes must be unique across all samples, so use a different bar code
+ for this sample.''' % ( barcode, sample.name )
+ unique = False
break
- count = 0
- for i in range(len(current_samples)):
- if sample_name == current_samples[i]['name']:
- count = count + 1
- if count > 1:
- message = "This request has <b>%i</b> samples with the name <b>%s</b>.\nSamples belonging to a request must have unique names." % (count, sample_name)
- break
- if message:
- return trans.fill_template( '/requests/common/show_request.mako',
- cntrller=cntrller,
- request=request, selected_samples=selected_samples,
- request_details=self.request_details(trans, request.id),
- current_samples = current_samples,
- sample_copy=self.__copy_sample(current_samples),
- details=details, edit_mode=edit_mode,
- sample_ops=sample_ops,
- status='error', message=message)
- # save all the new/unsaved samples entered by the user
- if edit_mode == 'False':
- for index in range(len(current_samples)-len(request.samples)):
- sample_index = len(request.samples)
- form_values = trans.app.model.FormValues(request.type.sample_form,
- current_samples[sample_index]['field_values'])
- trans.sa_session.add( form_values )
- trans.sa_session.flush()
- s = trans.app.model.Sample(current_samples[sample_index]['name'], '',
- request, form_values,
- current_samples[sample_index]['barcode'],
- current_samples[sample_index]['library'],
- current_samples[sample_index]['folder'])
- trans.sa_session.add( s )
- trans.sa_session.flush()
-
- else:
- status = 'done'
- message = 'Changes made to the sample(s) are saved. '
- for sample_index in range(len(current_samples)):
- sample = request.samples[sample_index]
- sample.name = current_samples[sample_index]['name']
- sample.library = current_samples[sample_index]['library']
- sample.folder = current_samples[sample_index]['folder']
- if request.submitted():
- bc_message = self.__validate_barcode(trans, sample, current_samples[sample_index]['barcode'])
- if bc_message:
- status = 'error'
- message += bc_message
- else:
- if not sample.bar_code:
- # if this is a 'new' (still in its first state) sample
- # change the state to the next
- if sample.current_state().id == request.type.states[0].id:
- event = trans.app.model.SampleEvent(sample,
- request.type.states[1],
- 'Sample added to the system')
- trans.sa_session.add( event )
- trans.sa_session.flush()
- # now check if all the samples' barcode has been entered.
- # If yes then send notification email if configured
- common_state = request.common_state()
- if common_state:
- if common_state.id == request.type.states[1].id:
- event = trans.app.model.RequestEvent(request,
- request.states.SUBMITTED,
- "All samples are in %s state." % common_state.name)
- trans.sa_session.add( event )
- trans.sa_session.flush()
- request.send_email_notification(trans, request.type.states[1])
- sample.bar_code = current_samples[sample_index]['barcode']
- trans.sa_session.add( sample )
- trans.sa_session.flush()
- form_values = trans.sa_session.query( trans.app.model.FormValues ).get( sample.values.id )
- form_values.content = current_samples[sample_index]['field_values']
- trans.sa_session.add( form_values )
- trans.sa_session.flush()
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='show',
- id=trans.security.encode_id(request.id),
- status=status,
- message=message ))
- elif params.get( 'edit_samples_button', False ):
- edit_mode = 'True'
- return trans.fill_template( '/requests/common/show_request.mako',
- cntrller=cntrller,
- request=request, selected_samples=selected_samples,
- request_details=self.request_details(trans, request.id),
- current_samples=current_samples,
- sample_copy=self.__copy_sample(current_samples),
- sample_ops=sample_ops,
- details=details, libraries=libraries,
- edit_mode=edit_mode)
- elif params.get( 'cancel_changes_button', False ):
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='show',
- id=trans.security.encode_id(request.id)) )
- elif params.get( 'change_state_button', False ) == 'Save':
- comments = util.restore_text( params.comment )
- selected_state = int( params.select_state )
- new_state = trans.sa_session.query( trans.app.model.SampleState ).get( selected_state )
- for sample_id in selected_samples:
- sample = trans.sa_session.query( trans.app.model.Sample ).get( sample_id )
- event = trans.app.model.SampleEvent(sample, new_state, comments)
- trans.sa_session.add( event )
- trans.sa_session.flush()
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller=cntrller,
- action='update_request_state',
- request_id=request.id ))
- elif params.get( 'change_state_button', False ) == 'Cancel':
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='show',
- id=trans.security.encode_id(request.id)) )
- elif params.get( 'change_lib_button', False ) == 'Save':
- library = trans.sa_session.query( trans.app.model.Library ).get( int( params.get( 'sample_0_library_id', None ) ) )
- folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( int( params.get( 'sample_0_folder_id', None ) ) )
- for sample_id in selected_samples:
- sample = trans.sa_session.query( trans.app.model.Sample ).get( sample_id )
- sample.library = library
- sample.folder = folder
- trans.sa_session.add( sample )
- trans.sa_session.flush()
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='show',
- id=trans.security.encode_id(request.id),
- status='done',
- message='Changes made to the selected sample(s) are saved. ') )
- elif params.get( 'change_lib_button', False ) == 'Cancel':
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='show',
- id=trans.security.encode_id(request.id)) )
- else:
- return trans.fill_template( '/requests/common/show_request.mako',
- cntrller=cntrller,
- request=request, selected_samples=selected_samples,
- request_details=self.request_details(trans, request.id),
- current_samples=current_samples,
- sample_copy=self.__copy_sample(current_samples),
- details=details, libraries=libraries,
- sample_ops=sample_ops,
- edit_mode=edit_mode, status=status, message=message,
- bulk_lib_ops=self.__library_widgets(trans, request.user, 0, libraries, None, **kwd))
- def __import_samples(self, trans, cntrller, request, current_samples, details, libraries, **kwd):
- '''
- This method reads the samples csv file and imports all the samples
- The format of the csv file is:
- SampleName,DataLibrary,DataLibraryFolder,Field1,Field2....
- '''
- try:
- params = util.Params( kwd )
- edit_mode = params.get( 'edit_mode', 'False' )
- file_obj = params.get('file_data', '')
- reader = csv.reader(file_obj.file)
- for row in reader:
- lib_id = None
- folder_id = None
- lib = trans.sa_session.query( trans.app.model.Library ) \
- .filter( and_( trans.app.model.Library.table.c.name==row[1], \
- trans.app.model.Library.table.c.deleted==False ) )\
- .first()
- if lib:
- folder = trans.sa_session.query( trans.app.model.LibraryFolder ) \
- .filter( and_( trans.app.model.LibraryFolder.table.c.name==row[2], \
- trans.app.model.LibraryFolder.table.c.deleted==False ) )\
- .first()
- if folder:
- lib_id = lib.id
- folder_id = folder.id
- lib_widget, folder_widget = self.__library_widgets(trans, request.user, len(current_samples),
- libraries, None, lib_id, folder_id, **kwd)
- current_samples.append(dict(name=row[0],
- barcode='',
- library=None,
- folder=None,
- lib_widget=lib_widget,
- folder_widget=folder_widget,
- field_values=row[3:]))
- return trans.fill_template( '/requests/common/show_request.mako',
- cntrller=cntrller,
- request=request,
- request_details=self.request_details(trans, request.id),
- current_samples=current_samples,
- sample_copy=self.__copy_sample(current_samples),
- details=details,
- edit_mode=edit_mode)
- except:
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='show',
- id=trans.security.encode_id(request.id),
- status='error',
- message='Error in importing samples file' ))
- def __validate_barcode(self, trans, sample, barcode):
- '''
- This method makes sure that the given barcode about to be assigned to
- the given sample is gobally unique. That is, barcodes must be unique
- across requests in Galaxy LIMS
- '''
- message = ''
- for index in range(len(sample.request.samples)):
- # check for empty bar code
- if not barcode.strip():
- message = 'Please fill the barcode for sample <b>%s</b>.' % sample.name
- break
- # check all the saved bar codes
- all_samples = trans.sa_session.query( trans.app.model.Sample )
- for s in all_samples:
- if barcode == s.bar_code:
- if sample.id == s.id:
- continue
- else:
- message = '''The bar code <b>%s</b> of sample <b>%s</b>
- belongs another sample. The sample bar codes must be
- unique throughout the system''' % \
- (barcode, sample.name)
- break
- if message:
+ if not unique:
break
return message
- @web.expose
- @web.require_login( "create/submit sequencing requests" )
- def delete_sample(self, trans, **kwd):
- params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- request = trans.sa_session.query( trans.app.model.Request ).get( int( params.get( 'request_id', 0 ) ) )
- current_samples, details, edit_mode, libraries = self.__update_samples( trans, request, **kwd )
- sample_index = int(params.get('sample_id', 0))
- sample_name = current_samples[sample_index]['name']
- s = request.has_sample(sample_name)
- if s:
- trans.sa_session.delete( s.values )
- trans.sa_session.delete( s )
- trans.sa_session.flush()
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='show',
- id=trans.security.encode_id(request.id),
- status='done',
- message='Sample <b>%s</b> has been deleted.' % sample_name ))
- def request_details(self, trans, id):
- '''
- Shows the request details
- '''
- request = trans.sa_session.query( trans.app.model.Request ).get( id )
- # list of widgets to be rendered on the request form
- request_details = []
- # form fields
- for index, field in enumerate(request.type.request_form.fields):
- if field['required']:
- req = 'Required'
- else:
- req = 'Optional'
- if field['type'] == 'AddressField':
- if request.values.content[index]:
- request_details.append(dict(label=field['label'],
- value=trans.sa_session.query( trans.app.model.UserAddress ).get( int( request.values.content[index] ) ).get_html(),
- helptext=field['helptext']+' ('+req+')'))
- else:
- request_details.append(dict(label=field['label'],
- value=None,
- helptext=field['helptext']+' ('+req+')'))
- else:
- request_details.append(dict(label=field['label'],
- value=request.values.content[index],
- helptext=field['helptext']+' ('+req+')'))
- return request_details
- @web.expose
- @web.require_login( "create/submit sequencing requests" )
- def sample_events(self, trans, **kwd):
- params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- try:
- sample_id = int(params.get('sample_id', False))
- sample = trans.sa_session.query( trans.app.model.Sample ).get( sample_id )
- except:
- message = "Invalid sample ID"
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- status='error',
- message=message) )
- events_list = []
- all_events = sample.events
- for event in all_events:
- events_list.append((event.state.name, event.state.desc,
- time_ago(event.update_time),
- event.comment))
- return trans.fill_template( '/requests/common/sample_events.mako',
- cntrller=cntrller,
- events_list=events_list,
- sample=sample)
- @web.expose
- @web.require_admin
- def show_datatx_page( self, trans, **kwd ):
- params = util.Params( kwd )
- cntrller = util.restore_text( params.get( 'cntrller', 'requests' ) )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- try:
- sample = trans.sa_session.query( trans.app.model.Sample ).get( trans.security.decode_id( kwd['sample_id'] ) )
- except:
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- status='error',
- message="Invalid sample ID") )
- # check if a library and folder has been set for this sample yet.
- if not sample.library or not sample.folder:
- return trans.response.send_redirect( web.url_for( controller=cntrller,
- action='list',
- operation='show',
- status='error',
- message="Set a data library and folder for <b>%s</b> to transfer dataset(s)." % sample.name,
- id=trans.security.encode_id(sample.request.id) ) )
- if cntrller == 'requests_admin':
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='manage_datasets',
- sample_id=sample.id) )
-
- if params.get( 'folder_path', '' ):
- folder_path = util.restore_text( params.get( 'folder_path', '' ) )
- else:
- if len(sample.datasets):
- folder_path = os.path.dirname(sample.datasets[-1].file_path[:-1])
- else:
- folder_path = util.restore_text( sample.request.type.datatx_info.get('data_dir', '') )
- 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']:
- status = 'error'
- message = 'The sequencer login information is incomplete. Click on the <b>Sequencer information</b> to add login details.'
- return trans.fill_template( '/requests/common/get_data.mako',
- cntrller=cntrller, sample=sample,
- dataset_files=sample.datasets,
- message=message, status=status, files=[],
- folder_path=folder_path )
- # Find sequencing requests & samples
- def __find_widgets(self, trans, **kwd):
- params = util.Params( kwd )
- request_states = SelectField('request_states', multiple=True, display="checkboxes")
- sel_op = kwd.get('request_states', trans.app.model.Request.states.SUBMITTED)
- for i, s in trans.app.model.Request.states.items():
- if s in sel_op:
- request_states.add_option(s, s, True)
- else:
- request_states.add_option(s, s)
- search_type = SelectField('search_type')
- sel_op = kwd.get('search_type', 'sample name')
- for s in ['sample name', 'barcode', 'dataset']:
- if s in sel_op:
- search_type.add_option(s, s, True)
- else:
- search_type.add_option(s, s)
- search_box = TextField('search_box', 50, kwd.get('search_box', ''))
- return request_states, search_type, search_box
- @web.expose
- @web.require_admin
- def find( self, trans, **kwd ):
- params = util.Params( kwd )
- cntrller = params.get( 'cntrller', 'requests' )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- samples_list = []
- results = ''
- if params.get('go_button', '') == 'Find':
- search_string = kwd.get( 'search_box', '' )
- search_type = params.get( 'search_type', '' )
- request_states = util.listify( params.get( 'request_states', '' ) )
- samples = []
- if search_type == 'barcode':
- samples = trans.sa_session.query( trans.app.model.Sample ) \
- .filter( and_( trans.app.model.Sample.table.c.deleted==False,
- trans.app.model.Sample.table.c.bar_code.like(search_string) ) )\
- .order_by( trans.app.model.Sample.table.c.create_time.desc())\
- .all()
- elif search_type == 'sample name':
- samples = trans.sa_session.query( trans.app.model.Sample ) \
- .filter( and_( trans.app.model.Sample.table.c.deleted==False,
- trans.app.model.Sample.table.c.name.ilike(search_string) ) )\
- .order_by( trans.app.model.Sample.table.c.create_time.desc())\
- .all()
- elif search_type == 'dataset':
- samples = trans.sa_session.query( trans.app.model.Sample ) \
- .filter( and_( trans.app.model.Sample.table.c.deleted==False,
- trans.app.model.SampleDataset.table.c.sample_id==trans.app.model.Sample.table.c.id,
- trans.app.model.SampleDataset.table.c.name.ilike(search_string) ) )\
- .order_by( trans.app.model.Sample.table.c.create_time.desc())\
- .all()
- if cntrller == 'requests':
- for s in samples:
- if s.request.user.id == trans.user.id \
- and s.request.state() in request_states\
- and not s.request.deleted:
- samples_list.append(s)
- elif cntrller == 'requests_admin':
- for s in samples:
- if not s.request.deleted \
- and s.request.state() in request_states:
- samples_list.append(s)
- results = 'There are %i sample(s) matching the search parameters.' % len(samples_list)
- request_states, search_type, search_box = self.__find_widgets(trans, **kwd)
- return trans.fill_template( '/requests/common/find.mako',
- cntrller=cntrller, request_states=request_states,
- samples=samples_list, search_type=search_type,
- results=results, search_box=search_box )
+ def __validate_email( self, email ):
+ error = ''
+ if len( email ) == 0 or "@" not in email or "." not in email:
+ error = "(%s) is not a valid email address. " % str( email )
+ elif len( email ) > 255:
+ error = "(%s) exceeds maximum allowable length. " % str( email )
+ return error
+ # ===== Other miscellaneoud utility methods =====
+ def __get_selected_samples( self, trans, request, **kwd ):
+ selected_samples = []
+ for sample in request.samples:
+ if CheckboxField.is_checked( kwd.get( 'select_sample_%i' % sample.id, '' ) ):
+ selected_samples.append( trans.security.encode_id( sample.id ) )
+ return selected_samples
+
+# ===== Miscellaneoud utility methods outside of the RequestsCommon class =====
+def invalid_id_redirect( trans, cntrller, obj_id, action='browse_requests' ):
+ status = 'error'
+ message = "Invalid request id (%s)" % str( obj_id )
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action=action,
+ status=status,
+ message=message ) )
--- /dev/null
+++ b/templates/requests/common/dataset_transfer.mako
@@ -0,0 +1,47 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<h2>Datasets of Sample "${sample.name}"</h2>
+
+<ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">Refresh</a>
+ </li>
+ <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 ) )}">${sample.library.name} Data Library</a>
+ </li>
+ <li>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a>
+ </li>
+</ul>
+
+%if dataset_files:
+ <div class="form-row">
+ <table class="grid">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Size</th>
+ <th>Status</th>
+ </tr>
+ <thead>
+ <tbody>
+ %for dataset_file in dataset_files:
+ <tr>
+ <td>${dataset_file.name}</td>
+ <td>${dataset_file.size}</td>
+ <td>${dataset_file.status}</td>
+ </tr>
+ %endfor
+ </tbody>
+ </table>
+ </div>
+%else:
+ <div class="form-row">
+ There are no datasets associated with this sample.
+ </div>
+%endif
--- a/templates/admin/requests/create_request_type.mako
+++ b/templates/admin/requests/create_request_type.mako
@@ -1,7 +1,6 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
-
%if message:
${render_msg( message, status )}
%endif
@@ -88,6 +87,5 @@
<input type="submit" name="save_request_type" value="Save"/></div></form>
-
%endif
</div>
--- a/templates/requests/common/sample_state.mako
+++ b/templates/requests/common/sample_state.mako
@@ -1,5 +1,5 @@
<%def name="render_sample_state( cntrller, sample )">
- <a href="${h.url_for( controller='requests_common', cntrller=cntrller, action='sample_events', sample_id=sample.id)}">${sample.current_state().name}</a>
+ <a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></%def>
${render_sample_state( cntrller, sample )}
--- a/templates/admin/requests/datasets_grid.mako
+++ b/templates/admin/requests/datasets_grid.mako
@@ -1,6 +1,3 @@
-
-
-
<%def name="custom_javascripts()"><script type="text/javascript">
$("#select-dataset-action-button").bind( "click", function(e) {
@@ -10,7 +7,7 @@
error: function() { alert( "Couldn't create new browser" ) },
success: function(form_html) {
show_modal("Select file", form_html, {
- "Cancel": function() { window.location = "${h.url_for( controller='requests_admin', action='list' )}"; },
+ "Cancel": function() { window.location = "${h.url_for( controller='requests_admin', action='browse_requests' )}"; },
"Continue": function() { $(document).trigger("convert_dbkeys"); continue_fn(); }
});
$("#new-title").focus();
@@ -28,4 +25,3 @@
</%def><%inherit file="/grid_base.mako"/>
-
--- a/templates/requests/common/get_data.mako
+++ /dev/null
@@ -1,61 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-
-
-<h2>Datasets of Sample "${sample.name}"</h2>
-
-<ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', cntrller=cntrller, action='show_datatx_page', sample_id=trans.security.encode_id(sample.id) )}">
- <span>Refresh</span></a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller='library', id=trans.security.encode_id( sample.library.id ) )}">
- <span>${sample.library.name} Data Library</span></a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='list', operation='show', id=trans.security.encode_id(sample.request.id) )}">
- <span>Browse this request</span></a>
- </li>
-</ul>
-
-
-%if len(dataset_files):
- <div class="form-row">
- <table class="grid">
- <thead>
- <tr>
- <th>Name</th>
- <th>Size</th>
- <th>Status</th>
- </tr>
- <thead>
- <tbody>
- %for dataset_index, dataset_file in enumerate(dataset_files):
- <tr>
-
- <td>
- ${dataset_file.name}
- </td>
- <td>
- ${dataset_file.size}
- </td>
- <td>
- ${dataset_file.status}
- </td>
- </tr>
- %endfor
- </tbody>
- </table>
- </div>
-%else:
- <div class="form-row">
- There are no dataset files associated with this sample.
- </div>
-%endif
--- a/templates/admin/requests/manage_request_types.mako
+++ /dev/null
@@ -1,1 +0,0 @@
-<%inherit file="/grid_base.mako"/>
1
0

galaxy-dist commit 9ddd7c1d4dd5: Refactoring of trackster data providers framework. Merged all data providers into a single file as this reduces code, brings together related classes, and is more pythonic. Also created a single method to get data provider using either names or datasets.
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 jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1286829948 14400
# Node ID 9ddd7c1d4dd5075a0878026e59cf04f3d9d768d6
# Parent 4bc2738671123bf3731e940ae1f2a0004ceca2be
Refactoring of trackster data providers framework. Merged all data providers into a single file as this reduces code, brings together related classes, and is more pythonic. Also created a single method to get data provider using either names or datasets.
--- a/lib/galaxy/visualization/tracks/data/summary_tree.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""
-Summary tree data provider for the Galaxy track browser.
-"""
-
-import pkg_resources; pkg_resources.require( "bx-python" )
-from galaxy.visualization.tracks.summary import *
-from math import ceil, log
-from galaxy.util.lrucache import LRUCache
-from base import TracksDataProvider
-
-CACHE = LRUCache(20) # Store 20 recently accessed indices for performance
-
-class SummaryTreeDataProvider( TracksDataProvider ):
- def get_summary( self, chrom, start, end, **kwargs):
- filename = self.converted_dataset.file_name
- st = CACHE[filename]
- if st is None:
- st = summary_tree_from_file( self.converted_dataset.file_name )
- CACHE[filename] = st
-
- # If chrom is not found in blocks, try removing the first three
- # characters (e.g. 'chr') and see if that works. This enables the
- # provider to handle chrome names defined as chrXXX and as XXX.
- if chrom in st.chrom_blocks:
- pass
- elif chrom[3:] in st.chrom_blocks:
- chrom = chrom[3:]
- else:
- return None
-
- resolution = max(1, ceil(float(kwargs['resolution'])))
-
- level = ceil( log( resolution, st.block_size ) )
- level = int(max( level, 0 ))
- if level <= 0:
- return None
-
- stats = st.chrom_stats[chrom]
- results = st.query(chrom, int(start), int(end), level)
- if results == "detail":
- return None
- elif results == "draw" or level <= 1:
- return "no_detail"
- else:
- return results, stats[level]["max"], stats[level]["avg"], stats[level]["delta"]
--- a/lib/galaxy/visualization/tracks/data/interval_index.py
+++ /dev/null
@@ -1,68 +0,0 @@
-"""
-Interval index data provider for the Galaxy track browser.
-Kanwei Li, 2010
-
-Payload format: [ uid (offset), start, end, name, strand, thick_start, thick_end, blocks ]
-"""
-
-import pkg_resources; pkg_resources.require( "bx-python" )
-from bx.interval_index_file import Indexes
-from galaxy.datatypes.interval import Bed, Gff
-from base import TracksDataProvider
-
-MAX_VALS = 5000 # only display first MAX_VALS features
-
-class IntervalIndexDataProvider( TracksDataProvider ):
- def get_data( self, chrom, start, end, **kwargs ):
- start, end = int(start), int(end)
- source = open( self.original_dataset.file_name )
- index = Indexes( self.converted_dataset.file_name )
- results = []
- count = 0
- message = None
-
- # If chrom is not found in indexes, try removing the first three
- # characters (e.g. 'chr') and see if that works. This enables the
- # provider to handle chrome names defined as chrXXX and as XXX.
- chrom = str(chrom)
- if chrom not in index.indexes and chrom[3:] in index.indexes:
- chrom = chrom[3:]
-
- for start, end, offset in index.find(chrom, start, end):
- if count >= MAX_VALS:
- message = "Only the first %s features are being displayed." % MAX_VALS
- break
- count += 1
- source.seek(offset)
- feature = source.readline().split()
- payload = [ offset, start, end ]
- # TODO: can we use column metadata to fill out payload?
- # TODO: use function to set payload data
- if "no_detail" not in kwargs:
- length = len(feature)
- if isinstance( self.original_dataset.datatype, Gff ):
- # GFF dataset.
- if length >= 3:
- payload.append( feature[2] ) # name
- if length >= 7:
- payload.append( feature[6] ) # strand
- elif isinstance( self.original_dataset.datatype, Bed ):
- # BED dataset.
- if length >= 4:
- payload.append(feature[3]) # name
- if length >= 6: # strand
- payload.append(feature[5])
-
- if length >= 8:
- payload.append(int(feature[6]))
- payload.append(int(feature[7]))
-
- if length >= 12:
- block_sizes = [ int(n) for n in feature[10].split(',') if n != '']
- block_starts = [ int(n) for n in feature[11].split(',') if n != '' ]
- blocks = zip(block_sizes, block_starts)
- payload.append( [ (start + block[1], start + block[1] + block[0]) for block in blocks] )
-
- results.append(payload)
-
- return { 'data': results, 'message': message }
--- a/lib/galaxy/visualization/tracks/data/bam.py
+++ /dev/null
@@ -1,73 +0,0 @@
-"""
-Visualization data provider for BAM format.
-Kanwei Li, 2010
-"""
-
-import pkg_resources; pkg_resources.require( "pysam" )
-
-from pysam import csamtools
-from math import floor, ceil, log
-import logging
-log = logging.getLogger(__name__)
-from base import TracksDataProvider
-
-MAX_VALS = 5000 # only display first MAX_VALS datapoints
-
-class BamDataProvider( TracksDataProvider ):
- """
- Provides access to intervals from a sorted indexed BAM file.
- """
- def get_data( self, chrom, start, end, **kwargs ):
- """
- Fetch intervals in the region
- """
- start, end = int(start), int(end)
- # Attempt to open the BAM file with index
- bamfile = csamtools.Samfile( filename=self.original_dataset.file_name, mode='rb', index_filename=self.converted_dataset.file_name )
- message = None
- try:
- data = bamfile.fetch(start=start, end=end, reference=chrom)
- except ValueError, e:
- # Some BAM files do not prefix chromosome names with chr, try without
- if chrom.startswith( 'chr' ):
- try:
- data = bamfile.fetch( start=start, end=end, reference=chrom[3:] )
- except ValueError:
- return None
- else:
- return None
- # Encode reads as list of dictionaries
- results = []
- paired_pending = {}
- for read in data:
- if len(results) > MAX_VALS:
- message = "Only the first %s pairs are being displayed." % MAX_VALS
- break
- qname = read.qname
- if read.is_proper_pair:
- if qname in paired_pending: # one in dict is always first
- pair = paired_pending[qname]
- results.append( [ qname, pair['start'], read.pos + read.rlen, read.seq, [pair['start'], pair['end'], pair['seq']], [read.pos, read.pos + read.rlen, read.seq] ] )
- # results.append( [read.qname, pair['start'], read.pos + read.rlen, qname, [pair['start'], pair['end']], [read.pos, read.pos + read.rlen] ] )
- del paired_pending[qname]
- else:
- paired_pending[qname] = { 'start': read.pos, 'end': read.pos + read.rlen, 'seq': read.seq, 'mate_start': read.mpos, 'rlen': read.rlen }
- else:
- results.append( [qname, read.pos, read.pos + read.rlen, read.seq] )
- # take care of reads whose mates are out of range
- for qname, read in paired_pending.iteritems():
- if read['mate_start'] < read['start']:
- start = read['mate_start']
- end = read['end']
- r1 = [read['mate_start'], read['mate_start'] + read['rlen']]
- r2 = [read['start'], read['end'], read['seq']]
- else:
- start = read['start']
- end = read['mate_start'] + read['rlen']
- r1 = [read['start'], read['end'], read['seq']]
- r2 = [read['mate_start'], read['mate_start'] + read['rlen']]
-
- results.append( [ qname, start, end, read['seq'], r1, r2 ] )
-
- bamfile.close()
- return { 'data': results, 'message': message }
--- a/lib/galaxy/visualization/tracks/data/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""
-Package for track data providers
-"""
--- a/lib/galaxy/web/controllers/tracks.py
+++ b/lib/galaxy/web/controllers/tracks.py
@@ -20,12 +20,7 @@ from galaxy.web.framework import simplej
from galaxy.web.framework.helpers import grids
from galaxy.util.bunch import Bunch
-from galaxy.visualization.tracks.data.array_tree import ArrayTreeDataProvider
-from galaxy.visualization.tracks.data.interval_index import IntervalIndexDataProvider
-from galaxy.visualization.tracks.data.bam import BamDataProvider
-from galaxy.visualization.tracks.data.summary_tree import SummaryTreeDataProvider
-from galaxy.visualization.tracks.data.vcf import VcfDataProvider
-from galaxy.visualization.tracks.data.base import dataset_to_data_provider
+from galaxy.visualization.tracks.data_providers import *
# Message strings returned to browser
messages = Bunch(
@@ -37,17 +32,6 @@ messages = Bunch(
ERROR = "error"
)
-# Mapping from dataset type to a class that can fetch data from a file of that
-# type. First key is converted dataset type; if result is another dict, second key
-# is original dataset type. TODO: This needs to be more flexible.
-# TODO: move this mapping into TracksDataProvider
-dataset_type_to_data_provider = {
- "array_tree": ArrayTreeDataProvider,
- "interval_index": { "vcf": VcfDataProvider, "default" : IntervalIndexDataProvider },
- "bai": BamDataProvider,
- "summary_tree": SummaryTreeDataProvider
-}
-
class DatasetSelectionGrid( grids.Grid ):
class DbKeyColumn( grids.GridColumn ):
def filter( self, trans, user, query, dbkey ):
@@ -152,7 +136,7 @@ class TracksController( BaseController,
hda_query = trans.sa_session.query( model.HistoryDatasetAssociation )
dataset = hda_query.get( dataset_id )
track_type, _ = dataset.datatype.get_track_type()
- track_data_provider_class = dataset_to_data_provider( dataset )
+ track_data_provider_class = get_data_provider( original_dataset=dataset )
track_data_provider = track_data_provider_class( original_dataset=dataset )
track = {
@@ -296,7 +280,7 @@ class TracksController( BaseController,
extra_info = None
if 'index' in data_sources:
# Have to choose between indexer and data provider
- indexer = dataset_type_to_data_provider[data_sources['index']]( dataset.get_converted_dataset(trans, data_sources['index']), dataset )
+ indexer = get_data_provider( name=data_sources['index'] )( dataset.get_converted_dataset(trans, data_sources['index']), dataset )
summary = indexer.get_summary( chrom, low, high, **kwargs )
if summary is not None and kwargs.get("mode", "Auto") == "Auto":
# Only check for summary if it's Auto mode (which is the default)
@@ -309,13 +293,7 @@ class TracksController( BaseController,
# Get data provider.
tracks_dataset_type = data_sources['data']
- value = dataset_type_to_data_provider[ tracks_dataset_type ]
- if isinstance( value, dict ):
- # Get converter by dataset extension; if there is no data provider,
- # get the default
- data_provider_class = value.get( dataset.ext, value.get( "default" ) )
- else:
- data_provider_class = value
+ data_provider_class = get_data_provider( name=tracks_dataset_type, original_dataset=dataset )
data_provider = data_provider_class( dataset.get_converted_dataset(trans, tracks_dataset_type), dataset )
# Get and return data from data_provider.
--- a/lib/galaxy/visualization/tracks/data/array_tree.py
+++ /dev/null
@@ -1,86 +0,0 @@
-"""
-Array tree data provider for the Galaxy track browser.
-"""
-
-import pkg_resources
-pkg_resources.require( "numpy" )
-pkg_resources.require( "bx-python" )
-from bx.arrays.array_tree import FileArrayTreeDict
-from math import floor, ceil, log, pow
-from base import TracksDataProvider
-
-class ArrayTreeDataProvider( TracksDataProvider ):
-
- def get_stats( self, chrom ):
- f = open( self.converted_dataset.file_name )
- d = FileArrayTreeDict( f )
- try:
- chrom_array_tree = d[chrom]
- except KeyError:
- f.close()
- return None
-
- root_summary = chrom_array_tree.get_summary( 0, chrom_array_tree.levels )
-
- level = chrom_array_tree.levels - 1
- desired_summary = chrom_array_tree.get_summary( 0, level )
- bs = chrom_array_tree.block_size ** level
-
- frequencies = map(int, desired_summary.frequencies)
- out = [ (i * bs, freq) for i, freq in enumerate(frequencies) ]
-
- f.close()
- return { 'max': float( max(root_summary.maxs) ), \
- 'min': float( min(root_summary.mins) ), \
- 'frequencies': out, \
- 'total_frequency': sum(root_summary.frequencies) }
-
- # Return None instead of NaN to pass jQuery 1.4's strict JSON
- def float_nan(self, n):
- if n != n: # NaN != NaN
- return None
- else:
- return float(n)
-
- def get_data( self, chrom, start, end, **kwargs ):
- if 'stats' in kwargs:
- return self.get_stats(chrom)
-
- f = open( self.converted_dataset.file_name )
- d = FileArrayTreeDict( f )
-
- # Get the right chromosome
- try:
- chrom_array_tree = d[chrom]
- except:
- f.close()
- return None
-
- block_size = chrom_array_tree.block_size
- start = int( start )
- end = int( end )
- resolution = max(1, ceil(float(kwargs['resolution'])))
-
- level = int( floor( log( resolution, block_size ) ) )
- level = max( level, 0 )
- stepsize = block_size ** level
-
- # Is the requested level valid?
- assert 0 <= level <= chrom_array_tree.levels
-
- results = []
- for block_start in range( start, end, stepsize * block_size ):
- # print block_start
- # Return either data point or a summary depending on the level
- indexes = range( block_start, block_start + stepsize * block_size, stepsize )
- if level > 0:
- s = chrom_array_tree.get_summary( block_start, level )
- if s is not None:
- results.extend( zip( indexes, map( self.float_nan, s.sums / s.counts ) ) )
- else:
- l = chrom_array_tree.get_leaf( block_start )
- if l is not None:
- results.extend( zip( indexes, map( self.float_nan, l ) ) )
-
- f.close()
- return results
--- /dev/null
+++ b/lib/galaxy/visualization/tracks/data_providers.py
@@ -0,0 +1,399 @@
+"""
+Data providers for tracks visualizations.
+"""
+
+from math import floor, ceil, log, pow
+import pkg_resources
+pkg_resources.require( "bx-python" ); pkg_resources.require( "pysam" ); pkg_resources.require( "numpy" )
+from bx.interval_index_file import Indexes
+from bx.arrays.array_tree import FileArrayTreeDict
+from galaxy.util.lrucache import LRUCache
+from galaxy.visualization.tracks.summary import *
+from galaxy.datatypes.tabular import Vcf
+from galaxy.datatypes.interval import Bed, Gff
+from pysam import csamtools
+
+MAX_VALS = 5000 # only display first MAX_VALS features
+
+class TracksDataProvider( object ):
+ """ Base class for tracks data providers. """
+
+ """
+ Mapping from column name to index in data. This mapping is used to create
+ filters.
+ """
+ col_name_data_index_mapping = {}
+
+ def __init__( self, converted_dataset=None, original_dataset=None ):
+ """ Create basic data provider. """
+ self.converted_dataset = converted_dataset
+ self.original_dataset = original_dataset
+
+ def get_data( self, chrom, start, end, **kwargs ):
+ """ Returns data in region defined by chrom, start, and end. """
+ # Override.
+ pass
+
+ def get_filters( self ):
+ """
+ Returns filters for provider's data. Return value is a list of
+ filters; each filter is a dictionary with the keys 'name', 'index', 'value'.
+ NOTE: This method uses the original dataset's datatype and metadata to
+ create the filters.
+ """
+ # Get column names.
+ try:
+ column_names = self.original_dataset.datatype.column_names
+ except AttributeError:
+ column_names = range( self.original_dataset.metadata.columns )
+
+ # Dataset must have column types; if not, cannot create filters.
+ try:
+ column_types = self.original_dataset.metadata.column_types
+ except AttributeError:
+ return []
+
+ # Create and return filters.
+ filters = []
+ if self.original_dataset.metadata.viz_filter_columns:
+ for viz_col_index in self.original_dataset.metadata.viz_filter_columns:
+ col_name = column_names[ viz_col_index ]
+ # Make sure that column has a mapped index. If not, do not add filter.
+ try:
+ index = self.col_name_data_index_mapping[ col_name ]
+ except KeyError:
+ continue
+ filters.append(
+ { 'name' : col_name, 'value' : column_types[viz_col_index], \
+ 'index' : index } )
+ return filters
+
+class SummaryTreeDataProvider( TracksDataProvider ):
+ """
+ Summary tree data provider for the Galaxy track browser.
+ """
+
+ CACHE = LRUCache(20) # Store 20 recently accessed indices for performance
+
+ def get_summary( self, chrom, start, end, **kwargs):
+ filename = self.converted_dataset.file_name
+ st = self.CACHE[filename]
+ if st is None:
+ st = summary_tree_from_file( self.converted_dataset.file_name )
+ self.CACHE[filename] = st
+
+ # If chrom is not found in blocks, try removing the first three
+ # characters (e.g. 'chr') and see if that works. This enables the
+ # provider to handle chrome names defined as chrXXX and as XXX.
+ if chrom in st.chrom_blocks:
+ pass
+ elif chrom[3:] in st.chrom_blocks:
+ chrom = chrom[3:]
+ else:
+ return None
+
+ resolution = max(1, ceil(float(kwargs['resolution'])))
+
+ level = ceil( log( resolution, st.block_size ) )
+ level = int(max( level, 0 ))
+ if level <= 0:
+ return None
+
+ stats = st.chrom_stats[chrom]
+ results = st.query(chrom, int(start), int(end), level)
+ if results == "detail":
+ return None
+ elif results == "draw" or level <= 1:
+ return "no_detail"
+ else:
+ return results, stats[level]["max"], stats[level]["avg"], stats[level]["delta"]
+
+class VcfDataProvider( TracksDataProvider ):
+ """
+ VCF data provider for the Galaxy track browser.
+
+ Payload format:
+ [ uid (offset), start, end, ID, reference base(s), alternate base(s), quality score]
+ """
+
+ col_name_data_index_mapping = { 'Qual' : 6 }
+
+ def get_data( self, chrom, start, end, **kwargs ):
+ """ Returns data in region defined by chrom, start, and end. """
+ start, end = int(start), int(end)
+ source = open( self.original_dataset.file_name )
+ index = Indexes( self.converted_dataset.file_name )
+ results = []
+ count = 0
+ message = None
+
+ # If chrom is not found in indexes, try removing the first three
+ # characters (e.g. 'chr') and see if that works. This enables the
+ # provider to handle chrome names defined as chrXXX and as XXX.
+ chrom = str(chrom)
+ if chrom not in index.indexes and chrom[3:] in index.indexes:
+ chrom = chrom[3:]
+
+ for start, end, offset in index.find(chrom, start, end):
+ if count >= MAX_VALS:
+ message = "Only the first %s features are being displayed." % MAX_VALS
+ break
+ count += 1
+ source.seek(offset)
+ feature = source.readline().split()
+
+ payload = [ offset, start, end, \
+ # ID:
+ feature[2], \
+ # reference base(s):
+ feature[3], \
+ # alternative base(s)
+ feature[4], \
+ # phred quality score
+ feature[5] ]
+ results.append(payload)
+
+ return { 'data_type' : 'vcf', 'data': results, 'message': message }
+
+class BamDataProvider( TracksDataProvider ):
+ """
+ Provides access to intervals from a sorted indexed BAM file.
+ """
+ def get_data( self, chrom, start, end, **kwargs ):
+ """
+ Fetch intervals in the region
+ """
+ start, end = int(start), int(end)
+ # Attempt to open the BAM file with index
+ bamfile = csamtools.Samfile( filename=self.original_dataset.file_name, mode='rb', index_filename=self.converted_dataset.file_name )
+ message = None
+ try:
+ data = bamfile.fetch(start=start, end=end, reference=chrom)
+ except ValueError, e:
+ # Some BAM files do not prefix chromosome names with chr, try without
+ if chrom.startswith( 'chr' ):
+ try:
+ data = bamfile.fetch( start=start, end=end, reference=chrom[3:] )
+ except ValueError:
+ return None
+ else:
+ return None
+ # Encode reads as list of dictionaries
+ results = []
+ paired_pending = {}
+ for read in data:
+ if len(results) > MAX_VALS:
+ message = "Only the first %s pairs are being displayed." % MAX_VALS
+ break
+ qname = read.qname
+ if read.is_proper_pair:
+ if qname in paired_pending: # one in dict is always first
+ pair = paired_pending[qname]
+ results.append( [ qname, pair['start'], read.pos + read.rlen, read.seq, [pair['start'], pair['end'], pair['seq']], [read.pos, read.pos + read.rlen, read.seq] ] )
+ # results.append( [read.qname, pair['start'], read.pos + read.rlen, qname, [pair['start'], pair['end']], [read.pos, read.pos + read.rlen] ] )
+ del paired_pending[qname]
+ else:
+ paired_pending[qname] = { 'start': read.pos, 'end': read.pos + read.rlen, 'seq': read.seq, 'mate_start': read.mpos, 'rlen': read.rlen }
+ else:
+ results.append( [qname, read.pos, read.pos + read.rlen, read.seq] )
+ # take care of reads whose mates are out of range
+ for qname, read in paired_pending.iteritems():
+ if read['mate_start'] < read['start']:
+ start = read['mate_start']
+ end = read['end']
+ r1 = [read['mate_start'], read['mate_start'] + read['rlen']]
+ r2 = [read['start'], read['end'], read['seq']]
+ else:
+ start = read['start']
+ end = read['mate_start'] + read['rlen']
+ r1 = [read['start'], read['end'], read['seq']]
+ r2 = [read['mate_start'], read['mate_start'] + read['rlen']]
+
+ results.append( [ qname, start, end, read['seq'], r1, r2 ] )
+
+ bamfile.close()
+ return { 'data': results, 'message': message }
+
+class ArrayTreeDataProvider( TracksDataProvider ):
+ """
+ Array tree data provider for the Galaxy track browser.
+ """
+ def get_stats( self, chrom ):
+ f = open( self.converted_dataset.file_name )
+ d = FileArrayTreeDict( f )
+ try:
+ chrom_array_tree = d[chrom]
+ except KeyError:
+ f.close()
+ return None
+
+ root_summary = chrom_array_tree.get_summary( 0, chrom_array_tree.levels )
+
+ level = chrom_array_tree.levels - 1
+ desired_summary = chrom_array_tree.get_summary( 0, level )
+ bs = chrom_array_tree.block_size ** level
+
+ frequencies = map(int, desired_summary.frequencies)
+ out = [ (i * bs, freq) for i, freq in enumerate(frequencies) ]
+
+ f.close()
+ return { 'max': float( max(root_summary.maxs) ), \
+ 'min': float( min(root_summary.mins) ), \
+ 'frequencies': out, \
+ 'total_frequency': sum(root_summary.frequencies) }
+
+ # Return None instead of NaN to pass jQuery 1.4's strict JSON
+ def float_nan(self, n):
+ if n != n: # NaN != NaN
+ return None
+ else:
+ return float(n)
+
+ def get_data( self, chrom, start, end, **kwargs ):
+ if 'stats' in kwargs:
+ return self.get_stats(chrom)
+
+ f = open( self.converted_dataset.file_name )
+ d = FileArrayTreeDict( f )
+
+ # Get the right chromosome
+ try:
+ chrom_array_tree = d[chrom]
+ except:
+ f.close()
+ return None
+
+ block_size = chrom_array_tree.block_size
+ start = int( start )
+ end = int( end )
+ resolution = max(1, ceil(float(kwargs['resolution'])))
+
+ level = int( floor( log( resolution, block_size ) ) )
+ level = max( level, 0 )
+ stepsize = block_size ** level
+
+ # Is the requested level valid?
+ assert 0 <= level <= chrom_array_tree.levels
+
+ results = []
+ for block_start in range( start, end, stepsize * block_size ):
+ # print block_start
+ # Return either data point or a summary depending on the level
+ indexes = range( block_start, block_start + stepsize * block_size, stepsize )
+ if level > 0:
+ s = chrom_array_tree.get_summary( block_start, level )
+ if s is not None:
+ results.extend( zip( indexes, map( self.float_nan, s.sums / s.counts ) ) )
+ else:
+ l = chrom_array_tree.get_leaf( block_start )
+ if l is not None:
+ results.extend( zip( indexes, map( self.float_nan, l ) ) )
+
+ f.close()
+ return results
+
+class IntervalIndexDataProvider( TracksDataProvider ):
+ """
+ Interval index data provider for the Galaxy track browser.
+
+ Payload format: [ uid (offset), start, end, name, strand, thick_start, thick_end, blocks ]
+ """
+ def get_data( self, chrom, start, end, **kwargs ):
+ start, end = int(start), int(end)
+ source = open( self.original_dataset.file_name )
+ index = Indexes( self.converted_dataset.file_name )
+ results = []
+ count = 0
+ message = None
+
+ # If chrom is not found in indexes, try removing the first three
+ # characters (e.g. 'chr') and see if that works. This enables the
+ # provider to handle chrome names defined as chrXXX and as XXX.
+ chrom = str(chrom)
+ if chrom not in index.indexes and chrom[3:] in index.indexes:
+ chrom = chrom[3:]
+
+ for start, end, offset in index.find(chrom, start, end):
+ if count >= MAX_VALS:
+ message = "Only the first %s features are being displayed." % MAX_VALS
+ break
+ count += 1
+ source.seek(offset)
+ feature = source.readline().split()
+ payload = [ offset, start, end ]
+ # TODO: can we use column metadata to fill out payload?
+ # TODO: use function to set payload data
+ if "no_detail" not in kwargs:
+ length = len(feature)
+ if isinstance( self.original_dataset.datatype, Gff ):
+ # GFF dataset.
+ if length >= 3:
+ payload.append( feature[2] ) # name
+ if length >= 7:
+ payload.append( feature[6] ) # strand
+ elif isinstance( self.original_dataset.datatype, Bed ):
+ # BED dataset.
+ if length >= 4:
+ payload.append(feature[3]) # name
+ if length >= 6: # strand
+ payload.append(feature[5])
+
+ if length >= 8:
+ payload.append(int(feature[6]))
+ payload.append(int(feature[7]))
+
+ if length >= 12:
+ block_sizes = [ int(n) for n in feature[10].split(',') if n != '']
+ block_starts = [ int(n) for n in feature[11].split(',') if n != '' ]
+ blocks = zip(block_sizes, block_starts)
+ payload.append( [ (start + block[1], start + block[1] + block[0]) for block in blocks] )
+
+ results.append(payload)
+
+ return { 'data': results, 'message': message }
+
+#
+# Helper methods.
+#
+
+# Mapping from dataset type name to a class that can fetch data from a file of that
+# type. First key is converted dataset type; if result is another dict, second key
+# is original dataset type. TODO: This needs to be more flexible.
+dataset_type_name_to_data_provider = {
+ "array_tree": ArrayTreeDataProvider,
+ "interval_index": { "vcf": VcfDataProvider, "default" : IntervalIndexDataProvider },
+ "bai": BamDataProvider,
+ "summary_tree": SummaryTreeDataProvider
+}
+
+dataset_type_to_data_provider = {
+ Vcf : VcfDataProvider
+}
+
+def get_data_provider( name=None, original_dataset=None ):
+ """
+ Returns data provider class by name and/or original dataset.
+ """
+ data_provider = None
+ if name:
+ value = dataset_type_name_to_data_provider[ name ]
+ if isinstance( value, dict ):
+ # Get converter by dataset extension; if there is no data provider,
+ # get the default.
+ data_provider = value.get( original_dataset.ext, value.get( "default" ) )
+ else:
+ data_provider = value
+ elif original_dataset:
+ # Look for data provider in mapping.
+ data_provider = \
+ dataset_type_to_data_provider.get( original_dataset.datatype.__class__, None )
+
+ # If get_track_type is available, then dataset can be added to trackster
+ # and hence has at least a generic data provider.
+ try:
+ original_dataset.datatype.get_track_type()
+ data_provider = TracksDataProvider
+ except:
+ pass
+ return data_provider
+
--- a/lib/galaxy/visualization/tracks/data/vcf.py
+++ /dev/null
@@ -1,53 +0,0 @@
-import pkg_resources; pkg_resources.require( "bx-python" )
-from bx.interval_index_file import Indexes
-from galaxy.datatypes.tabular import Vcf
-from base import TracksDataProvider
-
-MAX_VALS = 5000 # only display first MAX_VALS features
-
-class VcfDataProvider( TracksDataProvider ):
- """
- VCF data provider for the Galaxy track browser.
-
- Payload format:
- [ uid (offset), start, end, ID, reference base(s), alternate base(s), quality score]
- """
-
- col_name_data_index_mapping = { 'Qual' : 6 }
-
- def get_data( self, chrom, start, end, **kwargs ):
- """ Returns data in region defined by chrom, start, and end. """
- start, end = int(start), int(end)
- source = open( self.original_dataset.file_name )
- index = Indexes( self.converted_dataset.file_name )
- results = []
- count = 0
- message = None
-
- # If chrom is not found in indexes, try removing the first three
- # characters (e.g. 'chr') and see if that works. This enables the
- # provider to handle chrome names defined as chrXXX and as XXX.
- chrom = str(chrom)
- if chrom not in index.indexes and chrom[3:] in index.indexes:
- chrom = chrom[3:]
-
- for start, end, offset in index.find(chrom, start, end):
- if count >= MAX_VALS:
- message = "Only the first %s features are being displayed." % MAX_VALS
- break
- count += 1
- source.seek(offset)
- feature = source.readline().split()
-
- payload = [ offset, start, end, \
- # ID:
- feature[2], \
- # reference base(s):
- feature[3], \
- # alternative base(s)
- feature[4], \
- # phred quality score
- feature[5] ]
- results.append(payload)
-
- return { 'data_type' : 'vcf', 'data': results, 'message': message }
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -9,7 +9,7 @@ from galaxy.model.orm import *
from galaxy.workflow.modules import *
from galaxy.web.framework import simplejson
from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, TextArea, TextField, WorkflowField
-from galaxy.visualization.tracks.data.base import dataset_to_data_provider
+from galaxy.visualization.tracks.data_providers import get_data_provider
from Cheetah.Template import Template
@@ -170,7 +170,7 @@ class UsesVisualization( SharableItemSec
prefs = {}
dataset = hda_query.get( dataset_id )
track_type, _ = dataset.datatype.get_track_type()
- track_data_provider_class = dataset_to_data_provider( dataset )
+ track_data_provider_class = get_data_provider( original_dataset=dataset )
track_data_provider = track_data_provider_class( original_dataset=dataset )
tracks.append( {
--- a/lib/galaxy/visualization/tracks/data/base.py
+++ /dev/null
@@ -1,77 +0,0 @@
-from galaxy.datatypes.tabular import Vcf
-from galaxy.visualization.tracks import data
-
-class TracksDataProvider( object ):
- """ Base class for tracks data providers. """
-
- """
- Mapping from column name to index in data. This mapping is used to create
- filters.
- """
- col_name_data_index_mapping = {}
-
- def __init__( self, converted_dataset=None, original_dataset=None ):
- """ Create basic data provider. """
- self.converted_dataset = converted_dataset
- self.original_dataset = original_dataset
-
- def get_data( self, chrom, start, end, **kwargs ):
- """ Returns data in region defined by chrom, start, and end. """
- # Override.
- pass
-
- def get_filters( self ):
- """
- Returns filters for provider's data. Return value is a list of
- filters; each filter is a dictionary with the keys 'name', 'index', 'value'.
- NOTE: This method uses the original dataset's datatype and metadata to
- create the filters.
- """
- # Get column names.
- try:
- column_names = self.original_dataset.datatype.column_names
- except AttributeError:
- column_names = range( self.original_dataset.metadata.columns )
-
- # Dataset must have column types; if not, cannot create filters.
- try:
- column_types = self.original_dataset.metadata.column_types
- except AttributeError:
- return []
-
- # Create and return filters.
- filters = []
- if self.original_dataset.metadata.viz_filter_columns:
- for viz_col_index in self.original_dataset.metadata.viz_filter_columns:
- col_name = column_names[ viz_col_index ]
- # Make sure that column has a mapped index. If not, do not add filter.
- try:
- index = self.col_name_data_index_mapping[ col_name ]
- except KeyError:
- continue
- filters.append(
- { 'name' : col_name, 'value' : column_types[viz_col_index], \
- 'index' : index } )
- return filters
-
-
-#
-# Helper methods.
-#
-
-def dataset_to_data_provider( dataset=None ):
- """
- Returns data provider for a dataset.
- """
- # TODO: merge this method with the dict in tracks controller to provide a
- # unified way to get data providers based on dataset/converted dataset type.
- if isinstance( dataset.datatype, Vcf ):
- return data.vcf.VcfDataProvider
- else:
- try:
- # If get_track_type is available, then generic data provider
- # should work.
- dataset.datatype.get_track_type()
- return TracksDataProvider
- except e:
- return None
1
0

galaxy-dist commit 51873f4ee7a6: Fixed typo in FloatToolParameter doctest
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User peterjc <p.j.a.cock(a)googlemail.com>
# Date 1286450288 -3600
# Node ID 51873f4ee7a6a3535dc028c66259a5381cc46c9c
# Parent e082f7f28e628263faacb652ef9d0ee9b37d8532
Fixed typo in FloatToolParameter doctest
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -215,7 +215,7 @@ class FloatToolParameter( TextToolParame
"""
Parameter that takes a real number value.
- >>> p = FloatToolParameter( None, XML( '<param name="blah" type="integer" size="4" value="3.141592" />' ) )
+ >>> p = FloatToolParameter( None, XML( '<param name="blah" type="float" size="4" value="3.141592" />' ) )
>>> print p.name
blah
>>> print p.get_html()
1
0

galaxy-dist commit 7c5d27bf6a41: Make VCF a first-class type in trackster. Added VCF data provider and VCF tracks type to trackster; new functionality is used to show reference base and alternate base for SNPs in VCF tracks. Also refactored data providers so that they inherit from a base provider.
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 jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1286501181 14400
# Node ID 7c5d27bf6a41cd215088b8b9081572a30735bd43
# Parent d85159be56e854731822a314a2e982e0c6987389
Make VCF a first-class type in trackster. Added VCF data provider and VCF tracks type to trackster; new functionality is used to show reference base and alternate base for SNPs in VCF tracks. Also refactored data providers so that they inherit from a base provider.
--- a/lib/galaxy/visualization/tracks/data/interval_index.py
+++ b/lib/galaxy/visualization/tracks/data/interval_index.py
@@ -8,15 +8,11 @@ Payload format: [ uid (offset), start, e
import pkg_resources; pkg_resources.require( "bx-python" )
from bx.interval_index_file import Indexes
from galaxy.datatypes.interval import Bed, Gff
-from galaxy.datatypes.tabular import Vcf
+from base import TracksDataProvider
MAX_VALS = 5000 # only display first MAX_VALS features
-class IntervalIndexDataProvider( object ):
- def __init__( self, converted_dataset, original_dataset ):
- self.original_dataset = original_dataset
- self.converted_dataset = converted_dataset
-
+class IntervalIndexDataProvider( TracksDataProvider ):
def get_data( self, chrom, start, end, **kwargs ):
start, end = int(start), int(end)
source = open( self.original_dataset.file_name )
@@ -66,10 +62,7 @@ class IntervalIndexDataProvider( object
block_starts = [ int(n) for n in feature[11].split(',') if n != '' ]
blocks = zip(block_sizes, block_starts)
payload.append( [ (start + block[1], start + block[1] + block[0]) for block in blocks] )
- elif isinstance( self.original_dataset.datatype, Vcf ):
- # VCF dataset.
- payload.append( feature[2] ) # name
-
+
results.append(payload)
return { 'data': results, 'message': message }
--- a/lib/galaxy/visualization/tracks/data/bam.py
+++ b/lib/galaxy/visualization/tracks/data/bam.py
@@ -9,10 +9,11 @@ from pysam import csamtools
from math import floor, ceil, log
import logging
log = logging.getLogger(__name__)
+from base import TracksDataProvider
MAX_VALS = 5000 # only display first MAX_VALS datapoints
-class BamDataProvider( object ):
+class BamDataProvider( TracksDataProvider ):
"""
Provides access to intervals from a sorted indexed BAM file.
"""
--- 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=100,DATA_ERROR="There was an error in indexing this dataset. ",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),PX_PER_CHAR=CONTEXT.measureText("A").width,RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function()
{RIGHT_STRAND_INV=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,c,e,d,b){this.container=a;this.vis_id=d;this.dbkey=b;this.title=e;this.chrom=c;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.
min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{init:function(){var c=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").hide().appendTo(c);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide().appendTo(c);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);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/>").addClass("overview-viewport").appendTo(this.overview);this.overview_box_background=$("<div/>").addClass("overview-boxback").app
endTo(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(){void (0)}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.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.hi
de();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(d){if(d.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=d.chrom_info;var f='<option value="">Select Chrom/Contig</option>';for(i in a.chrom_data){var e=a.chrom_data[i]["chrom"];f+='<option value="'+e+'">'+e+"</option>"}a.chrom_select.html(f);a.intro_div.show();a.content_div.hide();a.chrom_select.b
ind("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.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 j=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(j/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(j){var f=Math.min(j.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(j.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(k){var f=Math.min(k.clientX,this.drag_origin_x),d=Math.max(k.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),j=a.low;a.low=Math.round(f/g*h)+j;a.high=Math.round(d/g*h)+j;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(th
is,this.top_labeltrack));this.add_label_track(new LabelTrack(this,this.nav_labeltrack))},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(d,a,f){var c=this;var e=$.grep(c.chrom_data,function(h,j){return h.chrom===d})[0];if(e===undefined){return}if(d!==c.chrom){c.chrom=d;if(c.chrom===""){c.intro_div.show();c.content_div.hide()}else{c.intro_div.hide();c.content_div.show()}c.chrom_select.val(c.chrom);c.max_high=e.len;c.reset();c.redraw(true);for(var g in c.tracks){var b=c.tracks[g];if(b.init){b.init()}}}if(a!==undefined&&f!==undefined){c.low=Math.max(a,0);c.high=Math.min(f,c.max_high)}c.overview_viewport.find("canvas").remove();c.redraw()},go_to:function(f){var k=this,b=f.split(":"),h=b[0],j=b[1];if(j!==undefined){try{var g=j.split("-"),a=parseInt(g[0].replace(/,/g,"")),d=parseInt(g[1].replace(/,/g,""))}catch(c){return false}}k.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_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 d=this.high-this.low,b=this.low,f=this.high;if(b<this.max_low){b=this.max_low}if(f>this.max_high){f=this.max_high}if(this.high!==0&&d<this.min_separation){f=b+this.min_separation}this.low=Math.floor(b);th
is.high=Math.ceil(f);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 e=(this.low/(this.max_high-this.max_low))*this.overview_viewport.width();var g=(this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width();this.overview_box.css({left:e,width:Math.max(12,g)}).show();if(this.overview_highlight){this.overview_highlight.css({left:e,width:g})}this.update_location(this.low,this.high);if(!h){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+thi
s.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);this.high=Math.round(c+a);this.redraw()}});var Track=function(b,a,c){this.name=b;this.parent_element=c;this.view=a;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track");if(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);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.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.con
tent_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("he
ight",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 d=this,c=d.view;if(d.hidden){return}if(d.display_modes!==undefined){if(d.mode_div===undefined){d.mode_div=$("<div class='right-float menubutton popup' />").appendTo(d.header_div);var h=d.display_modes[0];d.mode=h;d.mode_div.text(h);var a=function(j){d.mode_div.text(j);d.mode=j;d.tile_cache.clear();d.draw()};var f={};for(var e in d.display_modes){var g=d.display_modes[e];f[g]=function(j){return function(){a(j)}}(g)}make_popupmenu(d.mode_div,f)}else{d.mode_div.hide()}}var b={};b["Set track as overview"]=function(){c.overview_viewport.find("canvas").remove();d.is_overview=true;d.set_overview();for(var j in c.tracks){if(c.tracks[j]!==d){c.tracks[j].is_overview=false}}};b["Edit configuration"]=function(){var k=function(){hide_modal()};var j=function(){d.update_options(d.track_id);hide_modal()};show_modal("Configure Trac
k",d.gen_options(d.track_id),{Cancel:k,OK:j})};b.Remove=function(){c.remove_track(d);if(c.num_tracks===0){$("#no-tracks").show()}};make_popupmenu(d.name_div,b)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var j=this.view.low,e=this.view.high,f=e-j,d=this.view.resolution;var l=$("<div style='position: relative;'></div>"),m=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(l),this.max_height=0;var a=Math.floor(j/d/DENSITY);while((a*DENSITY*d)<e){var k=this.content_div.width()+"_"+m+"_"+a;var c=this.tile_cache.get(k);if(c){var g=a*DENSITY*d;var b=(g-j)*m;if(this.left_offset){b-=this.left_offset}c.css({left:b});l.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height+"px")}else{this.delayed_draw(this,k,j,e,a,d,l,m)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_el
ement){if(!c.initial_canvas){c.initial_canvas=$(tile_element).clone();var l=tile_element.get(0).getContext("2d");var j=c.initial_canvas.get(0).getContext("2d");var k=l.getImageData(0,0,l.canvas.width,l.canvas.height);j.putImageData(k,0,0);c.set_overview()}c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)},set_overview:function(){var a=this.view;a.overview_viewport.height(a.default_overview_height);a.overview_box.height(a.default_overview_height);if(this.initial_canvas&&this.is_overview){if(!a.overview_highlight){a.overview_highlight=$("<div />").addClass("overview-highlight").appendTo(a.overview_viewport)}a.overview_viewport.append(this.initial_canvas);a.overview_highlight.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.h
idden=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.height_px=12;this.container_div.addClass("reference-track");this.dummy_canvas=$("<canvas></canvas>").get(0).getContext("2d");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.proto
type,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,e=$("<canvas class='tile'></canvas>"),n=e.get(0).getContext("2d"),j=f+"_"+b;if(o>PX_PER_CHAR){if(this.data_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-this.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o);n.fillText(m[h],a+this.left_offset,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b
,a,c){this.track_type="LineTrack";this.display_modes=["Line","Filled","Intensity"];this.mode="Line";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={min_value:undefined,max_value:undefined,mode:"Line"};if(c.min_value!==undefined){this.prefs.min_value=c.min_value}if(c.max_value!==undefined){this.prefs.max_value=c.max_value}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.
vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(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:"relative",top:"32px",left:"10px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+32+"px",left:"10px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(p,r,c,
e){if(this.vertical_range===undefined){return}var s=r*DENSITY*p,a=DENSITY*p,b=$("<canvas class='tile'></canvas>"),v=p+"_"+r;if(this.data_cache.get(v)===undefined){this.get_data(p,r);return}var j=this.data_cache.get(v);if(j===null){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var o=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,n=this.vertical_range,t=this.total_frequency,d=this.height_px,m=this.mode;o.beginPath();if(data.length>1){var f=Math.ceil((data[1][0]-data[0][0])*e)}else{var f=10}var u,h;for(var q=0;q<data.length;q++){u=(data[q][0]-s)*e;h=data[q][1];if(m=="Intensity"){if(h===null){continue}if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/n*255);o.fillStyle="rgb("+h+","+h+","+h+")";o.fillRect(u,0,f,this.height_px)}else{if(h===null){if(k&&m==="Filled"){o.lineTo(u,d)}k=false;continue}else{if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/n*d);if(k)
{o.lineTo(u,h)}else{k=true;if(m==="Filled"){o.moveTo(u,d);o.lineTo(u,h)}else{o.moveTo(u,h)}}}}}if(m==="Filled"){if(k){o.lineTo(u,d)}o.fill()}else{o.stroke()}c.append(b);return b},gen_options:function(k){var a=$("<div />").addClass("form-row");var e="track_"+k+"_minval",h=$("<label></label>").attr("for",e).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),j=$("<input></input>").attr("id",e).val(b),g="track_"+k+"_maxval",d=$("<label></label>").attr("for",g).text("Max value:"),f=(this.prefs.max_value===undefined?"":this.prefs.max_value),c=$("<input></input>").attr("id",g).val(f);return a.append(h).append(j).append(d).append(c)},update_options:function(c){var a=$("#track_"+c+"_minval").val(),b=$("#track_"+c+"_maxval").val();if(a!==this.prefs.min_value||b!==this.prefs.max_value){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(b);this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+c+"_minval").text(this.pre
fs.min_value);$("#linetrack_"+c+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=2;this.summary_draw_height=20;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:"black",label_color:"black",show_counts:true};if(c.block_color!==undefined){this.prefs.block_color=c.block_color}if(c.label_color!==undefined){this.prefs.label_color=c.label_color}if(c.show_counts!==undefined){this.prefs.sh
ow_counts=c.show_counts}};$.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.view.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,h,c,r){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=r;this.s_e_by_tile[a]={}}var n=this.inc_slots[a].w_scale,z=[],l=0,b=$("<canvas></canvas>").get(0).getContext("2d"),o=this.view.max_low;var B=[];if(this.inc_slots[a].mode!==r){delete this.inc_slots[a];this.inc_slots[a]={mode:r,w_scale:n};delete this.s_
e_by_tile[a];this.s_e_by_tile[a]={}}for(var w=0,x=h.length;w<x;w++){var g=h[w],m=g[0];if(this.inc_slots[a][m]!==undefined){l=Math.max(l,this.inc_slots[a][m]);B.push(this.inc_slots[a][m])}else{z.push(w)}}for(var w=0,x=z.length;w<x;w++){var g=h[z[w]],m=g[0],s=g[1],d=g[2],q=g[3],e=Math.floor((s-o)*n),f=Math.ceil((d-o)*n);if(q!==undefined&&!c){var t=b.measureText(q).width;if(e-t<0){f+=t}else{e-=t}}var v=0;while(true){var p=true;if(this.s_e_by_tile[a][v]!==undefined){for(var u=0,A=this.s_e_by_tile[a][v].length;u<A;u++){var y=this.s_e_by_tile[a][v][u];if(f>y[0]&&e<y[1]){p=false;break}}}if(p){if(this.s_e_by_tile[a][v]===undefined){this.s_e_by_tile[a][v]=[]}this.s_e_by_tile[a][v].push([e,f]);this.inc_slots[a][m]=v;l=Math.max(l,v);break}v++}}return l},rect_or_text:function(n,o,f,m,b,d,k,e,h){n.textAlign="center";var j=Math.round(o/2);if((this.mode==="Pack"||this.mode==="Auto")&&d!==undefined&&o>PX_PER_CHAR){n.fillStyle=this.prefs.block_color;n.fillRect(k,h+1,e,9);n.fillStyle="#eee";f
or(var g=0,l=d.length;g<l;g++){if(b+g>=f&&b+g<=m){var a=Math.floor(Math.max(0,(b+g-f)*o));n.fillText(d[g],a+this.left_offset+j,h+9)}}}else{n.fillStyle=this.prefs.block_color;n.fillRect(k,h+4,e,3)}},draw_tile:function(aa,l,o,an){var H=l*DENSITY*aa,ag=(l+1)*DENSITY*aa,G=ag-H;var ah=(!this.initial_canvas?"initial":H+"_"+ag);var C=this.data_cache.get(ah);var d;if(C===undefined||(this.mode!=="Auto"&&C.dataset_type==="summary_tree")){this.data_queue[[H,ag]]=true;this.get_data(H,ag);return}var a=Math.ceil(G*an),O=$("<canvas class='tile'></canvas>"),ac=this.prefs.label_color,g=this.prefs.block_color,n=this.mode,r=25,Y=(n==="Squish")||(n==="Dense")&&(n!=="Pack")||(n==="Auto"&&(C.extra_info==="no_detail")),S=this.left_offset,am,v,ao;if(C.dataset_type==="summary_tree"){v=this.summary_draw_height}else{if(n==="Dense"){v=r;ao=10}else{ao=(Y?this.vertical_nodetail_px:this.vertical_detail_px);var s=(an<0.0001?1/view.zoom_res:an);v=this.incremental_slots(s,C.data,Y,n)*ao+r;am=this.inc_slots[s
]}}O.css({position:"absolute",top:0,left:(H-this.view.low)*an-S});O.get(0).width=a+S;O.get(0).height=v;o.parent().css("height",Math.max(this.height_px,v)+"px");var D=O.get(0).getContext("2d");D.fillStyle=g;D.font=this.default_font;D.textAlign="right";if(C.dataset_type=="summary_tree"){var N,K=55,af=255-K,h=af*2/3,U=C.data,F=C.max,m=C.avg,b=Math.ceil(C.delta*an);for(var aj=0,B=U.length;aj<B;aj++){var W=Math.floor((U[aj][0]-H)*an);var V=U[aj][1];if(!V){continue}N=Math.floor(af-(V/F)*af);D.fillStyle="rgb("+N+","+N+","+N+")";D.fillRect(W+S,0,b,this.summary_draw_height);if(this.prefs.show_counts&&D.measureText(V).width<b){if(N>h){D.fillStyle="black"}else{D.fillStyle="#ddd"}D.textAlign="center";D.fillText(V,W+S+(b/2),12)}}d="Summary";o.append(O);return O}if(C.message){O.css({border:"solid red","border-width":"2px 2px 2px 0px"});D.fillStyle="red";D.textAlign="left";D.fillText(C.message,100+S,ao)}var al=C.data;var ai=0;for(var aj=0,B=al.length;aj<B;aj++){var P=al[aj],M=P[0],ak=P[1],
X=P[2],I=P[3];if(ak<=ag&&X>=H){var Z=Math.floor(Math.max(0,(ak-H)*an)),E=Math.ceil(Math.min(a,Math.max(0,(X-H)*an))),T=(n==="Dense"?1:(1+am[M]))*ao;if(C.dataset_type==="bai"){D.fillStyle=g;if(P[4] instanceof Array){var w=Math.floor(Math.max(0,(P[4][0]-H)*an)),L=Math.ceil(Math.min(a,Math.max(0,(P[4][1]-H)*an))),u=Math.floor(Math.max(0,(P[5][0]-H)*an)),q=Math.ceil(Math.min(a,Math.max(0,(P[5][1]-H)*an)));if(P[4][1]>=H&&P[4][0]<=ag){this.rect_or_text(D,an,H,ag,P[4][0],P[4][2],w+S,L-w,T)}if(P[5][1]>=H&&P[5][0]<=ag){this.rect_or_text(D,an,H,ag,P[5][0],P[5][2],u+S,q-u,T)}if(u>L){D.fillStyle="#999";D.fillRect(L+S,T+5,u-L,1)}}else{D.fillStyle=g;this.rect_or_text(D,an,H,ag,ak,I,Z+S,E-Z,T)}if(n!=="Dense"&&!Y&&ak>H){D.fillStyle=this.prefs.label_color;if(l===0&&Z-D.measureText(I).width<0){D.textAlign="left";D.fillText(M,E+2+S,T+8)}else{D.textAlign="right";D.fillText(M,Z-2+S,T+8)}D.fillStyle=g}}else{if(C.dataset_type==="interval_index"){if(Y){D.fillStyle=g;D.fillRect(Z+S,T+5,E-Z,1)}else{v
ar A=P[4],R=P[5],ab=P[6],f=P[7];var z,ad,J=null,ap=null;if(R&&ab){J=Math.floor(Math.max(0,(R-H)*an));ap=Math.ceil(Math.min(a,Math.max(0,(ab-H)*an)))}if(n!=="Dense"&&I!==undefined&&ak>H){D.fillStyle=ac;if(l===0&&Z-D.measureText(I).width<0){D.textAlign="left";D.fillText(I,E+2+S,T+8)}else{D.textAlign="right";D.fillText(I,Z-2+S,T+8)}D.fillStyle=g}if(f){if(A){if(A=="+"){D.fillStyle=RIGHT_STRAND}else{if(A=="-"){D.fillStyle=LEFT_STRAND}}D.fillRect(Z+S,T,E-Z,10);D.fillStyle=g}for(var ah=0,e=f.length;ah<e;ah++){var p=f[ah],c=Math.floor(Math.max(0,(p[0]-H)*an)),Q=Math.ceil(Math.min(a,Math.max((p[1]-H)*an)));if(c>Q){continue}z=5;ad=3;D.fillRect(c+S,T+ad,Q-c,z);if(J!==undefined&&!(c>ap||Q<J)){z=9;ad=1;var ae=Math.max(c,J),t=Math.min(Q,ap);D.fillRect(ae+S,T+ad,t-ae,z)}}}else{z=9;ad=1;D.fillRect(Z+S,T+ad,E-Z,z);if(P.strand){if(P.strand=="+"){D.fillStyle=RIGHT_STRAND_INV}else{if(P.strand=="-"){D.fillStyle=LEFT_STRAND_INV}}D.fillRect(Z+S,T,E-Z,10);D.fillStyle=g}}}}}ai++}}o.append(O);return
O},gen_options:function(j){var a=$("<div />").addClass("form-row");var e="track_"+j+"_block_color",l=$("<label />").attr("for",e).text("Block color:"),m=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),k="track_"+j+"_label_color",g=$("<label />").attr("for",k).text("Text color:"),h=$("<input />").attr("id",k).attr("name",k).val(this.prefs.label_color),f="track_"+j+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(l).append(m).append(g).append(h).append(d)},update_options:function(e){var b=$("#track_"+e+"_block_color").val(),d=$("#track_"+e+"_label_color").val(),c=$("#track_"+e+"_mode option:selected").val(),a=$("#track_"+e+"_show_count").attr("checked");if(b!==this.prefs.block_color||d!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_c
olor=b;this.prefs.label_color=d;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,c){FeatureTrack.call(this,d,b,a,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
+var DENSITY=200,FEATURE_LEVELS=10,MAX_FEATURE_DEPTH=100,DATA_ERROR="There was an error in indexing this dataset. ",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),PX_PER_CHAR=CONTEXT.measureText("A").width,RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function()
{RIGHT_STRAND_INV=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,c,e,d,b){this.container=a;this.vis_id=d;this.dbkey=b;this.title=e;this.chrom=c;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.
min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{init:function(){var c=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").hide().appendTo(c);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide().appendTo(c);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);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/>").addClass("overview-viewport").appendTo(this.overview);this.overview_box_background=$("<div/>").addClass("overview-boxback").app
endTo(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(){void (0)}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.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.hi
de();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(d){if(d.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=d.chrom_info;var f='<option value="">Select Chrom/Contig</option>';for(i in a.chrom_data){var e=a.chrom_data[i]["chrom"];f+='<option value="'+e+'">'+e+"</option>"}a.chrom_select.html(f);a.intro_div.show();a.content_div.hide();a.chrom_select.b
ind("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.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 j=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(j/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(j){var f=Math.min(j.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(j.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(k){var f=Math.min(k.clientX,this.drag_origin_x),d=Math.max(k.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),j=a.low;a.low=Math.round(f/g*h)+j;a.high=Math.round(d/g*h)+j;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(th
is,this.top_labeltrack));this.add_label_track(new LabelTrack(this,this.nav_labeltrack))},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(d,a,f){var c=this;var e=$.grep(c.chrom_data,function(h,j){return h.chrom===d})[0];if(e===undefined){return}if(d!==c.chrom){c.chrom=d;if(c.chrom===""){c.intro_div.show();c.content_div.hide()}else{c.intro_div.hide();c.content_div.show()}c.chrom_select.val(c.chrom);c.max_high=e.len;c.reset();c.redraw(true);for(var g in c.tracks){var b=c.tracks[g];if(b.init){b.init()}}}if(a!==undefined&&f!==undefined){c.low=Math.max(a,0);c.high=Math.min(f,c.max_high)}c.overview_viewport.find("canvas").remove();c.redraw()},go_to:function(f){var k=this,b=f.split(":"),h=b[0],j=b[1];if(j!==undefined){try{var g=j.split("-"),a=parseInt(g[0].replace(/,/g,"")),d=parseInt(g[1].replace(/,/g,""))}catch(c){return false}}k.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_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 d=this.high-this.low,b=this.low,f=this.high;if(b<this.max_low){b=this.max_low}if(f>this.max_high){f=this.max_high}if(this.high!==0&&d<this.min_separation){f=b+this.min_separation}this.low=Math.floor(b);th
is.high=Math.ceil(f);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 e=(this.low/(this.max_high-this.max_low))*this.overview_viewport.width();var g=(this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width();this.overview_box.css({left:e,width:Math.max(12,g)}).show();if(this.overview_highlight){this.overview_highlight.css({left:e,width:g})}this.update_location(this.low,this.high);if(!h){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+thi
s.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);this.high=Math.round(c+a);this.redraw()}});var Track=function(b,a,c){this.name=b;this.parent_element=c;this.view=a;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track");if(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);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.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.con
tent_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("he
ight",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 d=this,c=d.view;if(d.hidden){return}if(d.display_modes!==undefined){if(d.mode_div===undefined){d.mode_div=$("<div class='right-float menubutton popup' />").appendTo(d.header_div);var h=d.display_modes[0];d.mode=h;d.mode_div.text(h);var a=function(j){d.mode_div.text(j);d.mode=j;d.tile_cache.clear();d.draw()};var f={};for(var e in d.display_modes){var g=d.display_modes[e];f[g]=function(j){return function(){a(j)}}(g)}make_popupmenu(d.mode_div,f)}else{d.mode_div.hide()}}var b={};b["Set track as overview"]=function(){c.overview_viewport.find("canvas").remove();d.is_overview=true;d.set_overview();for(var j in c.tracks){if(c.tracks[j]!==d){c.tracks[j].is_overview=false}}};b["Edit configuration"]=function(){var k=function(){hide_modal()};var j=function(){d.update_options(d.track_id);hide_modal()};show_modal("Configure Trac
k",d.gen_options(d.track_id),{Cancel:k,OK:j})};b.Remove=function(){c.remove_track(d);if(c.num_tracks===0){$("#no-tracks").show()}};make_popupmenu(d.name_div,b)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var j=this.view.low,e=this.view.high,f=e-j,d=this.view.resolution;var l=$("<div style='position: relative;'></div>"),m=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(l),this.max_height=0;var a=Math.floor(j/d/DENSITY);while((a*DENSITY*d)<e){var k=this.content_div.width()+"_"+m+"_"+a;var c=this.tile_cache.get(k);if(c){var g=a*DENSITY*d;var b=(g-j)*m;if(this.left_offset){b-=this.left_offset}c.css({left:b});l.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height+"px")}else{this.delayed_draw(this,k,j,e,a,d,l,m)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_el
ement){if(!c.initial_canvas){c.initial_canvas=$(tile_element).clone();var l=tile_element.get(0).getContext("2d");var j=c.initial_canvas.get(0).getContext("2d");var k=l.getImageData(0,0,l.canvas.width,l.canvas.height);j.putImageData(k,0,0);c.set_overview()}c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)},set_overview:function(){var a=this.view;a.overview_viewport.height(a.default_overview_height);a.overview_box.height(a.default_overview_height);if(this.initial_canvas&&this.is_overview){if(!a.overview_highlight){a.overview_highlight=$("<div />").addClass("overview-highlight").appendTo(a.overview_viewport)}a.overview_viewport.append(this.initial_canvas);a.overview_highlight.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.h
idden=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.height_px=12;this.container_div.addClass("reference-track");this.dummy_canvas=$("<canvas></canvas>").get(0).getContext("2d");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.proto
type,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,e=$("<canvas class='tile'></canvas>"),n=e.get(0).getContext("2d"),j=f+"_"+b;if(o>PX_PER_CHAR){if(this.data_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-this.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o);n.fillText(m[h],a+this.left_offset,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b
,a,c){this.track_type="LineTrack";this.display_modes=["Line","Filled","Intensity"];this.mode="Line";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={min_value:undefined,max_value:undefined,mode:"Line"};if(c.min_value!==undefined){this.prefs.min_value=c.min_value}if(c.max_value!==undefined){this.prefs.max_value=c.max_value}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.
vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(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:"relative",top:"32px",left:"10px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+32+"px",left:"10px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(p,r,c,
e){if(this.vertical_range===undefined){return}var s=r*DENSITY*p,a=DENSITY*p,b=$("<canvas class='tile'></canvas>"),v=p+"_"+r;if(this.data_cache.get(v)===undefined){this.get_data(p,r);return}var j=this.data_cache.get(v);if(j===null){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var o=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,n=this.vertical_range,t=this.total_frequency,d=this.height_px,m=this.mode;o.beginPath();if(data.length>1){var f=Math.ceil((data[1][0]-data[0][0])*e)}else{var f=10}var u,h;for(var q=0;q<data.length;q++){u=(data[q][0]-s)*e;h=data[q][1];if(m=="Intensity"){if(h===null){continue}if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/n*255);o.fillStyle="rgb("+h+","+h+","+h+")";o.fillRect(u,0,f,this.height_px)}else{if(h===null){if(k&&m==="Filled"){o.lineTo(u,d)}k=false;continue}else{if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/n*d);if(k)
{o.lineTo(u,h)}else{k=true;if(m==="Filled"){o.moveTo(u,d);o.lineTo(u,h)}else{o.moveTo(u,h)}}}}}if(m==="Filled"){if(k){o.lineTo(u,d)}o.fill()}else{o.stroke()}c.append(b);return b},gen_options:function(k){var a=$("<div />").addClass("form-row");var e="track_"+k+"_minval",h=$("<label></label>").attr("for",e).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),j=$("<input></input>").attr("id",e).val(b),g="track_"+k+"_maxval",d=$("<label></label>").attr("for",g).text("Max value:"),f=(this.prefs.max_value===undefined?"":this.prefs.max_value),c=$("<input></input>").attr("id",g).val(f);return a.append(h).append(j).append(d).append(c)},update_options:function(c){var a=$("#track_"+c+"_minval").val(),b=$("#track_"+c+"_maxval").val();if(a!==this.prefs.min_value||b!==this.prefs.max_value){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(b);this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+c+"_minval").text(this.pre
fs.min_value);$("#linetrack_"+c+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=2;this.summary_draw_height=20;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:"black",label_color:"black",show_counts:true};if(c.block_color!==undefined){this.prefs.block_color=c.block_color}if(c.label_color!==undefined){this.prefs.label_color=c.label_color}if(c.show_counts!==undefined){this.prefs.sh
ow_counts=c.show_counts}};$.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.view.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,h,c,r){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=r;this.s_e_by_tile[a]={}}var n=this.inc_slots[a].w_scale,z=[],l=0,b=$("<canvas></canvas>").get(0).getContext("2d"),o=this.view.max_low;var B=[];if(this.inc_slots[a].mode!==r){delete this.inc_slots[a];this.inc_slots[a]={mode:r,w_scale:n};delete this.s_
e_by_tile[a];this.s_e_by_tile[a]={}}for(var w=0,x=h.length;w<x;w++){var g=h[w],m=g[0];if(this.inc_slots[a][m]!==undefined){l=Math.max(l,this.inc_slots[a][m]);B.push(this.inc_slots[a][m])}else{z.push(w)}}for(var w=0,x=z.length;w<x;w++){var g=h[z[w]],m=g[0],s=g[1],d=g[2],q=g[3],e=Math.floor((s-o)*n),f=Math.ceil((d-o)*n);if(q!==undefined&&!c){var t=b.measureText(q).width;if(e-t<0){f+=t}else{e-=t}}var v=0;while(true){var p=true;if(this.s_e_by_tile[a][v]!==undefined){for(var u=0,A=this.s_e_by_tile[a][v].length;u<A;u++){var y=this.s_e_by_tile[a][v][u];if(f>y[0]&&e<y[1]){p=false;break}}}if(p){if(this.s_e_by_tile[a][v]===undefined){this.s_e_by_tile[a][v]=[]}this.s_e_by_tile[a][v].push([e,f]);this.inc_slots[a][m]=v;l=Math.max(l,v);break}v++}}return l},rect_or_text:function(n,o,f,m,b,d,k,e,h){n.textAlign="center";var j=Math.round(o/2);if((this.mode==="Pack"||this.mode==="Auto")&&d!==undefined&&o>PX_PER_CHAR){n.fillStyle=this.prefs.block_color;n.fillRect(k,h+1,e,9);n.fillStyle="#eee";f
or(var g=0,l=d.length;g<l;g++){if(b+g>=f&&b+g<=m){var a=Math.floor(Math.max(0,(b+g-f)*o));n.fillText(d[g],a+this.left_offset+j,h+9)}}}else{n.fillStyle=this.prefs.block_color;n.fillRect(k,h+4,e,3)}},draw_tile:function(ae,o,r,ar){var L=o*DENSITY*ae,ak=(o+1)*DENSITY*ae,K=ak-L;var al=(!this.initial_canvas?"initial":L+"_"+ak);var G=this.data_cache.get(al);var e;if(G===undefined||(this.mode!=="Auto"&&G.dataset_type==="summary_tree")){this.data_queue[[L,ak]]=true;this.get_data(L,ak);return}var a=Math.ceil(K*ar),S=$("<canvas class='tile'></canvas>"),ag=this.prefs.label_color,h=this.prefs.block_color,q=this.mode,v=25,ac=(q==="Squish")||(q==="Dense")&&(q!=="Pack")||(q==="Auto"&&(G.extra_info==="no_detail")),W=this.left_offset,aq,B,at;if(G.dataset_type==="summary_tree"){B=this.summary_draw_height}else{if(q==="Dense"){B=v;at=10}else{at=(ac?this.vertical_nodetail_px:this.vertical_detail_px);var w=(ar<0.0001?1/view.zoom_res:ar);B=this.incremental_slots(w,G.data,ac,q)*at+v;aq=this.inc_slot
s[w]}}S.css({position:"absolute",top:0,left:(L-this.view.low)*ar-W});S.get(0).width=a+W;S.get(0).height=B;r.parent().css("height",Math.max(this.height_px,B)+"px");var H=S.get(0).getContext("2d");H.fillStyle=h;H.font=this.default_font;H.textAlign="right";if(G.dataset_type=="summary_tree"){var R,O=55,aj=255-O,n=aj*2/3,Y=G.data,J=G.max,p=G.avg,b=Math.ceil(G.delta*ar);for(var an=0,F=Y.length;an<F;an++){var aa=Math.floor((Y[an][0]-L)*ar);var Z=Y[an][1];if(!Z){continue}R=Math.floor(aj-(Z/J)*aj);H.fillStyle="rgb("+R+","+R+","+R+")";H.fillRect(aa+W,0,b,this.summary_draw_height);if(this.prefs.show_counts&&H.measureText(Z).width<b){if(R>n){H.fillStyle="black"}else{H.fillStyle="#ddd"}H.textAlign="center";H.fillText(Z,aa+W+(b/2),12)}}e="Summary";r.append(S);return S}if(G.message){S.css({border:"solid red","border-width":"2px 2px 2px 0px"});H.fillStyle="red";H.textAlign="left";H.fillText(G.message,100+W,at)}var ap=G.data;var am=0;for(var an=0,F=ap.length;an<F;an++){var T=ap[an],Q=T[0],ao
=T[1],ab=T[2],M=T[3];if(ao<=ak&&ab>=L){var ad=Math.floor(Math.max(0,(ao-L)*ar)),I=Math.ceil(Math.min(a,Math.max(0,(ab-L)*ar))),X=(q==="Dense"?1:(1+aq[Q]))*at;if(G.dataset_type==="bai"){H.fillStyle=h;if(T[4] instanceof Array){var C=Math.floor(Math.max(0,(T[4][0]-L)*ar)),P=Math.ceil(Math.min(a,Math.max(0,(T[4][1]-L)*ar))),A=Math.floor(Math.max(0,(T[5][0]-L)*ar)),u=Math.ceil(Math.min(a,Math.max(0,(T[5][1]-L)*ar)));if(T[4][1]>=L&&T[4][0]<=ak){this.rect_or_text(H,ar,L,ak,T[4][0],T[4][2],C+W,P-C,X)}if(T[5][1]>=L&&T[5][0]<=ak){this.rect_or_text(H,ar,L,ak,T[5][0],T[5][2],A+W,u-A,X)}if(A>P){H.fillStyle="#999";H.fillRect(P+W,X+5,A-P,1)}}else{H.fillStyle=h;this.rect_or_text(H,ar,L,ak,ao,M,ad+W,I-ad,X)}if(q!=="Dense"&&!ac&&ao>L){H.fillStyle=this.prefs.label_color;if(o===0&&ad-H.measureText(M).width<0){H.textAlign="left";H.fillText(Q,I+2+W,X+8)}else{H.textAlign="right";H.fillText(Q,ad-2+W,X+8)}H.fillStyle=h}}else{if(G.dataset_type==="interval_index"){if(ac){H.fillStyle=h;H.fillRect(ad+W,
X+5,I-ad,1)}else{var E=T[4],V=T[5],af=T[6],g=T[7];var D,ah,N=null,au=null;if(V&&af){N=Math.floor(Math.max(0,(V-L)*ar));au=Math.ceil(Math.min(a,Math.max(0,(af-L)*ar)))}if(q!=="Dense"&&M!==undefined&&ao>L){H.fillStyle=ag;if(o===0&&ad-H.measureText(M).width<0){H.textAlign="left";H.fillText(M,I+2+W,X+8)}else{H.textAlign="right";H.fillText(M,ad-2+W,X+8)}H.fillStyle=h}if(g){if(E){if(E=="+"){H.fillStyle=RIGHT_STRAND}else{if(E=="-"){H.fillStyle=LEFT_STRAND}}H.fillRect(ad+W,X,I-ad,10);H.fillStyle=h}for(var al=0,f=g.length;al<f;al++){var t=g[al],d=Math.floor(Math.max(0,(t[0]-L)*ar)),U=Math.ceil(Math.min(a,Math.max((t[1]-L)*ar)));if(d>U){continue}D=5;ah=3;H.fillRect(d+W,X+ah,U-d,D);if(N!==undefined&&!(d>au||U<N)){D=9;ah=1;var ai=Math.max(d,N),z=Math.min(U,au);H.fillRect(ai+W,X+ah,z-ai,D)}}}else{D=9;ah=1;H.fillRect(ad+W,X+ah,I-ad,D);if(T.strand){if(T.strand=="+"){H.fillStyle=RIGHT_STRAND_INV}else{if(T.strand=="-"){H.fillStyle=LEFT_STRAND_INV}}H.fillRect(ad+W,X,I-ad,10);H.fillStyle=h}}}}
else{if(G.dataset_type==="vcf"){if(ac){H.fillStyle=h;H.fillRect(ad+W,X+5,I-ad,1)}else{var s=T[4],m=T[5],c=T[6];D=9;ah=1;H.fillRect(ad+W,X,I-ad,D);if(q!=="Dense"&&M!==undefined&&ao>L){H.fillStyle=ag;if(o===0&&ad-H.measureText(M).width<0){H.textAlign="left";H.fillText(M,I+2+W,X+8)}else{H.textAlign="right";H.fillText(M,ad-2+W,X+8)}H.fillStyle=h}var l=s+" / "+m;if(ao>L&&H.measureText(l).width<(I-ad)){H.fillStyle="white";H.textAlign="center";H.fillText(l,W+ad+(I-ad)/2,X+8);H.fillStyle=h}}}}}am++}}r.append(S);return S},gen_options:function(j){var a=$("<div />").addClass("form-row");var e="track_"+j+"_block_color",l=$("<label />").attr("for",e).text("Block color:"),m=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),k="track_"+j+"_label_color",g=$("<label />").attr("for",k).text("Text color:"),h=$("<input />").attr("id",k).attr("name",k).val(this.prefs.label_color),f="track_"+j+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input
type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(l).append(m).append(g).append(h).append(d)},update_options:function(e){var b=$("#track_"+e+"_block_color").val(),d=$("#track_"+e+"_label_color").val(),c=$("#track_"+e+"_mode option:selected").val(),a=$("#track_"+e+"_show_count").attr("checked");if(b!==this.prefs.block_color||d!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=d;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,c){FeatureTrack.call(this,d,b,a,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -1348,8 +1348,45 @@ var FeatureTrack = function ( name, view
}
ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, 10);
ctx.fillStyle = block_color;
+ }
+ }
+ }
+ } else if (result.dataset_type === 'vcf') {
+ if (no_detail) {
+ ctx.fillStyle = block_color;
+ ctx.fillRect(f_start + left_offset, y_center + 5, f_end - f_start, 1);
+ }
+ else { // Show blocks, labels, etc.
+ // Unpack.
+ var ref_base = feature[4], alt_base = feature[5], qual = feature[6];
+
+ // Draw block for entry.
+ thickness = 9;
+ y_start = 1;
+ ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, thickness);
+
+ // Add label for entry.
+ if (mode !== "Dense" && feature_name !== undefined && feature_start > tile_low) {
+ // Draw label
+ ctx.fillStyle = label_color;
+ if (tile_index === 0 && f_start - ctx.measureText(feature_name).width < 0) {
+ ctx.textAlign = "left";
+ ctx.fillText(feature_name, f_end + 2 + left_offset, y_center + 8);
+ } else {
+ ctx.textAlign = "right";
+ ctx.fillText(feature_name, f_start - 2 + left_offset, y_center + 8);
}
+ ctx.fillStyle = block_color;
}
+
+ // Show additional data on block.
+ var vcf_label = ref_base + " / " + alt_base;
+ if (feature_start > tile_low && ctx.measureText(vcf_label).width < (f_end - f_start)) {
+ ctx.fillStyle = "white";
+ ctx.textAlign = "center";
+ ctx.fillText(vcf_label, left_offset + f_start + (f_end-f_start)/2, y_center + 8);
+ ctx.fillStyle = block_color;
+ }
}
}
j++;
--- a/lib/galaxy/web/controllers/tracks.py
+++ b/lib/galaxy/web/controllers/tracks.py
@@ -24,6 +24,7 @@ from galaxy.visualization.tracks.data.ar
from galaxy.visualization.tracks.data.interval_index import IntervalIndexDataProvider
from galaxy.visualization.tracks.data.bam import BamDataProvider
from galaxy.visualization.tracks.data.summary_tree import SummaryTreeDataProvider
+from galaxy.visualization.tracks.data.vcf import VCFDataProvider
# Message strings returned to browser
messages = Bunch(
@@ -36,10 +37,11 @@ messages = Bunch(
)
# Mapping from dataset type to a class that can fetch data from a file of that
-# type. This also needs to be more flexible.
+# type. First key is converted dataset type; if result is another dict, second key
+# is original dataset type. TODO: This needs to be more flexible.
dataset_type_to_data_provider = {
"array_tree": ArrayTreeDataProvider,
- "interval_index": IntervalIndexDataProvider,
+ "interval_index": { "vcf": VCFDataProvider, "default" : IntervalIndexDataProvider },
"bai": BamDataProvider,
"summary_tree": SummaryTreeDataProvider
}
@@ -300,15 +302,28 @@ class TracksController( BaseController,
frequencies, max_v, avg_v, delta = summary
return { 'dataset_type': data_sources['index'], 'data': frequencies, 'max': max_v, 'avg': avg_v, 'delta': delta }
- dataset_type = data_sources['data']
- data_provider = dataset_type_to_data_provider[ dataset_type ]( dataset.get_converted_dataset(trans, dataset_type), dataset )
+ # Get data provider.
+ tracks_dataset_type = data_sources['data']
+ value = dataset_type_to_data_provider[ tracks_dataset_type ]
+ if isinstance( value, dict ):
+ # Get converter by dataset extension; if there is no data provider,
+ # get the default
+ data_provider_class = value.get( dataset.ext, value.get( "default" ) )
+ else:
+ data_provider_class = value
+ data_provider = data_provider_class( dataset.get_converted_dataset(trans, tracks_dataset_type), dataset )
+ # Get and return data from data_provider.
data = data_provider.get_data( chrom, low, high, **kwargs )
message = None
if isinstance(data, dict) and 'message' in data:
message = data['message']
- data = data['data']
- return { 'dataset_type': dataset_type, 'extra_info': extra_info, 'data': data, 'message': message }
+ dataset_type = data.get( 'data_type', tracks_dataset_type )
+ track_data = data['data']
+ else:
+ track_data = data
+ return { 'dataset_type': dataset_type, 'extra_info': extra_info, 'data': track_data, 'message': message }
+
@web.json
def save( self, trans, **kwargs ):
--- a/lib/galaxy/visualization/tracks/data/array_tree.py
+++ b/lib/galaxy/visualization/tracks/data/array_tree.py
@@ -7,8 +7,9 @@ pkg_resources.require( "numpy" )
pkg_resources.require( "bx-python" )
from bx.arrays.array_tree import FileArrayTreeDict
from math import floor, ceil, log, pow
+from base import TracksDataProvider
-class ArrayTreeDataProvider( object ):
+class ArrayTreeDataProvider( TracksDataProvider ):
def __init__( self, dataset, original_dataset ):
self.dataset = dataset
--- /dev/null
+++ b/lib/galaxy/visualization/tracks/data/vcf.py
@@ -0,0 +1,53 @@
+"""
+VCF data provider for the Galaxy track browser.
+
+Payload format:
+[ uid (offset), start, end, ID, reference base(s), alternate base(s), quality score]
+"""
+
+import pkg_resources; pkg_resources.require( "bx-python" )
+from bx.interval_index_file import Indexes
+from galaxy.datatypes.tabular import Vcf
+from base import TracksDataProvider
+
+MAX_VALS = 5000 # only display first MAX_VALS features
+
+class VCFDataProvider( TracksDataProvider ):
+ """ Provides data for VCF tracks. """
+
+ def get_data( self, chrom, start, end, **kwargs ):
+ """ Returns data in region defined by chrom, start, and end. """
+ start, end = int(start), int(end)
+ source = open( self.original_dataset.file_name )
+ index = Indexes( self.converted_dataset.file_name )
+ results = []
+ count = 0
+ message = None
+
+ # If chrom is not found in indexes, try removing the first three
+ # characters (e.g. 'chr') and see if that works. This enables the
+ # provider to handle chrome names defined as chrXXX and as XXX.
+ chrom = str(chrom)
+ if chrom not in index.indexes and chrom[3:] in index.indexes:
+ chrom = chrom[3:]
+
+ for start, end, offset in index.find(chrom, start, end):
+ if count >= MAX_VALS:
+ message = "Only the first %s features are being displayed." % MAX_VALS
+ break
+ count += 1
+ source.seek(offset)
+ feature = source.readline().split()
+
+ payload = [ offset, start, end, \
+ # ID:
+ feature[2], \
+ # reference base(s):
+ feature[3], \
+ # alternative base(s)
+ feature[4], \
+ # phred quality score
+ feature[5] ]
+ results.append(payload)
+
+ return { 'data_type' : 'vcf', 'data': results, 'message': message }
--- /dev/null
+++ b/lib/galaxy/visualization/tracks/data/base.py
@@ -0,0 +1,11 @@
+class TracksDataProvider( object ):
+ """ Base class for tracks data providers. """
+
+ def __init__( self, converted_dataset, original_dataset ):
+ self.converted_dataset = converted_dataset
+ self.original_dataset = original_dataset
+
+ def get_data( self, chrom, start, end, **kwargs ):
+ """ Returns data in region defined by chrom, start, and end. """
+ # Override.
+ pass
1
0

galaxy-dist commit d3fa82adee97: Finish refactoring of tracks data providers so that they all inherit from a single base object, TracksDataProvider.
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 jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1286559820 14400
# Node ID d3fa82adee9782d27d4de9a92a3d1be47bd138c8
# Parent 13abfea1b0ef2fa93a6bca55af62c11fd220588d
Finish refactoring of tracks data providers so that they all inherit from a single base object, TracksDataProvider.
--- a/lib/galaxy/visualization/tracks/data/array_tree.py
+++ b/lib/galaxy/visualization/tracks/data/array_tree.py
@@ -10,8 +10,6 @@ from math import floor, ceil, log, pow
from base import TracksDataProvider
class ArrayTreeDataProvider( TracksDataProvider ):
- def __init__( self, dataset, original_dataset ):
- self.dataset = dataset
def get_stats( self, chrom ):
f = open( self.dataset.file_name )
@@ -48,7 +46,7 @@ class ArrayTreeDataProvider( TracksDataP
if 'stats' in kwargs:
return self.get_stats(chrom)
- f = open( self.dataset.file_name )
+ f = open( self.converted_dataset.file_name )
d = FileArrayTreeDict( f )
# Get the right chromosome
--- a/lib/galaxy/visualization/tracks/data/summary_tree.py
+++ b/lib/galaxy/visualization/tracks/data/summary_tree.py
@@ -6,18 +6,16 @@ import pkg_resources; pkg_resources.requ
from galaxy.visualization.tracks.summary import *
from math import ceil, log
from galaxy.util.lrucache import LRUCache
+from base import TracksDataProvider
CACHE = LRUCache(20) # Store 20 recently accessed indices for performance
-class SummaryTreeDataProvider( object ):
- def __init__( self, dataset, original_dataset ):
- self.dataset = dataset
-
+class SummaryTreeDataProvider( TracksDataProvider ):
def get_summary( self, chrom, start, end, **kwargs):
- filename = self.dataset.file_name
+ filename = self.converted_dataset.file_name
st = CACHE[filename]
if st is None:
- st = summary_tree_from_file( self.dataset.file_name )
+ st = summary_tree_from_file( self.converted_dataset.file_name )
CACHE[filename] = st
# If chrom is not found in blocks, try removing the first three
--- a/lib/galaxy/visualization/tracks/data/bam.py
+++ b/lib/galaxy/visualization/tracks/data/bam.py
@@ -16,19 +16,14 @@ MAX_VALS = 5000 # only display first MAX
class BamDataProvider( TracksDataProvider ):
"""
Provides access to intervals from a sorted indexed BAM file.
- """
- def __init__( self, index, original_dataset ):
-
- self.index = index
- self.original_dataset = original_dataset
-
+ """
def get_data( self, chrom, start, end, **kwargs ):
"""
Fetch intervals in the region
"""
start, end = int(start), int(end)
# Attempt to open the BAM file with index
- bamfile = csamtools.Samfile( filename=self.original_dataset.file_name, mode='rb', index_filename=self.index.file_name )
+ bamfile = csamtools.Samfile( filename=self.original_dataset.file_name, mode='rb', index_filename=self.converted_dataset.file_name )
message = None
try:
data = bamfile.fetch(start=start, end=end, reference=chrom)
1
0

galaxy-dist commit 13abfea1b0ef: Removed redundant loading of name as handled by parameter base class
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User peterjc <p.j.a.cock(a)googlemail.com>
# Date 1286451944 -3600
# Node ID 13abfea1b0ef2fa93a6bca55af62c11fd220588d
# Parent 51873f4ee7a6a3535dc028c66259a5381cc46c9c
Removed redundant loading of name as handled by parameter base class
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -161,7 +161,6 @@ class TextToolParameter( ToolParameter )
"""
def __init__( self, tool, elem ):
ToolParameter.__init__( self, tool, elem )
- self.name = elem.get( 'name' )
self.size = elem.get( 'size' )
self.value = elem.get( 'value' )
self.area = string_as_bool( elem.get( 'area', False ) )
@@ -271,7 +270,6 @@ class BooleanToolParameter( ToolParamete
ToolParameter.__init__( self, tool, elem )
self.truevalue = elem.get( 'truevalue', 'true' )
self.falsevalue = elem.get( 'falsevalue', 'false' )
- self.name = elem.get( 'name' )
self.checked = string_as_bool( elem.get( 'checked' ) )
def get_html_field( self, trans=None, value=None, other_values={} ):
checked = self.checked
@@ -308,7 +306,6 @@ class FileToolParameter( ToolParameter )
Example: C{<param name="bins" type="file" />}
"""
ToolParameter.__init__( self, tool, elem )
- self.name = elem.get( 'name' )
self.ajax = string_as_bool( elem.get( 'ajax-upload' ) )
def get_html_field( self, trans=None, value=None, other_values={} ):
return form_builder.FileField( self.name, ajax = self.ajax, value = value )
@@ -371,7 +368,6 @@ class HiddenToolParameter( ToolParameter
"""
def __init__( self, tool, elem ):
ToolParameter.__init__( self, tool, elem )
- self.name = elem.get( 'name' )
self.value = elem.get( 'value' )
def get_html_field( self, trans=None, value=None, other_values={} ):
return form_builder.HiddenField( self.name, self.value )
@@ -391,7 +387,6 @@ class BaseURLToolParameter( ToolParamete
"""
def __init__( self, tool, elem ):
ToolParameter.__init__( self, tool, elem )
- self.name = elem.get( 'name' )
self.value = elem.get( 'value', '' )
def get_value( self, trans ):
# url = trans.request.base + self.value
@@ -1393,7 +1388,6 @@ class DataToolParameter( ToolParameter )
# """
# def __init__( self, tool, elem ):
# ToolParameter.__init__( self, tool, elem )
-# self.name = elem.get('name')
# def get_html( self, trans, value=None, other_values={} ):
# assert trans.history is not None, "HistoryIDParameter requires a history"
# self.html = form_builder.HiddenField( self.name, trans.history.id ).get_html()
1
0
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Kanwei Li <kanwei(a)gmail.com>
# Date 1286571405 14400
# Node ID ddcfb334eb3ad53425a62f4b094374b219daa455
# Parent b36700318a817db7d9817213aef9e7d1602b9aec
trackster:
- Fix LineTrack axis labels not aligning consistently
- New image for draggable elements
- New slider that is rounded, easier to see and will go off-screen if smaller than the minimum width
- Added "Close overview" link when overview is showing
- Fix array_tree from recent refactoring
Binary file static/images/draggable_horizontal.png has changed
--- 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=100,DATA_ERROR="There was an error in indexing this dataset. ",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),PX_PER_CHAR=CONTEXT.measureText("A").width,RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function()
{RIGHT_STRAND_INV=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,c,e,d,b){this.container=a;this.vis_id=d;this.dbkey=b;this.title=e;this.chrom=c;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.
min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{init:function(){var c=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").hide().appendTo(c);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide().appendTo(c);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);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/>").addClass("overview-viewport").appendTo(this.overview);this.overview_box_background=$("<div/>").addClass("overview-boxback").app
endTo(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(){void (0)}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.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.hi
de();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(d){if(d.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=d.chrom_info;var f='<option value="">Select Chrom/Contig</option>';for(i in a.chrom_data){var e=a.chrom_data[i]["chrom"];f+='<option value="'+e+'">'+e+"</option>"}a.chrom_select.html(f);a.intro_div.show();a.content_div.hide();a.chrom_select.b
ind("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.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 j=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(j/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(j){var f=Math.min(j.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(j.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(k){var f=Math.min(k.clientX,this.drag_origin_x),d=Math.max(k.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),j=a.low;a.low=Math.round(f/g*h)+j;a.high=Math.round(d/g*h)+j;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(th
is,this.top_labeltrack));this.add_label_track(new LabelTrack(this,this.nav_labeltrack))},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(d,a,f){var c=this;var e=$.grep(c.chrom_data,function(h,j){return h.chrom===d})[0];if(e===undefined){return}if(d!==c.chrom){c.chrom=d;if(c.chrom===""){c.intro_div.show();c.content_div.hide()}else{c.intro_div.hide();c.content_div.show()}c.chrom_select.val(c.chrom);c.max_high=e.len;c.reset();c.redraw(true);for(var g in c.tracks){var b=c.tracks[g];if(b.init){b.init()}}}if(a!==undefined&&f!==undefined){c.low=Math.max(a,0);c.high=Math.min(f,c.max_high)}c.overview_viewport.find("canvas").remove();c.redraw()},go_to:function(f){var k=this,b=f.split(":"),h=b[0],j=b[1];if(j!==undefined){try{var g=j.split("-"),a=parseInt(g[0].replace(/,/g,"")),d=parseInt(g[1].replace(/,/g,""))}catch(c){return false}}k.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_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 d=this.high-this.low,b=this.low,f=this.high;if(b<this.max_low){b=this.max_low}if(f>this.max_high){f=this.max_high}if(this.high!==0&&d<this.min_separation){f=b+this.min_separation}this.low=Math.floor(b);th
is.high=Math.ceil(f);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 e=(this.low/(this.max_high-this.max_low))*this.overview_viewport.width();var g=(this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width();this.overview_box.css({left:e,width:Math.max(12,g)}).show();if(this.overview_highlight){this.overview_highlight.css({left:e,width:g})}this.update_location(this.low,this.high);if(!h){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+thi
s.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);this.high=Math.round(c+a);this.redraw()}});var Track=function(b,a,c){this.name=b;this.parent_element=c;this.view=a;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track");if(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);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.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.con
tent_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("he
ight",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 d=this,c=d.view;if(d.hidden){return}if(d.display_modes!==undefined){if(d.mode_div===undefined){d.mode_div=$("<div class='right-float menubutton popup' />").appendTo(d.header_div);var h=d.display_modes[0];d.mode=h;d.mode_div.text(h);var a=function(j){d.mode_div.text(j);d.mode=j;d.tile_cache.clear();d.draw()};var f={};for(var e in d.display_modes){var g=d.display_modes[e];f[g]=function(j){return function(){a(j)}}(g)}make_popupmenu(d.mode_div,f)}else{d.mode_div.hide()}}var b={};b["Set track as overview"]=function(){c.overview_viewport.find("canvas").remove();d.is_overview=true;d.set_overview();for(var j in c.tracks){if(c.tracks[j]!==d){c.tracks[j].is_overview=false}}};b["Edit configuration"]=function(){var k=function(){hide_modal()};var j=function(){d.update_options(d.track_id);hide_modal()};show_modal("Configure Trac
k",d.gen_options(d.track_id),{Cancel:k,OK:j})};b.Remove=function(){c.remove_track(d);if(c.num_tracks===0){$("#no-tracks").show()}};make_popupmenu(d.name_div,b)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var j=this.view.low,e=this.view.high,f=e-j,d=this.view.resolution;var l=$("<div style='position: relative;'></div>"),m=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(l),this.max_height=0;var a=Math.floor(j/d/DENSITY);while((a*DENSITY*d)<e){var k=this.content_div.width()+"_"+m+"_"+a;var c=this.tile_cache.get(k);if(c){var g=a*DENSITY*d;var b=(g-j)*m;if(this.left_offset){b-=this.left_offset}c.css({left:b});l.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height+"px")}else{this.delayed_draw(this,k,j,e,a,d,l,m)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_el
ement){if(!c.initial_canvas){c.initial_canvas=$(tile_element).clone();var l=tile_element.get(0).getContext("2d");var j=c.initial_canvas.get(0).getContext("2d");var k=l.getImageData(0,0,l.canvas.width,l.canvas.height);j.putImageData(k,0,0);c.set_overview()}c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)},set_overview:function(){var a=this.view;a.overview_viewport.height(a.default_overview_height);a.overview_box.height(a.default_overview_height);if(this.initial_canvas&&this.is_overview){if(!a.overview_highlight){a.overview_highlight=$("<div />").addClass("overview-highlight").appendTo(a.overview_viewport)}a.overview_viewport.append(this.initial_canvas);a.overview_highlight.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.h
idden=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.height_px=12;this.container_div.addClass("reference-track");this.dummy_canvas=$("<canvas></canvas>").get(0).getContext("2d");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.proto
type,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,e=$("<canvas class='tile'></canvas>"),n=e.get(0).getContext("2d"),j=f+"_"+b;if(o>PX_PER_CHAR){if(this.data_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-this.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o);n.fillText(m[h],a+this.left_offset,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b
,a,c){this.track_type="LineTrack";this.display_modes=["Line","Filled","Intensity"];this.mode="Line";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={min_value:undefined,max_value:undefined,mode:"Line"};if(c.min_value!==undefined){this.prefs.min_value=c.min_value}if(c.max_value!==undefined){this.prefs.max_value=c.max_value}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.
vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(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:"relative",top:"32px",left:"10px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+32+"px",left:"10px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(p,r,c,
e){if(this.vertical_range===undefined){return}var s=r*DENSITY*p,a=DENSITY*p,b=$("<canvas class='tile'></canvas>"),v=p+"_"+r;if(this.data_cache.get(v)===undefined){this.get_data(p,r);return}var j=this.data_cache.get(v);if(j===null){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var o=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,n=this.vertical_range,t=this.total_frequency,d=this.height_px,m=this.mode;o.beginPath();if(data.length>1){var f=Math.ceil((data[1][0]-data[0][0])*e)}else{var f=10}var u,h;for(var q=0;q<data.length;q++){u=(data[q][0]-s)*e;h=data[q][1];if(m=="Intensity"){if(h===null){continue}if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/n*255);o.fillStyle="rgb("+h+","+h+","+h+")";o.fillRect(u,0,f,this.height_px)}else{if(h===null){if(k&&m==="Filled"){o.lineTo(u,d)}k=false;continue}else{if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/n*d);if(k)
{o.lineTo(u,h)}else{k=true;if(m==="Filled"){o.moveTo(u,d);o.lineTo(u,h)}else{o.moveTo(u,h)}}}}}if(m==="Filled"){if(k){o.lineTo(u,d)}o.fill()}else{o.stroke()}c.append(b);return b},gen_options:function(k){var a=$("<div />").addClass("form-row");var e="track_"+k+"_minval",h=$("<label></label>").attr("for",e).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),j=$("<input></input>").attr("id",e).val(b),g="track_"+k+"_maxval",d=$("<label></label>").attr("for",g).text("Max value:"),f=(this.prefs.max_value===undefined?"":this.prefs.max_value),c=$("<input></input>").attr("id",g).val(f);return a.append(h).append(j).append(d).append(c)},update_options:function(c){var a=$("#track_"+c+"_minval").val(),b=$("#track_"+c+"_maxval").val();if(a!==this.prefs.min_value||b!==this.prefs.max_value){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(b);this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+c+"_minval").text(this.pre
fs.min_value);$("#linetrack_"+c+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=2;this.summary_draw_height=20;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:"black",label_color:"black",show_counts:true};if(c.block_color!==undefined){this.prefs.block_color=c.block_color}if(c.label_color!==undefined){this.prefs.label_color=c.label_color}if(c.show_counts!==undefined){this.prefs.sh
ow_counts=c.show_counts}};$.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.view.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,h,c,r){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=r;this.s_e_by_tile[a]={}}var n=this.inc_slots[a].w_scale,z=[],l=0,b=$("<canvas></canvas>").get(0).getContext("2d"),o=this.view.max_low;var B=[];if(this.inc_slots[a].mode!==r){delete this.inc_slots[a];this.inc_slots[a]={mode:r,w_scale:n};delete this.s_
e_by_tile[a];this.s_e_by_tile[a]={}}for(var w=0,x=h.length;w<x;w++){var g=h[w],m=g[0];if(this.inc_slots[a][m]!==undefined){l=Math.max(l,this.inc_slots[a][m]);B.push(this.inc_slots[a][m])}else{z.push(w)}}for(var w=0,x=z.length;w<x;w++){var g=h[z[w]],m=g[0],s=g[1],d=g[2],q=g[3],e=Math.floor((s-o)*n),f=Math.ceil((d-o)*n);if(q!==undefined&&!c){var t=b.measureText(q).width;if(e-t<0){f+=t}else{e-=t}}var v=0;while(true){var p=true;if(this.s_e_by_tile[a][v]!==undefined){for(var u=0,A=this.s_e_by_tile[a][v].length;u<A;u++){var y=this.s_e_by_tile[a][v][u];if(f>y[0]&&e<y[1]){p=false;break}}}if(p){if(this.s_e_by_tile[a][v]===undefined){this.s_e_by_tile[a][v]=[]}this.s_e_by_tile[a][v].push([e,f]);this.inc_slots[a][m]=v;l=Math.max(l,v);break}v++}}return l},rect_or_text:function(n,o,f,m,b,d,k,e,h){n.textAlign="center";var j=Math.round(o/2);if((this.mode==="Pack"||this.mode==="Auto")&&d!==undefined&&o>PX_PER_CHAR){n.fillStyle=this.prefs.block_color;n.fillRect(k,h+1,e,9);n.fillStyle="#eee";f
or(var g=0,l=d.length;g<l;g++){if(b+g>=f&&b+g<=m){var a=Math.floor(Math.max(0,(b+g-f)*o));n.fillText(d[g],a+this.left_offset+j,h+9)}}}else{n.fillStyle=this.prefs.block_color;n.fillRect(k,h+4,e,3)}},draw_tile:function(ae,o,r,ar){var L=o*DENSITY*ae,ak=(o+1)*DENSITY*ae,K=ak-L;var al=(!this.initial_canvas?"initial":L+"_"+ak);var G=this.data_cache.get(al);var e;if(G===undefined||(this.mode!=="Auto"&&G.dataset_type==="summary_tree")){this.data_queue[[L,ak]]=true;this.get_data(L,ak);return}var a=Math.ceil(K*ar),S=$("<canvas class='tile'></canvas>"),ag=this.prefs.label_color,h=this.prefs.block_color,q=this.mode,v=25,ac=(q==="Squish")||(q==="Dense")&&(q!=="Pack")||(q==="Auto"&&(G.extra_info==="no_detail")),W=this.left_offset,aq,B,at;if(G.dataset_type==="summary_tree"){B=this.summary_draw_height}else{if(q==="Dense"){B=v;at=10}else{at=(ac?this.vertical_nodetail_px:this.vertical_detail_px);var w=(ar<0.0001?1/view.zoom_res:ar);B=this.incremental_slots(w,G.data,ac,q)*at+v;aq=this.inc_slot
s[w]}}S.css({position:"absolute",top:0,left:(L-this.view.low)*ar-W});S.get(0).width=a+W;S.get(0).height=B;r.parent().css("height",Math.max(this.height_px,B)+"px");var H=S.get(0).getContext("2d");H.fillStyle=h;H.font=this.default_font;H.textAlign="right";if(G.dataset_type=="summary_tree"){var R,O=55,aj=255-O,n=aj*2/3,Y=G.data,J=G.max,p=G.avg,b=Math.ceil(G.delta*ar);for(var an=0,F=Y.length;an<F;an++){var aa=Math.floor((Y[an][0]-L)*ar);var Z=Y[an][1];if(!Z){continue}R=Math.floor(aj-(Z/J)*aj);H.fillStyle="rgb("+R+","+R+","+R+")";H.fillRect(aa+W,0,b,this.summary_draw_height);if(this.prefs.show_counts&&H.measureText(Z).width<b){if(R>n){H.fillStyle="black"}else{H.fillStyle="#ddd"}H.textAlign="center";H.fillText(Z,aa+W+(b/2),12)}}e="Summary";r.append(S);return S}if(G.message){S.css({border:"solid red","border-width":"2px 2px 2px 0px"});H.fillStyle="red";H.textAlign="left";H.fillText(G.message,100+W,at)}var ap=G.data;var am=0;for(var an=0,F=ap.length;an<F;an++){var T=ap[an],Q=T[0],ao
=T[1],ab=T[2],M=T[3];if(ao<=ak&&ab>=L){var ad=Math.floor(Math.max(0,(ao-L)*ar)),I=Math.ceil(Math.min(a,Math.max(0,(ab-L)*ar))),X=(q==="Dense"?1:(1+aq[Q]))*at;if(G.dataset_type==="bai"){H.fillStyle=h;if(T[4] instanceof Array){var C=Math.floor(Math.max(0,(T[4][0]-L)*ar)),P=Math.ceil(Math.min(a,Math.max(0,(T[4][1]-L)*ar))),A=Math.floor(Math.max(0,(T[5][0]-L)*ar)),u=Math.ceil(Math.min(a,Math.max(0,(T[5][1]-L)*ar)));if(T[4][1]>=L&&T[4][0]<=ak){this.rect_or_text(H,ar,L,ak,T[4][0],T[4][2],C+W,P-C,X)}if(T[5][1]>=L&&T[5][0]<=ak){this.rect_or_text(H,ar,L,ak,T[5][0],T[5][2],A+W,u-A,X)}if(A>P){H.fillStyle="#999";H.fillRect(P+W,X+5,A-P,1)}}else{H.fillStyle=h;this.rect_or_text(H,ar,L,ak,ao,M,ad+W,I-ad,X)}if(q!=="Dense"&&!ac&&ao>L){H.fillStyle=this.prefs.label_color;if(o===0&&ad-H.measureText(M).width<0){H.textAlign="left";H.fillText(Q,I+2+W,X+8)}else{H.textAlign="right";H.fillText(Q,ad-2+W,X+8)}H.fillStyle=h}}else{if(G.dataset_type==="interval_index"){if(ac){H.fillStyle=h;H.fillRect(ad+W,
X+5,I-ad,1)}else{var E=T[4],V=T[5],af=T[6],g=T[7];var D,ah,N=null,au=null;if(V&&af){N=Math.floor(Math.max(0,(V-L)*ar));au=Math.ceil(Math.min(a,Math.max(0,(af-L)*ar)))}if(q!=="Dense"&&M!==undefined&&ao>L){H.fillStyle=ag;if(o===0&&ad-H.measureText(M).width<0){H.textAlign="left";H.fillText(M,I+2+W,X+8)}else{H.textAlign="right";H.fillText(M,ad-2+W,X+8)}H.fillStyle=h}if(g){if(E){if(E=="+"){H.fillStyle=RIGHT_STRAND}else{if(E=="-"){H.fillStyle=LEFT_STRAND}}H.fillRect(ad+W,X,I-ad,10);H.fillStyle=h}for(var al=0,f=g.length;al<f;al++){var t=g[al],d=Math.floor(Math.max(0,(t[0]-L)*ar)),U=Math.ceil(Math.min(a,Math.max((t[1]-L)*ar)));if(d>U){continue}D=5;ah=3;H.fillRect(d+W,X+ah,U-d,D);if(N!==undefined&&!(d>au||U<N)){D=9;ah=1;var ai=Math.max(d,N),z=Math.min(U,au);H.fillRect(ai+W,X+ah,z-ai,D)}}}else{D=9;ah=1;H.fillRect(ad+W,X+ah,I-ad,D);if(T.strand){if(T.strand=="+"){H.fillStyle=RIGHT_STRAND_INV}else{if(T.strand=="-"){H.fillStyle=LEFT_STRAND_INV}}H.fillRect(ad+W,X,I-ad,10);H.fillStyle=h}}}}
else{if(G.dataset_type==="vcf"){if(ac){H.fillStyle=h;H.fillRect(ad+W,X+5,I-ad,1)}else{var s=T[4],m=T[5],c=T[6];D=9;ah=1;H.fillRect(ad+W,X,I-ad,D);if(q!=="Dense"&&M!==undefined&&ao>L){H.fillStyle=ag;if(o===0&&ad-H.measureText(M).width<0){H.textAlign="left";H.fillText(M,I+2+W,X+8)}else{H.textAlign="right";H.fillText(M,ad-2+W,X+8)}H.fillStyle=h}var l=s+" / "+m;if(ao>L&&H.measureText(l).width<(I-ad)){H.fillStyle="white";H.textAlign="center";H.fillText(l,W+ad+(I-ad)/2,X+8);H.fillStyle=h}}}}}am++}}r.append(S);return S},gen_options:function(j){var a=$("<div />").addClass("form-row");var e="track_"+j+"_block_color",l=$("<label />").attr("for",e).text("Block color:"),m=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),k="track_"+j+"_label_color",g=$("<label />").attr("for",k).text("Text color:"),h=$("<input />").attr("id",k).attr("name",k).val(this.prefs.label_color),f="track_"+j+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input
type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(l).append(m).append(g).append(h).append(d)},update_options:function(e){var b=$("#track_"+e+"_block_color").val(),d=$("#track_"+e+"_label_color").val(),c=$("#track_"+e+"_mode option:selected").val(),a=$("#track_"+e+"_show_count").attr("checked");if(b!==this.prefs.block_color||d!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=d;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,c){FeatureTrack.call(this,d,b,a,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
+var DENSITY=200,FEATURE_LEVELS=10,MAX_FEATURE_DEPTH=100,DATA_ERROR="There was an error in indexing this dataset. ",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),PX_PER_CHAR=CONTEXT.measureText("A").width,RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function()
{RIGHT_STRAND_INV=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,c,e,d,b){this.container=a;this.vis_id=d;this.dbkey=b;this.title=e;this.chrom=c;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.
min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{init:function(){var 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/>").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(){void (0)}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.which)!==27){a.go_to($(this).val())}$(this).hide();a.location_span.show();a.chrom_select.show();return false}};this.nav_input=$("<inp
ut/>").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(d){if(d.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=d.chrom_info
;var f='<option value="">Select Chrom/Contig</option>';for(i in a.chrom_data){var e=a.chrom_data[i]["chrom"];f+='<option value="'+e+'">'+e+"</option>"}a.chrom_select.html(f);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 d in a.tracks){a.tracks[d].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.origin
al_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 j=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(j/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(j){var f=Math.min(j.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(j.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(k){var f=Math.min(k.clientX,this.drag_origin_x),d=Math.max(k.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),j=a.low;a.low=Math.round(f/g*h)+j;a.high=Math.round(d/g*h)+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))},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(d,a,f){var c=this;var e=$.grep(c.chrom_data,function(h,j){return h.chrom===d})[0];if(e===undefined){return}if(d!==c.chrom){c.chrom=d;if(c.chrom===""){c.intro_div.show()}else{c.intro_div.hide()}c.chrom_select.val(c.chrom);c.max_high=e.len;c.reset();c.redraw(true);for(var g i
n c.tracks){var b=c.tracks[g];if(b.init){b.init()}}}if(a!==undefined&&f!==undefined){c.low=Math.max(a,0);c.high=Math.min(f,c.max_high)}c.overview_viewport.find("canvas").remove();c.redraw()},go_to:function(f){var k=this,b=f.split(":"),h=b[0],j=b[1];if(j!==undefined){try{var g=j.split("-"),a=parseInt(g[0].replace(/,/g,"")),d=parseInt(g[1].replace(/,/g,""))}catch(c){return false}}k.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_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();var e=(this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width();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,widt
h: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(var 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()}});var Track=function(b,a,c){this.name=b;this.parent_element=c;this.view=a;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track");if(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.
container_div);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.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 d=this,c=d.view;if(d.hidden){return}if(d.display_modes!==undefined){if(d.mode_div===undefined){d.mode_div=$("<div class='right-float menubutton popup' />").appendTo(d.header_div);var h=d.display_modes[0];d.mode=h;d.mode_div.text(h);var a=function(j){d.mode_div.text(j);d.mode=j;d.tile_cache.clear();d.draw()};var f={};for(var e in d.display_modes){var g=d.display
_modes[e];f[g]=function(j){return function(){a(j)}}(g)}make_popupmenu(d.mode_div,f)}else{d.mode_div.hide()}}var b={};b["Set as overview"]=function(){c.overview_viewport.find("canvas").remove();d.is_overview=true;d.set_overview();for(var j in c.tracks){if(c.tracks[j]!==d){c.tracks[j].is_overview=false}}};b["Edit configuration"]=function(){var k=function(){hide_modal()};var j=function(){d.update_options(d.track_id);hide_modal()};show_modal("Configure Track",d.gen_options(d.track_id),{Cancel:k,OK:j})};b.Remove=function(){c.remove_track(d);if(c.num_tracks===0){$("#no-tracks").show()}};make_popupmenu(d.name_div,b)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var j=this.view.low,e=this.view.high,f=e-j,d=this.view.resolution;var l=$("<div style='position: relative;'></div>"),m=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(l),this.max_height=0;var a=Math.floor(j/d/DENSITY);while((a*DENSITY*d)<e){var k=this.conten
t_div.width()+"_"+m+"_"+a;var c=this.tile_cache.get(k);if(c){var g=a*DENSITY*d;var b=(g-j)*m;if(this.left_offset){b-=this.left_offset}c.css({left:b});l.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height+"px")}else{this.delayed_draw(this,k,j,e,a,d,l,m)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){if(!c.initial_canvas){c.initial_canvas=$(tile_element).clone();var l=tile_element.get(0).getContext("2d");var j=c.initial_canvas.get(0).getContext("2d");var k=l.getImageData(0,0,l.canvas.width,l.canvas.height);j.putImageData(k,0,0);c.set_overview()}c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)},set_overview:function(){var a=this.view;a.overview_viewport.height(a.default_overview_height);a.overview_box.height(a.default_overv
iew_height);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="ReferenceTrac
k";this.hidden=true;Track.call(this,null,a,a.top_labeltrack);TiledTrack.call(this);this.height_px=12;this.container_div.addClass("reference-track");this.dummy_canvas=$("<canvas></canvas>").get(0).getContext("2d");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.prototype,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,e=$("<canvas class='tile'></canvas>"),n=e.get(0).getContext("2d"),j=f+"_"+b;if(o>PX_PER_CHAR){if(this.data_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content
_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-this.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o);n.fillText(m[h],a+this.left_offset,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b,a,c){this.track_type="LineTrack";this.display_modes=["Line","Filled","Intensity"];this.mode="Line";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={min_value:undefined,max_value:undefined,mode:"Line"};if(c.min_value!==undefined){this.prefs.min_value=c.min_value}if(c.max_value!==undefined){this.prefs.max_value=c.max_value}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({s
tats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();a.container_div.css("position","relative");var e=$("<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);e.css({position:"absolute",top:a.height_px+11+"px",left:"10px"});e.prependTo(a.container_div)})},get_dat
a:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(p,r,c,e){if(this.vertical_range===undefined){return}var s=r*DENSITY*p,a=DENSITY*p,b=$("<canvas class='tile'></canvas>"),v=p+"_"+r;if(this.data_cache.get(v)===undefined){this.get_data(p,r);return}var j=this.data_cache.get(v);if(j===null){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var o=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,n=this.vertical_range,t=this.total_frequency,d=this.height_px,m=this.mode;o.beginPath();if(data.length>1){var f=Math.ceil((data[1][0]-
data[0][0])*e)}else{var f=10}var u,h;for(var q=0;q<data.length;q++){u=(data[q][0]-s)*e;h=data[q][1];if(m=="Intensity"){if(h===null){continue}if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/n*255);o.fillStyle="rgb("+h+","+h+","+h+")";o.fillRect(u,0,f,this.height_px)}else{if(h===null){if(k&&m==="Filled"){o.lineTo(u,d)}k=false;continue}else{if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/n*d);if(k){o.lineTo(u,h)}else{k=true;if(m==="Filled"){o.moveTo(u,d);o.lineTo(u,h)}else{o.moveTo(u,h)}}}}}if(m==="Filled"){if(k){o.lineTo(u,d)}o.fill()}else{o.stroke()}c.append(b);return b},gen_options:function(k){var a=$("<div />").addClass("form-row");var e="track_"+k+"_minval",h=$("<label></label>").attr("for",e).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),j=$("<input></input>").attr("id",e).val(b),g="track_"+k+"_maxval",d=$("<label></label>").attr("for",g).text("Max value:"),f=(this.prefs.max_value===undefined?"":this.prefs.max_value),c=$("<inp
ut></input>").attr("id",g).val(f);return a.append(h).append(j).append(d).append(c)},update_options:function(c){var a=$("#track_"+c+"_minval").val(),b=$("#track_"+c+"_maxval").val();if(a!==this.prefs.min_value||b!==this.prefs.max_value){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(b);this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+c+"_minval").text(this.prefs.min_value);$("#linetrack_"+c+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=2;this.summary_draw_height=20;this.default_font="9px Monaco, Lucida Console, mono
space";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:"black",label_color:"black",show_counts:true};if(c.block_color!==undefined){this.prefs.block_color=c.block_color}if(c.label_color!==undefined){this.prefs.label_color=c.label_color}if(c.show_counts!==undefined){this.prefs.show_counts=c.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b="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.view.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b
.data_queue[c];b.draw()})}},incremental_slots:function(a,h,c,r){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=r;this.s_e_by_tile[a]={}}var n=this.inc_slots[a].w_scale,z=[],l=0,b=$("<canvas></canvas>").get(0).getContext("2d"),o=this.view.max_low;var B=[];if(this.inc_slots[a].mode!==r){delete this.inc_slots[a];this.inc_slots[a]={mode:r,w_scale:n};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var w=0,x=h.length;w<x;w++){var g=h[w],m=g[0];if(this.inc_slots[a][m]!==undefined){l=Math.max(l,this.inc_slots[a][m]);B.push(this.inc_slots[a][m])}else{z.push(w)}}for(var w=0,x=z.length;w<x;w++){var g=h[z[w]],m=g[0],s=g[1],d=g[2],q=g[3],e=Math.floor((s-o)*n),f=Math.ceil((d-o)*n);if(q!==undefined&&!c){var t=b.measureText(q).width;if(e-t<0){f+=t}else{e-=t}}var v=0;while(true){var p=true;if(this.s_e_by_tile[a][v]!==undefined){for(var u=0,A=this.s_e_by_tile[a][v].length;u<A;u++){var y=this.s_e_by_tile[a][v][u];if(f>y[0]&&e<y[1]){p=false;
break}}}if(p){if(this.s_e_by_tile[a][v]===undefined){this.s_e_by_tile[a][v]=[]}this.s_e_by_tile[a][v].push([e,f]);this.inc_slots[a][m]=v;l=Math.max(l,v);break}v++}}return l},rect_or_text:function(n,o,f,m,b,d,k,e,h){n.textAlign="center";var j=Math.round(o/2);if((this.mode==="Pack"||this.mode==="Auto")&&d!==undefined&&o>PX_PER_CHAR){n.fillStyle=this.prefs.block_color;n.fillRect(k,h+1,e,9);n.fillStyle="#eee";for(var g=0,l=d.length;g<l;g++){if(b+g>=f&&b+g<=m){var a=Math.floor(Math.max(0,(b+g-f)*o));n.fillText(d[g],a+this.left_offset+j,h+9)}}}else{n.fillStyle=this.prefs.block_color;n.fillRect(k,h+4,e,3)}},draw_tile:function(ae,o,r,ar){var L=o*DENSITY*ae,ak=(o+1)*DENSITY*ae,K=ak-L;var al=(!this.initial_canvas?"initial":L+"_"+ak);var G=this.data_cache.get(al);var e;if(G===undefined||(this.mode!=="Auto"&&G.dataset_type==="summary_tree")){this.data_queue[[L,ak]]=true;this.get_data(L,ak);return}var a=Math.ceil(K*ar),S=$("<canvas class='tile'></canvas>"),ag=this.prefs.label_color,h=thi
s.prefs.block_color,q=this.mode,v=25,ac=(q==="Squish")||(q==="Dense")&&(q!=="Pack")||(q==="Auto"&&(G.extra_info==="no_detail")),W=this.left_offset,aq,B,at;if(G.dataset_type==="summary_tree"){B=this.summary_draw_height}else{if(q==="Dense"){B=v;at=10}else{at=(ac?this.vertical_nodetail_px:this.vertical_detail_px);var w=(ar<0.0001?1/view.zoom_res:ar);B=this.incremental_slots(w,G.data,ac,q)*at+v;aq=this.inc_slots[w]}}S.css({position:"absolute",top:0,left:(L-this.view.low)*ar-W});S.get(0).width=a+W;S.get(0).height=B;r.parent().css("height",Math.max(this.height_px,B)+"px");var H=S.get(0).getContext("2d");H.fillStyle=h;H.font=this.default_font;H.textAlign="right";if(G.dataset_type=="summary_tree"){var R,O=55,aj=255-O,n=aj*2/3,Y=G.data,J=G.max,p=G.avg,b=Math.ceil(G.delta*ar);for(var an=0,F=Y.length;an<F;an++){var aa=Math.floor((Y[an][0]-L)*ar);var Z=Y[an][1];if(!Z){continue}R=Math.floor(aj-(Z/J)*aj);H.fillStyle="rgb("+R+","+R+","+R+")";H.fillRect(aa+W,0,b,this.summary_draw_height);if
(this.prefs.show_counts&&H.measureText(Z).width<b){if(R>n){H.fillStyle="black"}else{H.fillStyle="#ddd"}H.textAlign="center";H.fillText(Z,aa+W+(b/2),12)}}e="Summary";r.append(S);return S}if(G.message){S.css({border:"solid red","border-width":"2px 2px 2px 0px"});H.fillStyle="red";H.textAlign="left";H.fillText(G.message,100+W,at)}var ap=G.data;var am=0;for(var an=0,F=ap.length;an<F;an++){var T=ap[an],Q=T[0],ao=T[1],ab=T[2],M=T[3];if(ao<=ak&&ab>=L){var ad=Math.floor(Math.max(0,(ao-L)*ar)),I=Math.ceil(Math.min(a,Math.max(0,(ab-L)*ar))),X=(q==="Dense"?1:(1+aq[Q]))*at;if(G.dataset_type==="bai"){H.fillStyle=h;if(T[4] instanceof Array){var C=Math.floor(Math.max(0,(T[4][0]-L)*ar)),P=Math.ceil(Math.min(a,Math.max(0,(T[4][1]-L)*ar))),A=Math.floor(Math.max(0,(T[5][0]-L)*ar)),u=Math.ceil(Math.min(a,Math.max(0,(T[5][1]-L)*ar)));if(T[4][1]>=L&&T[4][0]<=ak){this.rect_or_text(H,ar,L,ak,T[4][0],T[4][2],C+W,P-C,X)}if(T[5][1]>=L&&T[5][0]<=ak){this.rect_or_text(H,ar,L,ak,T[5][0],T[5][2],A+W,u-A,X
)}if(A>P){H.fillStyle="#999";H.fillRect(P+W,X+5,A-P,1)}}else{H.fillStyle=h;this.rect_or_text(H,ar,L,ak,ao,M,ad+W,I-ad,X)}if(q!=="Dense"&&!ac&&ao>L){H.fillStyle=this.prefs.label_color;if(o===0&&ad-H.measureText(M).width<0){H.textAlign="left";H.fillText(Q,I+2+W,X+8)}else{H.textAlign="right";H.fillText(Q,ad-2+W,X+8)}H.fillStyle=h}}else{if(G.dataset_type==="interval_index"){if(ac){H.fillStyle=h;H.fillRect(ad+W,X+5,I-ad,1)}else{var E=T[4],V=T[5],af=T[6],g=T[7];var D,ah,N=null,au=null;if(V&&af){N=Math.floor(Math.max(0,(V-L)*ar));au=Math.ceil(Math.min(a,Math.max(0,(af-L)*ar)))}if(q!=="Dense"&&M!==undefined&&ao>L){H.fillStyle=ag;if(o===0&&ad-H.measureText(M).width<0){H.textAlign="left";H.fillText(M,I+2+W,X+8)}else{H.textAlign="right";H.fillText(M,ad-2+W,X+8)}H.fillStyle=h}if(g){if(E){if(E=="+"){H.fillStyle=RIGHT_STRAND}else{if(E=="-"){H.fillStyle=LEFT_STRAND}}H.fillRect(ad+W,X,I-ad,10);H.fillStyle=h}for(var al=0,f=g.length;al<f;al++){var t=g[al],d=Math.floor(Math.max(0,(t[0]-L)*ar))
,U=Math.ceil(Math.min(a,Math.max((t[1]-L)*ar)));if(d>U){continue}D=5;ah=3;H.fillRect(d+W,X+ah,U-d,D);if(N!==undefined&&!(d>au||U<N)){D=9;ah=1;var ai=Math.max(d,N),z=Math.min(U,au);H.fillRect(ai+W,X+ah,z-ai,D)}}}else{D=9;ah=1;H.fillRect(ad+W,X+ah,I-ad,D);if(T.strand){if(T.strand=="+"){H.fillStyle=RIGHT_STRAND_INV}else{if(T.strand=="-"){H.fillStyle=LEFT_STRAND_INV}}H.fillRect(ad+W,X,I-ad,10);H.fillStyle=h}}}}else{if(G.dataset_type==="vcf"){if(ac){H.fillStyle=h;H.fillRect(ad+W,X+5,I-ad,1)}else{var s=T[4],m=T[5],c=T[6];D=9;ah=1;H.fillRect(ad+W,X,I-ad,D);if(q!=="Dense"&&M!==undefined&&ao>L){H.fillStyle=ag;if(o===0&&ad-H.measureText(M).width<0){H.textAlign="left";H.fillText(M,I+2+W,X+8)}else{H.textAlign="right";H.fillText(M,ad-2+W,X+8)}H.fillStyle=h}var l=s+" / "+m;if(ao>L&&H.measureText(l).width<(I-ad)){H.fillStyle="white";H.textAlign="center";H.fillText(l,W+ad+(I-ad)/2,X+8);H.fillStyle=h}}}}}am++}}r.append(S);return S},gen_options:function(j){var a=$("<div />").addClass("form-ro
w");var e="track_"+j+"_block_color",l=$("<label />").attr("for",e).text("Block color:"),m=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),k="track_"+j+"_label_color",g=$("<label />").attr("for",k).text("Text color:"),h=$("<input />").attr("id",k).attr("name",k).val(this.prefs.label_color),f="track_"+j+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(l).append(m).append(g).append(h).append(d)},update_options:function(e){var b=$("#track_"+e+"_block_color").val(),d=$("#track_"+e+"_label_color").val(),c=$("#track_"+e+"_mode option:selected").val(),a=$("#track_"+e+"_show_count").attr("checked");if(b!==this.prefs.block_color||d!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=d;this.prefs.show_counts=a;this.t
ile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,c){FeatureTrack.call(this,d,b,a,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -98,15 +98,17 @@ var View = function( container, chrom, t
var parent_element = this.container,
view = this;
this.top_labeltrack = $("<div/>").addClass("top-labeltrack").appendTo(parent_element);
- this.content_div = $("<div/>").addClass("content").css("position", "relative").hide().appendTo(parent_element);
- this.intro_div = $("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide().appendTo(parent_element);
+ this.content_div = $("<div/>").addClass("content").css("position", "relative").appendTo(parent_element);
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(); // Future overlay
this.nav_container = $("<div/>").addClass("nav-container").appendTo(parent_element);
this.nav_labeltrack = $("<div/>").addClass("nav-labeltrack").appendTo(this.nav_container);
this.nav = $("<div/>").addClass("nav").appendTo(this.nav_container);
this.overview = $("<div/>").addClass("overview").appendTo(this.nav);
this.overview_viewport = $("<div/>").addClass("overview-viewport").appendTo(this.overview);
+ this.overview_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();
@@ -156,7 +158,6 @@ var View = function( container, chrom, t
}
view.chrom_select.html(chrom_options);
view.intro_div.show();
- view.content_div.hide();
view.chrom_select.bind("change", function() {
view.change_chrom(view.chrom_select.val());
});
@@ -195,6 +196,16 @@ var View = function( container, chrom, t
view.move_delta(-delta_chrom);
});
+ this.overview_close.bind("click", function() {
+ for (var track_id in view.tracks) {
+ view.tracks[track_id].is_overview = false;
+ }
+ $(this).siblings().filter("canvas").remove();
+ $(this).parent().css("height", view.overview_box.height());
+ view.overview_highlight.hide();
+ $(this).hide();
+ });
+
this.viewport_container.bind( "dragstart", function( e ) {
this.original_low = view.low;
this.current_height = e.clientY;
@@ -262,10 +273,8 @@ var View = function( container, chrom, t
if (view.chrom === "") {
// No chrom selected
view.intro_div.show();
- view.content_div.hide();
} else {
view.intro_div.hide();
- view.content_div.show();
}
view.chrom_select.val(view.chrom);
view.max_high = found.len;
@@ -381,10 +390,14 @@ var View = function( container, chrom, t
this.zoom_res = Math.pow( FEATURE_LEVELS, Math.max(0,Math.ceil( Math.log( this.resolution, FEATURE_LEVELS ) / Math.log(FEATURE_LEVELS) )));
// Overview
- var left_px = ( this.low / (this.max_high - this.max_low) ) * this.overview_viewport.width();
+ var left_px = this.low / (this.max_high - this.max_low) * this.overview_viewport.width();
var width_px = (this.high - this.low)/(this.max_high - this.max_low) * this.overview_viewport.width();
+ var min_width_px = 13;
- this.overview_box.css({ left: left_px, width: Math.max(12, width_px) }).show();
+ this.overview_box.css({ left: left_px, width: Math.max(min_width_px, width_px) }).show();
+ if (width_px < min_width_px) {
+ this.overview_box.css("left", left_px - (min_width_px - width_px)/2);
+ }
if (this.overview_highlight) {
this.overview_highlight.css({ left: left_px, width: width_px });
}
@@ -530,7 +543,7 @@ var TiledTrack = function() {
}
}
var track_dropdown = {};
- track_dropdown["Set track as overview"] = function() {
+ track_dropdown["Set as overview"] = function() {
view.overview_viewport.find("canvas").remove();
track.is_overview = true;
track.set_overview();
@@ -639,11 +652,9 @@ var TiledTrack = function() {
view.overview_box.height(view.default_overview_height);
if (this.initial_canvas && this.is_overview) {
- if (!view.overview_highlight) {
- view.overview_highlight = $("<div />").addClass("overview-highlight").appendTo(view.overview_viewport);
- }
+ view.overview_close.show();
view.overview_viewport.append(this.initial_canvas);
- view.overview_highlight.height(this.initial_canvas.height());
+ view.overview_highlight.show().height(this.initial_canvas.height());
view.overview_viewport.height(this.initial_canvas.height() + view.overview_box.height());
}
$(window).trigger("resize");
@@ -791,13 +802,15 @@ var LineTrack = function ( name, view, d
$('#linetrack_' + track_id + '_minval').remove();
$('#linetrack_' + track_id + '_maxval').remove();
+ track.container_div.css("position", "relative");
+
var min_label = $("<div />").addClass('yaxislabel').attr("id", 'linetrack_' + track_id + '_minval').text(round_1000(track.prefs.min_value));
var max_label = $("<div />").addClass('yaxislabel').attr("id", 'linetrack_' + track_id + '_maxval').text(round_1000(track.prefs.max_value));
- max_label.css({ position: "relative", top: "32px", left: "10px" });
+ max_label.css({ position: "absolute", top: "22px", left: "10px" });
max_label.prependTo(track.container_div);
- min_label.css({ position: "relative", top: track.height_px + 32 + "px", left: "10px" });
+ min_label.css({ position: "absolute", top: track.height_px + 11 + "px", left: "10px" });
min_label.prependTo(track.container_div);
});
},
Binary file static/images/visualization/draggable_horizontal.png has changed
--- a/lib/galaxy/web/controllers/tracks.py
+++ b/lib/galaxy/web/controllers/tracks.py
@@ -318,11 +318,11 @@ class TracksController( BaseController,
message = None
if isinstance(data, dict) and 'message' in data:
message = data['message']
- dataset_type = data.get( 'data_type', tracks_dataset_type )
+ tracks_dataset_type = data.get( 'data_type', tracks_dataset_type )
track_data = data['data']
else:
track_data = data
- return { 'dataset_type': dataset_type, 'extra_info': extra_info, 'data': track_data, 'message': message }
+ return { 'dataset_type': tracks_dataset_type, 'extra_info': extra_info, 'data': track_data, 'message': message }
@web.json
--- a/lib/galaxy/visualization/tracks/data/array_tree.py
+++ b/lib/galaxy/visualization/tracks/data/array_tree.py
@@ -12,7 +12,7 @@ from base import TracksDataProvider
class ArrayTreeDataProvider( TracksDataProvider ):
def get_stats( self, chrom ):
- f = open( self.dataset.file_name )
+ f = open( self.converted_dataset.file_name )
d = FileArrayTreeDict( f )
try:
chrom_array_tree = d[chrom]
--- a/static/june_2007_style/blue/trackster.css
+++ b/static/june_2007_style/blue/trackster.css
@@ -7,12 +7,14 @@
.nav-input{font-size:12px;width:30em;z-index:1000;}
.location{display:inline-block;width:15em;margin:0px 10px;}
.draghandle{float:left;background:transparent url(../images/visualization/draggable_horizontal.png) center center no-repeat;width:10px;height:12px;margin-right:5px;}
-.intro{margin-top:200px;margin-left:auto;margin-right:auto;color:#555;text-align:center;font-size:16px;}
+.intro{z-index:1000;margin-left:auto;margin-right:auto;color:#555;text-align:center;font-size:16px;}
.overview{width:100%;margin:0px;color:white;}
.overview-viewport{position:relative;height:14px;background:white;border-bottom:solid gray 1px;margin:0;}
-.overview-highlight{opacity:0.5;top:0px;position:absolute;z-index:100;border-style:solid;border-color:#6a6;border-width:0px 1px;}
+.overview-close{font:9px verdana;position:absolute;top:0px;right:0px;padding:5px;z-index:500;background-color:white;}
+.overview-highlight{opacity:0.5;top:0px;position:absolute;z-index:100;border-style:solid;border-color:#484848;border-width:0px 1px;}
.overview-boxback{width:100%;bottom:0px;z-index:50;position:absolute;height:14px;background:#eee;}
-.overview-box{opacity:0.5;bottom:0px;z-index:100;position:absolute;margin-top:0px;height:14px;background:#cfc url(../images/visualization/draggable_horizontal.png) center center no-repeat;border-style:solid;border-color:#6a6;border-width:0px 1px;}
+.overview-box{cursor:pointer;opacity:0.5;bottom:0px;z-index:100;position:absolute;margin-top:0px;height:14px;background:#484848 url(../images/visualization/draggable_horizontal.png) center center no-repeat;border-style:solid;border-color:#484848;border-width:0px 1px;-moz-border-radius:3px;border-radius:3px;}
+.overview-box:hover{background-color:#838383;border-color:#838383;}
.viewport-canvas{width:100%;height:100px;}
.yaxislabel{color:#777;}
.line-track .track-content{border-top:1px solid #ddd;border-bottom:1px solid #ddd;}
--- a/static/june_2007_style/trackster.css.tmpl
+++ b/static/june_2007_style/trackster.css.tmpl
@@ -42,7 +42,8 @@
margin-right: 5px;
}
.intro {
- margin-top: 200px;
+ z-index: 1000;
+/* margin-top: 200px;*/
margin-left: auto;
margin-right: auto;
color: #555;
@@ -63,13 +64,22 @@
border-bottom: solid gray 1px;
margin: 0;
}
+.overview-close {
+ font: 9px verdana;
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ padding: 5px;
+ z-index: 500;
+ background-color: white;
+}
.overview-highlight {
opacity: 0.5;
top: 0px;
position: absolute;
z-index: 100;
border-style: solid;
- border-color: #6a6;
+ border-color: #484848;
border-width: 0px 1px;
}
.overview-boxback {
@@ -81,16 +91,23 @@
background: #eee;
}
.overview-box {
+ cursor: pointer;
opacity: 0.5;
bottom: 0px;
z-index: 100;
position: absolute;
margin-top: 0px;
height: 14px;
- background: #cfc url(../images/visualization/draggable_horizontal.png) center center no-repeat;
+ background: #484848 url(../images/visualization/draggable_horizontal.png) center center no-repeat;
border-style: solid;
- border-color: #6a6;
+ border-color: #484848;
border-width: 0px 1px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+.overview-box:hover {
+ background-color: #838383;
+ border-color: #838383;
}
.viewport-canvas {
width: 100%;
1
0

galaxy-dist commit e082f7f28e62: Add requirements tag to fasta to bowtie index converters.
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 Dan Blankenberg <dan(a)bx.psu.edu>
# Date 1286544214 14400
# Node ID e082f7f28e628263faacb652ef9d0ee9b37d8532
# Parent 7c5d27bf6a41cd215088b8b9081572a30735bd43
Add requirements tag to fasta to bowtie index converters.
--- a/lib/galaxy/datatypes/converters/fasta_to_bowtie_color_index_converter.xml
+++ b/lib/galaxy/datatypes/converters/fasta_to_bowtie_color_index_converter.xml
@@ -1,4 +1,5 @@
<tool id="CONVERTER_fasta_to_bowtie_color_index" name="Convert FASTA to Bowtie color space Index" version="1.0.0">
+ <requirements><requirement type='package'>bowtie</requirement></requirements><!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> --><!-- Used on the metadata edit page. --><command>
--- a/lib/galaxy/datatypes/converters/fasta_to_bowtie_base_index_converter.xml
+++ b/lib/galaxy/datatypes/converters/fasta_to_bowtie_base_index_converter.xml
@@ -1,4 +1,5 @@
<tool id="CONVERTER_fasta_to_bowtie_base_index" name="Convert FASTA to Bowtie base space Index" version="1.0.0">
+ <requirements><requirement type='package'>bowtie</requirement></requirements><!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> --><!-- Used on the metadata edit page. --><command>
1
0

galaxy-dist commit b36700318a81: Making the 'Lab' tab visible to users own at least one sequencing request
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 1286562829 14400
# Node ID b36700318a817db7d9817213aef9e7d1602b9aec
# Parent d3fa82adee9782d27d4de9a92a3d1be47bd138c8
Making the 'Lab' tab visible to users own at least one sequencing request
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -78,7 +78,7 @@
%>
## Lab menu.
- %if trans.user and trans.user.accessible_request_types(trans):
+ %if trans.user and trans.user.requests:
<%
menu_options = [
[ 'Sequencing Requests', h.url_for( controller='/requests', action='index' ) ],
1
0

galaxy-dist commit b67978eeba2c: Add framework for specifying dynamic filters for tracks; framework uses datatype and dataset metadata to specify filters. VCF data provider uses framework to define filter for quality scores. Trackster client does not yet support filters.
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 jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1286804627 14400
# Node ID b67978eeba2c67c86453515d90f81a13acb88586
# Parent ddcfb334eb3ad53425a62f4b094374b219daa455
Add framework for specifying dynamic filters for tracks; framework uses datatype and dataset metadata to specify filters. VCF data provider uses framework to define filter for quality scores. Trackster client does not yet support filters.
--- a/lib/galaxy/datatypes/tabular.py
+++ b/lib/galaxy/datatypes/tabular.py
@@ -462,7 +462,14 @@ class ElandMulti( Tabular ):
return False
class Vcf( Tabular ):
+ """ Variant Call Format for describing SNPs and other simple genome variations. """
+
file_ext = 'vcf'
+ column_names = [ 'Chrom', 'Pos', 'ID', 'Ref', 'Alt', 'Qual', 'Filter', 'Info', 'Format', 'data' ]
+
+ MetadataElement( name="columns", default=10, desc="Number of columns", readonly=True, visible=False )
+ MetadataElement( name="column_types", default=['str','int','str','str','str','int','str','list','str','str'], param=metadata.ColumnTypesParameter, desc="Column types", readonly=True, visible=False )
+ MetadataElement( name="viz_filter_columns", default=[5] )
def sniff( self, filename ):
try:
@@ -472,6 +479,21 @@ class Vcf( Tabular ):
return True
except:
return False
+
+ def make_html_table( self, dataset, skipchars=[] ):
+ """Create HTML table, used for displaying peek"""
+ out = ['<table cellspacing="0" cellpadding="3">']
+ try:
+ # Generate column header
+ out.append( '<tr>' )
+ for i, name in enumerate( self.column_names ):
+ out.append( '<th>%s.%s</th>' % ( str( i+1 ), name ) )
+ out.append( self.make_html_peek_rows( dataset, skipchars=skipchars ) )
+ out.append( '</table>' )
+ out = "".join( out )
+ except Exception, exc:
+ out = "Can't create peek %s" % exc
+ return out
def get_track_type( self ):
return "FeatureTrack", {"data": "interval_index", "index": "summary_tree"}
--- a/lib/galaxy/web/controllers/tracks.py
+++ b/lib/galaxy/web/controllers/tracks.py
@@ -24,7 +24,8 @@ from galaxy.visualization.tracks.data.ar
from galaxy.visualization.tracks.data.interval_index import IntervalIndexDataProvider
from galaxy.visualization.tracks.data.bam import BamDataProvider
from galaxy.visualization.tracks.data.summary_tree import SummaryTreeDataProvider
-from galaxy.visualization.tracks.data.vcf import VCFDataProvider
+from galaxy.visualization.tracks.data.vcf import VcfDataProvider
+from galaxy.visualization.tracks.data.base import dataset_to_data_provider
# Message strings returned to browser
messages = Bunch(
@@ -39,9 +40,10 @@ messages = Bunch(
# Mapping from dataset type to a class that can fetch data from a file of that
# type. First key is converted dataset type; if result is another dict, second key
# is original dataset type. TODO: This needs to be more flexible.
+# TODO: move this mapping into TracksDataProvider
dataset_type_to_data_provider = {
"array_tree": ArrayTreeDataProvider,
- "interval_index": { "vcf": VCFDataProvider, "default" : IntervalIndexDataProvider },
+ "interval_index": { "vcf": VcfDataProvider, "default" : IntervalIndexDataProvider },
"bai": BamDataProvider,
"summary_tree": SummaryTreeDataProvider
}
@@ -150,12 +152,15 @@ class TracksController( BaseController,
hda_query = trans.sa_session.query( model.HistoryDatasetAssociation )
dataset = hda_query.get( dataset_id )
track_type, _ = dataset.datatype.get_track_type()
+ track_data_provider_class = dataset_to_data_provider( dataset )
+ track_data_provider = track_data_provider_class( original_dataset=dataset )
track = {
"track_type": track_type,
"name": dataset.name,
"dataset_id": dataset.id,
"prefs": {},
+ "filters": track_data_provider.get_filters()
}
return track
--- a/lib/galaxy/visualization/tracks/data/base.py
+++ b/lib/galaxy/visualization/tracks/data/base.py
@@ -1,7 +1,17 @@
+from galaxy.datatypes.tabular import Vcf
+from galaxy.visualization.tracks import data
+
class TracksDataProvider( object ):
""" Base class for tracks data providers. """
- def __init__( self, converted_dataset, original_dataset ):
+ """
+ Mapping from column name to index in data. This mapping is used to create
+ filters.
+ """
+ col_name_data_index_mapping = {}
+
+ def __init__( self, converted_dataset=None, original_dataset=None ):
+ """ Create basic data provider. """
self.converted_dataset = converted_dataset
self.original_dataset = original_dataset
@@ -9,3 +19,59 @@ class TracksDataProvider( object ):
""" Returns data in region defined by chrom, start, and end. """
# Override.
pass
+
+ def get_filters( self ):
+ """
+ Returns filters for provider's data. Return value is a list of
+ filters; each filter is a dictionary with the keys 'name', 'index', 'value'.
+ NOTE: This method uses the original dataset's datatype and metadata to
+ create the filters.
+ """
+ # Get column names.
+ try:
+ column_names = self.original_dataset.datatype.column_names
+ except AttributeError:
+ column_names = range( self.original_dataset.metadata.columns )
+
+ # Dataset must have column types; if not, cannot create filters.
+ try:
+ column_types = self.original_dataset.metadata.column_types
+ except AttributeError:
+ return []
+
+ # Create and return filters.
+ filters = []
+ if self.original_dataset.metadata.viz_filter_columns:
+ for viz_col_index in self.original_dataset.metadata.viz_filter_columns:
+ col_name = column_names[ viz_col_index ]
+ # Make sure that column has a mapped index. If not, do not add filter.
+ try:
+ index = self.col_name_data_index_mapping[ col_name ]
+ except KeyError:
+ continue
+ filters.append(
+ { 'name' : col_name, 'value' : column_types[viz_col_index], \
+ 'index' : index } )
+ return filters
+
+
+#
+# Helper methods.
+#
+
+def dataset_to_data_provider( dataset=None ):
+ """
+ Returns data provider for a dataset.
+ """
+ # TODO: merge this method with the dict in tracks controller to provide a
+ # unified way to get data providers based on dataset/converted dataset type.
+ if isinstance( dataset.datatype, Vcf ):
+ return data.vcf.VcfDataProvider
+ else:
+ try:
+ # If get_track_type is available, then generic data provider
+ # should work.
+ dataset.datatype.get_track_type()
+ return TracksDataProvider
+ except e:
+ return None
--- a/lib/galaxy/visualization/tracks/data/vcf.py
+++ b/lib/galaxy/visualization/tracks/data/vcf.py
@@ -1,10 +1,3 @@
-"""
-VCF data provider for the Galaxy track browser.
-
-Payload format:
-[ uid (offset), start, end, ID, reference base(s), alternate base(s), quality score]
-"""
-
import pkg_resources; pkg_resources.require( "bx-python" )
from bx.interval_index_file import Indexes
from galaxy.datatypes.tabular import Vcf
@@ -12,8 +5,15 @@ from base import TracksDataProvider
MAX_VALS = 5000 # only display first MAX_VALS features
-class VCFDataProvider( TracksDataProvider ):
- """ Provides data for VCF tracks. """
+class VcfDataProvider( TracksDataProvider ):
+ """
+ VCF data provider for the Galaxy track browser.
+
+ Payload format:
+ [ uid (offset), start, end, ID, reference base(s), alternate base(s), quality score]
+ """
+
+ col_name_data_index_mapping = { 'Qual' : 6 }
def get_data( self, chrom, start, end, **kwargs ):
""" Returns data in region defined by chrom, start, and end. """
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -9,6 +9,7 @@ from galaxy.model.orm import *
from galaxy.workflow.modules import *
from galaxy.web.framework import simplejson
from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, TextArea, TextField, WorkflowField
+from galaxy.visualization.tracks.data.base import dataset_to_data_provider
from Cheetah.Template import Template
@@ -169,11 +170,15 @@ class UsesVisualization( SharableItemSec
prefs = {}
dataset = hda_query.get( dataset_id )
track_type, _ = dataset.datatype.get_track_type()
+ track_data_provider_class = dataset_to_data_provider( dataset )
+ track_data_provider = track_data_provider_class( original_dataset=dataset )
+
tracks.append( {
"track_type": track_type,
"name": dataset.name,
"dataset_id": dataset.id,
"prefs": simplejson.dumps(prefs),
+ "filters": track_data_provider.get_filters()
} )
config = { "title": visualization.title, "vis_id": trans.security.encode_id( visualization.id ),
1
0

galaxy-dist commit d85159be56e8: Add basic support for bowtie indexes as a datatype (bowtie_base_index, bowtie_color_index), available via datatype conversion. Currently, the indexes need to be converted manually from the FASTA file before use in bowtie, but they can be reused.
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 Dan Blankenberg <dan(a)bx.psu.edu>
# Date 1286485181 14400
# Node ID d85159be56e854731822a314a2e982e0c6987389
# Parent 5038f0863ab9a8286fd97c718c63945bfd442dd0
Add basic support for bowtie indexes as a datatype (bowtie_base_index, bowtie_color_index), available via datatype conversion. Currently, the indexes need to be converted manually from the FASTA file before use in bowtie, but they can be reused.
More work is required to allow the one-off indexes built by bowtie to become Galaxy datasets; alternatively, the custom genome selection could be limited to the index datatype for input (and not allow fasta directly), which would allow implicit datatype conversion to occur when a fasta file is selected as input, but this would prevent the index tuning that is currently available when currently selecting a fasta file.
--- a/datatypes_conf.xml.sample
+++ b/datatypes_conf.xml.sample
@@ -45,10 +45,14 @@
<datatype extension="csv" type="galaxy.datatypes.tabular:Tabular"/><!-- End MSI added Datatypes --><datatype extension="customtrack" type="galaxy.datatypes.interval:CustomTrack"/>
+ <datatype extension="bowtie_color_index" type="galaxy.datatypes.ngsindex:BowtieColorIndex" mimetype="text/html" display_in_upload="False"/>
+ <datatype extension="bowtie_base_index" type="galaxy.datatypes.ngsindex:BowtieBaseIndex" mimetype="text/html" display_in_upload="False"/><datatype extension="csfasta" type="galaxy.datatypes.sequence:csFasta" display_in_upload="true"/><datatype extension="data" type="galaxy.datatypes.data:Data" mimetype="application/octet-stream" max_optional_metadata_filesize="1048576" /><datatype extension="fasta" type="galaxy.datatypes.sequence:Fasta" display_in_upload="true"><converter file="fasta_to_tabular_converter.xml" target_datatype="tabular"/>
+ <converter file="fasta_to_bowtie_base_index_converter.xml" target_datatype="bowtie_base_index"/>
+ <converter file="fasta_to_bowtie_color_index_converter.xml" target_datatype="bowtie_color_index"/></datatype><datatype extension="fastq" type="galaxy.datatypes.sequence:Fastq" display_in_upload="true"/><datatype extension="fastqsanger" type="galaxy.datatypes.sequence:FastqSanger" display_in_upload="true"/>
--- a/tools/sr_mapping/bowtie_wrapper.xml
+++ b/tools/sr_mapping/bowtie_wrapper.xml
@@ -12,6 +12,10 @@
--snpfrac="None"
--keepends="None"
#if $refGenomeSource.genomeSource == "history":
+ #if $refGenomeSource.ownFile.extension.startswith( 'bowtie_' ):
+ --ref="${refGenomeSource.ownFile.extra_files_path}/${refGenomeSource.ownFile.metadata.base_name}"
+ --do_not_build_index
+ #else:
--ref=$refGenomeSource.ownFile
--indexSettings=$refGenomeSource.indexParams.indexSettings
#if $refGenomeSource.indexParams.indexSettings == "indexFull":
@@ -50,6 +54,7 @@
--iseed="None"
--icutoff="None"
#end if
+ #end if
#else:
--ref=$refGenomeSource.index
--indexSettings="None"
@@ -203,9 +208,9 @@
</param></when><when value="history">
- <param name="ownFile" type="data" format="fasta" metadata_name="dbkey" label="Select the reference genome" />
+ <param name="ownFile" type="data" format="bowtie_base_index,fasta" metadata_name="dbkey" label="Select the reference genome" /><conditional name="indexParams">
- <param name="indexSettings" type="select" label="Choose whether to use Default options for building indices or to Set your own">
+ <param name="indexSettings" type="select" label="Choose whether to use Default options for building indices or to Set your own" help="These settings are ignored when using a prebuilt index"><option value="indexPreSet">Default</option><option value="indexFull">Set your own</option></param>
--- /dev/null
+++ b/lib/galaxy/datatypes/converters/fasta_to_bowtie_base_index_converter.xml
@@ -0,0 +1,18 @@
+<tool id="CONVERTER_fasta_to_bowtie_base_index" name="Convert FASTA to Bowtie base space Index" version="1.0.0">
+ <!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> -->
+ <!-- Used on the metadata edit page. -->
+ <command>
+ mkdir ${output.files_path}
+ && bowtie-build --quiet
+ -f
+ $input ${output.files_path}/${output.metadata.base_name}
+ </command>
+ <inputs>
+ <param name="input" type="data" format="fasta" label="Fasta file"/>
+ </inputs>
+ <outputs>
+ <data name="output" format="bowtie_base_index"/>
+ </outputs>
+ <help>
+ </help>
+</tool>
--- a/lib/galaxy/datatypes/registry.py
+++ b/lib/galaxy/datatypes/registry.py
@@ -3,7 +3,7 @@ Provides mapping between extensions and
"""
import os, tempfile
import logging
-import data, tabular, interval, images, sequence, qualityscore, genetics, xml, coverage, tracks, chrominfo, binary, assembly
+import data, tabular, interval, images, sequence, qualityscore, genetics, xml, coverage, tracks, chrominfo, binary, assembly, ngsindex
import galaxy.util
from galaxy.util.odict import odict
from display_applications.application import DisplayApplication
--- a/tools/sr_mapping/bowtie_wrapper.py
+++ b/tools/sr_mapping/bowtie_wrapper.py
@@ -56,6 +56,7 @@ usage: bowtie_wrapper.py [options]
-c, --icutoff=c: Number of first bases of the reference sequence to index
-x, --indexSettings=x: Whether or not indexing options are to be set
-H, --suppressHeader=H: Suppress header
+ --do_not_build_index: Flag to specify that provided file is already indexed and to just use 'as is'
"""
import optparse, os, shutil, subprocess, sys, tempfile
@@ -118,6 +119,7 @@ def __main__():
parser.add_option( '-c', '--icutoff', dest='icutoff', help='Number of first bases of the reference sequence to index' )
parser.add_option( '-x', '--indexSettings', dest='index_settings', help='Whether or not indexing options are to be set' )
parser.add_option( '-H', '--suppressHeader', dest='suppressHeader', help='Suppress header' )
+ parser.add_option( '--do_not_build_index', dest='do_not_build_index', action="store_true", default=False, help='Flag to specify that provided file is already indexed, use as is' )
(options, args) = parser.parse_args()
stdout = ''
@@ -129,7 +131,7 @@ def __main__():
else:
colorspace = ''
# index if necessary
- if options.genomeSource == 'history':
+ if options.genomeSource == 'history' and not options.do_not_build_index:
# set up commands
if options.index_settings =='indexPreSet':
indexing_cmds = '%s' % colorspace
--- a/tools/sr_mapping/bowtie_color_wrapper.xml
+++ b/tools/sr_mapping/bowtie_color_wrapper.xml
@@ -9,6 +9,10 @@
--suppressHeader=$suppressHeader
--genomeSource=$refGenomeSource.genomeSource
#if $refGenomeSource.genomeSource == "history":
+ #if $refGenomeSource.ownFile.extension.startswith( 'bowtie_' ):
+ --ref="${refGenomeSource.ownFile.extra_files_path}/${refGenomeSource.ownFile.metadata.base_name}"
+ --do_not_build_index
+ #else:
--ref=$refGenomeSource.ownFile
--indexSettings=$refGenomeSource.indexParams.indexSettings
#if $refGenomeSource.indexParams.indexSettings == "indexFull":
@@ -47,6 +51,7 @@
--iseed="None"
--icutoff="None"
#end if
+ #end if
#else:
--ref=$refGenomeSource.index
--indexSettings="None"
@@ -206,9 +211,9 @@
</param></when><when value="history">
- <param name="ownFile" type="data" format="fasta" metadata_name="dbkey" label="Select the reference genome" />
+ <param name="ownFile" type="data" format="bowtie_color_index,fasta" metadata_name="dbkey" label="Select the reference genome" /><conditional name="indexParams">
- <param name="indexSettings" type="select" label="Choose whether to use Default options for building indices or to Set your own">
+ <param name="indexSettings" type="select" label="Choose whether to use Default options for building indices or to Set your own" help="These settings are ignored when using a prebuilt index"><option value="indexPreSet">Default</option><option value="indexFull">Set your own</option></param>
--- /dev/null
+++ b/lib/galaxy/datatypes/ngsindex.py
@@ -0,0 +1,75 @@
+"""
+NGS indexes
+"""
+import logging
+from metadata import MetadataElement
+from images import Html
+
+log = logging.getLogger(__name__)
+
+class BowtieIndex( Html ):
+ """
+ base class for BowtieIndex
+ is subclassed by BowtieColorIndex and BowtieBaseIndex
+ """
+ MetadataElement( name="base_name", desc="base name for this index set", default='galaxy_generated_bowtie_index', set_in_upload=True, readonly=True )
+ MetadataElement( name="sequence_space", desc="sequence_space for this index set", default='unknown', set_in_upload=True, readonly=True )
+
+ file_ext = 'bowtie_index'
+ is_binary = True
+ composite_type = 'auto_primary_file'
+ allow_datatype_change = False
+
+ def generate_primary_file( self, dataset = None ):
+ """
+ This is called only at upload to write the html file
+ cannot rename the datasets here - they come with the default unfortunately
+ """
+ return '<html><head></head><body>AutoGenerated Primary File for Composite Dataset</body></html>'
+
+ def regenerate_primary_file(self,dataset):
+ """
+ cannot do this until we are setting metadata
+ """
+ bn = dataset.metadata.base_name
+ flist = os.listdir(dataset.extra_files_path)
+ rval = ['<html><head><title>Files for Composite Dataset %s</title></head><p/>Comprises the following files:<p/><ul>' % (bn)]
+ for i,fname in enumerate(flist):
+ sfname = os.path.split(fname)[-1]
+ rval.append( '<li><a href="%s">%s</a>' % ( sfname, sfname ) )
+ rval.append( '</ul></html>' )
+ f = file(dataset.file_name,'w')
+ f.write("\n".join( rval ))
+ f.write('\n')
+ f.close()
+
+ def set_peek( self, dataset, is_multi_byte=False ):
+ if not dataset.dataset.purged:
+ dataset.peek = "Bowtie index file (%s)" % ( dataset.metadata.sequence_space )
+ dataset.blurb = "%s space" % ( dataset.metadata.sequence_space )
+ else:
+ dataset.peek = 'file does not exist'
+ dataset.blurb = 'file purged from disk'
+ def display_peek( self, dataset ):
+ try:
+ return dataset.peek
+ except:
+ return "Bowtie index file"
+ def sniff( self, filename ):
+ return False
+
+class BowtieColorIndex( BowtieIndex ):
+ """
+ Bowtie color space index
+ """
+ MetadataElement( name="sequence_space", desc="sequence_space for this index set", default='color', set_in_upload=True, readonly=True )
+
+ file_ext = 'bowtie_color_index'
+
+class BowtieBaseIndex( BowtieIndex ):
+ """
+ Bowtie base space index
+ """
+ MetadataElement( name="sequence_space", desc="sequence_space for this index set", default='base', set_in_upload=True, readonly=True )
+
+ file_ext = 'bowtie_base_index'
--- /dev/null
+++ b/lib/galaxy/datatypes/converters/fasta_to_bowtie_color_index_converter.xml
@@ -0,0 +1,19 @@
+<tool id="CONVERTER_fasta_to_bowtie_color_index" name="Convert FASTA to Bowtie color space Index" version="1.0.0">
+ <!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> -->
+ <!-- Used on the metadata edit page. -->
+ <command>
+ mkdir ${output.files_path}
+ && bowtie-build --quiet
+ --color
+ -f
+ $input ${output.files_path}/${output.metadata.base_name}
+ </command>
+ <inputs>
+ <param name="input" type="data" format="fasta" label="Fasta file"/>
+ </inputs>
+ <outputs>
+ <data name="output" format="bowtie_color_index"/>
+ </outputs>
+ <help>
+ </help>
+</tool>
1
0

galaxy-dist commit 3316517f0980: Enable 'FASTX-Toolkit for FASTQ data' as a subsection under 'NGS: QC and manipulation' in tool_conf.xml.sample/main.
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 Dan Blankenberg <dan(a)bx.psu.edu>
# Date 1286458054 14400
# Node ID 3316517f0980642b7706f26ab3a5b4ec461acfde
# Parent 9cada14f87657b951ccc9a76af5de03263ba72d9
Enable 'FASTX-Toolkit for FASTQ data' as a subsection under 'NGS: QC and manipulation' in tool_conf.xml.sample/main.
--- a/tool_conf.xml.sample
+++ b/tool_conf.xml.sample
@@ -218,15 +218,6 @@
<tool file="fastq/fastq_paired_end_splitter.xml" /><tool file="fastq/fastq_paired_end_joiner.xml" /><tool file="fastq/fastq_stats.xml" />
- <!--<label text="Deprecated: Generic FASTQ data" id="fastq" />
- <tool file="next_gen_conversion/fastq_gen_conv.xml" />
- <tool file="fastx_toolkit/fastq_quality_converter.xml" />
- <tool file="fastx_toolkit/fastx_quality_statistics.xml" />
- <tool file="fastx_toolkit/fastq_quality_boxplot.xml" />
- <tool file="fastx_toolkit/fastx_nucleotides_distribution.xml" />
- <tool file="metag_tools/split_paired_reads.xml"
- <tool file="fastx_toolkit/fastq_to_fasta.xml" />
- --><label text="Roche-454 data" id="454" /><tool file="metag_tools/short_reads_figure_score.xml" /><tool file="metag_tools/short_reads_trim_seq.xml" />
@@ -244,6 +235,21 @@
<tool file="fastq/fastq_to_fasta.xml" /><tool file="fastq/fastq_to_tabular.xml" /><tool file="fastq/tabular_to_fastq.xml" />
+ <label text="FASTX-Toolkit for FASTQ data" id="fastx_toolkit" />
+ <tool file="fastx_toolkit/fastq_quality_converter.xml" />
+ <tool file="fastx_toolkit/fastx_quality_statistics.xml" />
+ <tool file="fastx_toolkit/fastq_quality_boxplot.xml" />
+ <tool file="fastx_toolkit/fastx_nucleotides_distribution.xml" />
+ <tool file="fastx_toolkit/fastq_to_fasta.xml" />
+ <tool file="fastx_toolkit/fastq_quality_filter.xml" />
+ <tool file="fastx_toolkit/fastq_to_fasta.xml" />
+ <tool file="fastx_toolkit/fastx_artifacts_filter.xml" />
+ <tool file="fastx_toolkit/fastx_barcode_splitter.xml" />
+ <tool file="fastx_toolkit/fastx_clipper.xml" />
+ <tool file="fastx_toolkit/fastx_collapser.xml" />
+ <tool file="fastx_toolkit/fastx_renamer.xml" />
+ <tool file="fastx_toolkit/fastx_reverse_complement.xml" />
+ <tool file="fastx_toolkit/fastx_trimmer.xml" /></section><!--
Keep this section commented until it includes tools that
--- a/tool_conf.xml.main
+++ b/tool_conf.xml.main
@@ -305,15 +305,6 @@
<tool file="fastq/fastq_paired_end_splitter.xml" /><tool file="fastq/fastq_paired_end_joiner.xml" /><tool file="fastq/fastq_stats.xml" />
- <!--<label text="Deprecated: Generic FASTQ data" id="fastq" />
- <tool file="next_gen_conversion/fastq_gen_conv.xml" />
- <tool file="fastx_toolkit/fastq_quality_converter.xml" />
- <tool file="fastx_toolkit/fastx_quality_statistics.xml" />
- <tool file="fastx_toolkit/fastq_quality_boxplot.xml" />
- <tool file="fastx_toolkit/fastx_nucleotides_distribution.xml" />
- <tool file="metag_tools/split_paired_reads.xml" />
- <tool file="fastx_toolkit/fastq_to_fasta.xml" />
- --><label text="Roche-454 data" id="454" /><tool file="metag_tools/short_reads_figure_score.xml" /><tool file="metag_tools/short_reads_trim_seq.xml" />
@@ -331,6 +322,21 @@
<tool file="fastq/fastq_to_fasta.xml" /><tool file="fastq/fastq_to_tabular.xml" /><tool file="fastq/tabular_to_fastq.xml" />
+ <label text="FASTX-Toolkit for FASTQ data" id="fastx_toolkit_fastq" />
+ <tool file="fastx_toolkit/fastq_quality_converter.xml" />
+ <tool file="fastx_toolkit/fastx_quality_statistics.xml" />
+ <tool file="fastx_toolkit/fastq_quality_boxplot.xml" />
+ <tool file="fastx_toolkit/fastx_nucleotides_distribution.xml" />
+ <tool file="fastx_toolkit/fastq_to_fasta.xml" />
+ <tool file="fastx_toolkit/fastq_quality_filter.xml" />
+ <tool file="fastx_toolkit/fastq_to_fasta.xml" />
+ <tool file="fastx_toolkit/fastx_artifacts_filter.xml" />
+ <tool file="fastx_toolkit/fastx_barcode_splitter.xml" />
+ <tool file="fastx_toolkit/fastx_clipper.xml" />
+ <tool file="fastx_toolkit/fastx_collapser.xml" />
+ <tool file="fastx_toolkit/fastx_renamer.xml" />
+ <tool file="fastx_toolkit/fastx_reverse_complement.xml" />
+ <tool file="fastx_toolkit/fastx_trimmer.xml" /></section><section name="NGS: Mapping" id="ngs_mapping"><label text="Illumina" id="illumina"/>
1
0

galaxy-dist commit 4ecbb9b5a360: Make published items set the "Shared Data" tab as the active tab
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 Kanwei Li <kanwei(a)gmail.com>
# Date 1286480034 14400
# Node ID 4ecbb9b5a360c1d1997890917799f31f5896a01e
# Parent 5a5e35e09eab477df04549408b56dd7d0ab024b0
Make published items set the "Shared Data" tab as the active tab
--- a/templates/sharing_base.mako
+++ b/templates/sharing_base.mako
@@ -15,7 +15,7 @@
%><%inherit file="${inherit(context)}"/>
-<%namespace file="./display_common.mako" import="*" />
+<%namespace file="/display_common.mako" import="*" /><%namespace file="/message.mako" import="render_msg" />
##
@@ -157,7 +157,7 @@
if item.published:
item_status = item_status + " and published"
%>
- This ${item_class_name_lc} <strong>${item_status}</strong>.
+ This ${item_class_name_lc} is currently <strong>${item_status}</strong>.
<div><p>Anyone can view and import this ${item_class_name_lc} by visiting the following URL:
@@ -195,10 +195,10 @@
%else: ## item.published == True
## Item is importable and published. User can unpublish or disable import and unpublish.
<input class="action-button" type="submit" name="unpublish" value="Unpublish ${item_class_name}">
- <div class="toolParamHelp">Removes ${item_class_name_lc} from Galaxy's <a href='${h.url_for( action='list_published' )}' target="_top">Published ${item_class_plural_name}</a> section so that it is not publicly listed or searchable.</div>
+ <div class="toolParamHelp">Removes this ${item_class_name_lc} from Galaxy's <a href='${h.url_for( action='list_published' )}' target="_top">Published ${item_class_plural_name}</a> section so that it is not publicly listed or searchable.</div><br><input class="action-button" type="submit" name="disable_link_access_and_unpublish" value="Disable Access to ${item_class_name} via Link and Unpublish">
- <div class="toolParamHelp">Disables ${item_class_name_lc}'s link so that it is not accessible and removes ${item_class_name_lc} from Galaxy's <a href='${h.url_for( action='list_published' )}' target='_top'>Published ${item_class_plural_name}</a> section so that it is not publicly listed or searchable.</div>
+ <div class="toolParamHelp">Disables this ${item_class_name_lc}'s link so that it is not accessible and removes ${item_class_name_lc} from Galaxy's <a href='${h.url_for( action='list_published' )}' target='_top'>Published ${item_class_plural_name}</a> section so that it is not publicly listed or searchable.</div>
%endif
</form>
--- a/templates/history/list_published.mako
+++ b/templates/history/list_published.mako
@@ -4,7 +4,7 @@
<%
self.has_left_panel=False
self.has_right_panel=False
- self.active_view="page"
+ self.active_view="shared"
self.message_box_visible=False
%></%def>
--- a/templates/visualization/list_published.mako
+++ b/templates/visualization/list_published.mako
@@ -4,7 +4,7 @@
<%
self.has_left_panel=False
self.has_right_panel=False
- self.active_view="page"
+ self.active_view="shared"
self.message_box_visible=False
%></%def>
--- a/templates/workflow/list_published.mako
+++ b/templates/workflow/list_published.mako
@@ -4,7 +4,7 @@
<%
self.has_left_panel=False
self.has_right_panel=False
- self.active_view=""
+ self.active_view="shared"
self.message_box_visible=False
%></%def>
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -74,7 +74,7 @@
[ 'Published Visualizations', h.url_for( controller='/visualization', action='list_published' ) ],
[ 'Published Pages', h.url_for( controller='/page', action='list_published' ) ]
]
- tab( "libraries", "Shared Data", h.url_for( controller='/library', action='index'), menu_options=menu_options )
+ tab( "shared", "Shared Data", h.url_for( controller='/library', action='index'), menu_options=menu_options )
%>
## Lab menu.
--- a/templates/page/index.mako
+++ b/templates/page/index.mako
@@ -4,7 +4,7 @@
<%
self.has_left_panel=False
self.has_right_panel=False
- self.active_view="page"
+ self.active_view="shared"
self.message_box_visible=False
%></%def>
--- a/templates/display_base.mako
+++ b/templates/display_base.mako
@@ -25,7 +25,7 @@
self.has_left_panel=False
self.has_right_panel=True
self.message_box_visible=False
- self.active_view=""
+ self.active_view="shared"
self.overlay_visible=False
%></%def>
--- a/templates/page/list_published.mako
+++ b/templates/page/list_published.mako
@@ -4,7 +4,7 @@
<%
self.has_left_panel=False
self.has_right_panel=False
- self.active_view="page"
+ self.active_view="shared"
self.message_box_visible=False
%></%def>
--- a/templates/library/index.mako
+++ b/templates/library/index.mako
@@ -4,7 +4,7 @@
<%
self.has_left_panel=False
self.has_right_panel=False
- self.active_view="libraries"
+ self.active_view="shared"
%></%def>
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -812,7 +812,7 @@
<% num_rows_rendered = 0 %>
%if query.count() == 0:
## No results.
- <tr><td></td><td><em>No Items</em></td></tr>
+ <tr><td colspan="100"><em>No Items</em></td></tr><% num_rows_rendered = 1 %>
%endif
%for i, item in enumerate( query ):
1
0

galaxy-dist commit 5038f0863ab9: Actually change the invocation of sputnik from bx-sputnik as mentioned in my last commit. Thanks Kanwei.
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 1286484637 14400
# Node ID 5038f0863ab9a8286fd97c718c63945bfd442dd0
# Parent b9f3c613c0266e9285679b06e39802a452f4b151
Actually change the invocation of sputnik from bx-sputnik as mentioned in my last commit. Thanks Kanwei.
--- a/tools/regVariation/microsats_alignment_level.py
+++ b/tools/regVariation/microsats_alignment_level.py
@@ -34,7 +34,7 @@ def main():
fout = open(outfile, "w")
print >>fout, "#Block\tSeq1_Name\tSeq1_Start\tSeq1_End\tSeq1_Type\tSeq1_Length\tSeq1_RepeatNumber\tSeq1_Unit\tSeq2_Name\tSeq2_Start\tSeq2_End\tSeq2_Type\tSeq2_Length\tSeq2_RepeatNumber\tSeq2_Unit"
#sputnik_cmd = os.path.join(os.path.split(sys.argv[0])[0], "sputnik")
- sputnik_cmd = "bx-sputnik"
+ sputnik_cmd = "sputnik"
input = infile.read()
skipped = 0
block_num = 0
1
0

galaxy-dist commit b9f3c613c026: Convert a bunch of binary requirement tags to package requirements, and change the sputnik tool to call 'sputnik' instead of 'bx-sputnik'.
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 1286483037 14400
# Node ID b9f3c613c0266e9285679b06e39802a452f4b151
# Parent 4ecbb9b5a360c1d1997890917799f31f5896a01e
Convert a bunch of binary requirement tags to package requirements, and change the sputnik tool to call 'sputnik' instead of 'bx-sputnik'.
--- a/tools/emboss_5/emboss_lindna.xml
+++ b/tools/emboss_5/emboss_lindna.xml
@@ -1,6 +1,7 @@
<tool id="EMBOSS: lindna48" name="lindna" version="5.0.0"><!-- tool produces memory error in ajmem.c --><description>Draws linear maps of DNA constructs</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>lindna -infile $input1 -graphout png -goutfile $out_file1 -ruler $ruler -blocktype $blocktype -maxgroups $maxgroups -maxlabels $maxlabels -intersymbol $intersymbol -intercolour $intercolour
-interticks $interticks -gapsize $gapsize -ticklines $ticklines -textheight $textheight -textlength $textlength -margin $margin -tickheight $tickheight -blockheight $blockheight -rangeheight
$rangeheight -gapgroup $gapgroup -postext $postext -auto</command>
--- a/tools/emboss_5/emboss_getorf.xml
+++ b/tools/emboss_5/emboss_getorf.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: getorf42" name="getorf" version="5.0.0"><description>Finds and extracts open reading frames (ORFs)</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>getorf -sequence $input1 -outseq $out_file1 -table $table -minsize $minsize -maxsize $maxsize -find $find -methionine $methionine -circular $circular -reverse $reverse -flanking $flanking
-osformat2 $out_format1 -auto</command><inputs>
--- a/tools/emboss_5/emboss_dan.xml
+++ b/tools/emboss_5/emboss_dan.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: dan19" name="dan" version="5.0.0"><description>Calculates DNA RNA/DNA melting temperature</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command interpreter="perl">emboss_single_outputfile_wrapper.pl dan -sequence $input1 -windowsize $window -goutfile $out_file1 -graph png -plot $plot1 -shiftincrement $shift -dnaconc $dnaconc
-saltconc $saltconc -product $product -formamide $formamide -mismatch $mismatch -prodlen $prodlen -thermo $thermo -temperature $temperature -rna $rna -outfile $out_file1 -auto</command><inputs>
--- a/tools/emboss_5/emboss_banana.xml
+++ b/tools/emboss_5/emboss_banana.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: banana3" name="banana" version="5.0.0"><description>Bending and curvature plot in B-DNA</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>banana -sequence $input1 -outfile $out_file1 -graph none -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_biosed.xml
+++ b/tools/emboss_5/emboss_biosed.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: biosed4" name="biosed" version="5.0.0"><description>Replace or delete sequence sections</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>biosed -sequence $input1 -outseq $out_file1 -target $target -replace $replace -osformat2 $out_format1 -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_isochore.xml
+++ b/tools/emboss_5/emboss_isochore.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: isochore47" name="isochore" version="5.0.0"><description>Plots isochores in large DNA sequences</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command interpreter="perl">emboss_single_outputfile_wrapper.pl isochore -sequence $input1 -outfile $ofile2 -goutfile $ofile1 -graph png -window $window -shift $shift -auto</command><!-- <command interpreter="perl">emboss_single_outputfile_wrapper.pl isochore -sequence $input1 -goutfile $ofile1 -graph png -window $window -shift $shift -auto</command>--><inputs>
--- a/tools/emboss_5/emboss_helixturnhelix.xml
+++ b/tools/emboss_5/emboss_helixturnhelix.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: helixturnhelix43" name="helixturnhelix" version="5.0.0"><description>Report nucleic acid binding motifs</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>helixturnhelix -sequence $input1 -outfile $out_file1 -mean $mean -sd $sd -minsd $minsd -eightyseven $eightyseven -rformat2 $out_format1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_backtranseq.xml
+++ b/tools/emboss_5/emboss_backtranseq.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: backtranseq2" name="backtranseq" version="5.0.0"><description>Back translate a protein sequence</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>backtranseq -sequence $input1 -outfile $out_file1 -cfile $cfile -osformat2 $out_format1 -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_epestfind.xml
+++ b/tools/emboss_5/emboss_epestfind.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: epestfind29" name="epestfind" version="5.0.0"><description>Finds PEST motifs as potential proteolytic cleavage sites</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command interpreter="perl">emboss_single_outputfile_wrapper.pl epestfind -sequence $input1 -goutfile $ofile2 -outfile $ofile1 -window $window -order $order -potential $potential -poor $poor
-invalid $invalid -map $map -graph png -auto</command><inputs>
--- a/tools/emboss_5/emboss_infoseq.xml
+++ b/tools/emboss_5/emboss_infoseq.xml
@@ -1,6 +1,7 @@
<tool id="EMBOSS: infoseq46" name="infoseq" version="5.0.0"><!-- info contains file information always --><description>Displays some simple information about sequences</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>infoseq -sequence $input1 -outfile $out_file1 -html $html_out1 -heading $heading -usa $usa -name $disname -accession $accession -gi $gi -version $version -type $type -length $length -pgc
$pgc -description $description -auto</command><inputs>
--- a/tools/emboss_5/emboss_dreg.xml
+++ b/tools/emboss_5/emboss_dreg.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: dreg27" name="dreg" version="5.0.0"><description>Regular expression search of a nucleotide sequence</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>dreg -sequence $input1 -outfile $out_file1 -pattern "$pattern" -raccshow3 "no" -rusashow3 "no" -rdesshow3 "no" -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_fuzznuc.xml
+++ b/tools/emboss_5/emboss_fuzznuc.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: fuzznuc37" name="fuzznuc" version="5.0.1"><description>Nucleic acid pattern search</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>fuzznuc -sequence $input1 -outfile $out_file1 -pattern '$pattern' -pmismatch $mismatch -complement $complement -rformat2 $out_format1 -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_cutseq.xml
+++ b/tools/emboss_5/emboss_cutseq.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: cutseq18" name="cutseq" version="5.0.0"><description>Removes a specified section from a sequence</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>cutseq -sequence $input1 -outseq $out_file1 -from $from -to $to -osformat2 $out_format1 -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_digest.xml
+++ b/tools/emboss_5/emboss_digest.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: digest23" name="digest" version="5.0.0"><description>Protein proteolytic enzyme or reagent cleavage digest</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>digest -seqall $input1 -outfile $out_file1 -menu $menu -unfavoured $unfavoured -overlap $overlap -allpartials $allpartials -rformat2 $out_format1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_fuzztran.xml
+++ b/tools/emboss_5/emboss_fuzztran.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: fuzztran39" name="fuzztran" version="5.0.0"><description>Protein pattern search after translation</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>fuzztran -sequence $input1 -outfile $out_file1 -pattern "$pattern" -pmismatch $mismatch -frame $frame -table $table -rformat2 $out_format1 -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_hmoment.xml
+++ b/tools/emboss_5/emboss_hmoment.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: hmoment44" name="hmoment" version="5.0.0"><description>Hydrophobic moment calculation</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>hmoment -seqall $input1 -outfile $out_file1 -window $window -aangle $aangle -graph png -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_antigenic.xml
+++ b/tools/emboss_5/emboss_antigenic.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: antigenic1" name="antigenic" version="5.0.0"><description>Predicts potentially antigenic regions of a protein sequence, using the method of Kolaskar and Tongaonkar.</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>antigenic -sequence $input1 -outfile $out_file1 -minlen $minlen -rformat2 $out_format1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_cpgplot.xml
+++ b/tools/emboss_5/emboss_cpgplot.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: cpgplot15" name="cpgplot" version="5.0.0"><description>Plot CpG rich areas</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command interpreter="perl">emboss_cpgplot_wrapper.pl cpgplot -sequence $input1 -window $window -minlen $minlen -minpc $minpc -outfile $outfile -graph png -goutfile $goutfile -outfeat $outfeat -minoe $minoe -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_codcmp.xml
+++ b/tools/emboss_5/emboss_codcmp.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: codcmp12" name="codcmp" version="5.0.0"><description>Codon usage table comparison</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>codcmp -first $cfile1 -second $cfile2 -outfile $out_file1 -auto</command><inputs><param name="cfile1" type="select">
--- a/tools/emboss_5/emboss_dottup.xml
+++ b/tools/emboss_5/emboss_dottup.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: dottup26" name="dottup" version="5.0.0"><description>Displays a wordmatch dotplot of two sequences</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command interpreter="perl">emboss_single_outputfile_wrapper.pl dottup -asequence $input1 -bsequence $input2 -goutfile $out_file1 -wordsize $wordsize -boxit $boxit -graph png -xygraph png -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_degapseq.xml
+++ b/tools/emboss_5/emboss_degapseq.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: degapseq20" name="degapseq" version="5.0.0"><description>Removes gap characters from sequences</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>degapseq -sequence $input1 -outseq $out_file1 -osformat2 $out_format1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_descseq.xml
+++ b/tools/emboss_5/emboss_descseq.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: descseq21" name="descseq" version="5.0.0"><description>Alter the name or description of a sequence</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>descseq -sequence $input1 -outseq $out_file1 -name "$seqname" -description "$desc" -append $append -osformat2 $out_format1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_extractseq.xml
+++ b/tools/emboss_5/emboss_extractseq.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: extractseq35" name="extractseq" version="5.0.0"><description>Extract regions from a sequence</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>extractseq -sequence $input1 -outseq $out_file1 -regions $regions -separate $separate -osformat2 $out_format1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_chips.xml
+++ b/tools/emboss_5/emboss_chips.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: chips10" name="chips" version="5.0.0"><description>Codon usage statistics</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>chips -seqall $input1 -outfile $out_file1 -sum $sum -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_einverted.xml
+++ b/tools/emboss_5/emboss_einverted.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: einverted28" name="einverted" version="5.0.0"><description>Finds DNA inverted repeats</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>einverted -sequence $input1 -outfile $out_file1 -gap $gap -threshold $threshold -match $match -mismatch $mismatch -maxrepeat $maxrepeat -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_fuzzpro.xml
+++ b/tools/emboss_5/emboss_fuzzpro.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: fuzzpro38" name="fuzzpro" version="5.0.0"><description>Protein pattern search</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>fuzzpro -sequence $input1 -outfile $out_file1 -pattern "$pattern" -pmismatch $mismatch -rformat2 $out_format1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_checktrans.xml
+++ b/tools/emboss_5/emboss_checktrans.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: checktrans9" name="checktrans" version="5.0.0"><description>Reports STOP codons and ORF statistics of a protein</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>checktrans -sequence $input1 -outfile $out_file1 -outseq $out_file2 -osformat3 $out_format2 -outfeat $out_file3 -offormat4 $out_format3 -orfml $orfml -addlast $addlast -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_cusp.xml
+++ b/tools/emboss_5/emboss_cusp.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: cusp17" name="cusp" version="5.0.0"><description>Create a codon usage table</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>cusp -sequence $input1 -outfile $out_file1 -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_extractfeat.xml
+++ b/tools/emboss_5/emboss_extractfeat.xml
@@ -1,6 +1,7 @@
<tool id="EMBOSS: extractfeat34" name="extractfeat" version="5.0.0"><!-- tool tested with documentation, functional test not designed due to empty files resulting from test input sequences --><description>Extract features from a sequence</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>extractfeat -sequence $input1 -outseq $out_file1 -before $before -after $after -source "$source" -type "$type" -sense $sense -minscore $minscore -maxscore $maxscore -tag "$tag" -value
"$value" -join $join -featinname $featinname -describe "$describe" -osformat2 $out_format1 -auto</command><inputs>
--- a/tools/emboss_5/emboss_cirdna.xml
+++ b/tools/emboss_5/emboss_cirdna.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: cirdna11" name="cirdna" version="5.0.0"><description>Draws circular maps of DNA constructs</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command interpreter="perl">emboss_single_outputfile_wrapper.pl cirdna -infile $input1 -graphout png -goutfile $out_file1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_btwisted.xml
+++ b/tools/emboss_5/emboss_btwisted.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: btwisted5" name="btwisted" version="5.0.0"><description>Calculates the twisting in a B-DNA sequence</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>btwisted -sequence $input1 -outfile $out_file1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_maskfeat.xml
+++ b/tools/emboss_5/emboss_maskfeat.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: maskfeat50" name="maskfeat" version="5.0.0"><description>Mask off features of a sequence</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>maskfeat -sequence $input1 -outseq $out_file1 -type "$type" -tolower $tolower -maskchar "$maskchar" -osformat2 $out_format1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_cpgreport.xml
+++ b/tools/emboss_5/emboss_cpgreport.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: cpgreport16" name="cpgreport" version="5.0.0"><description>Reports all CpG rich regions</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>cpgreport -sequence $input1 -outfile $out_file1 -outfeat $out_file2 -offormat3 $out_format2 -score $score -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_freak.xml
+++ b/tools/emboss_5/emboss_freak.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: freak36" name="freak" version="5.0.0"><description>Residue/base frequency table or plot</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>freak -seqall $input1 -outfile $out_file1 -window $window -letters $letters -graph png -step $step -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_dotpath.xml
+++ b/tools/emboss_5/emboss_dotpath.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: dotpath25" name="dotpath" version="5.0.0"><description>Non-overlapping wordmatch dotplot of two sequences</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command interpreter="perl">emboss_single_outputfile_wrapper.pl dotpath -asequence $input1 -bsequence $input2 -goutfile $out_file1 -wordsize $wordsize -overlaps $overlaps -boxit $boxit -graph png
-auto</command><inputs>
--- a/tools/emboss_5/emboss_iep.xml
+++ b/tools/emboss_5/emboss_iep.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: iep45" name="iep" version="5.0.0"><description>Calculates the isoelectric point of a protein</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>iep -sequence $input1 -outfile $out_file1 -step $step -amino $amino -graph png -termini $termini -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_coderet.xml
+++ b/tools/emboss_5/emboss_coderet.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: coderet13" name="coderet" version="5.0.0"><description>Extract CDS, mRNA and translations from feature tables</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><!-- <command>coderet -seqall $input1 -outfile $out_file1 -osformat2 $out_format1 -cds $cds -mrna $mrna -translation $translation -auto</command>--><command>coderet -seqall $input1 -outfile $out_file1 -auto</command><inputs>
--- a/tools/emboss_5/emboss_charge.xml
+++ b/tools/emboss_5/emboss_charge.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: charge8" name="charge" version="5.0.0"><description>Protein charge plot</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>charge -seqall $input1 -outfile $out_file1 -window $window -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_diffseq.xml
+++ b/tools/emboss_5/emboss_diffseq.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: diffseq22" name="diffseq" version="5.0.0"><description>Find differences between nearly identical sequences</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>diffseq -asequence $input1 -bsequence $input2 -outfile $out_file1 -aoutfeat $out_file2 -boutfeat $out_file3 -wordsize $wordsize -globaldifferences $globaldifferences -rformat3
$out_format1 -offormat4 $out_format2 -offormat5 $out_format3 -auto</command><inputs>
--- a/tools/emboss_5/emboss_dotmatcher.xml
+++ b/tools/emboss_5/emboss_dotmatcher.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: dotmatcher24" name="dotmatcher" version="5.0.0"><description>Displays a thresholded dotplot of two sequences</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command interpreter="perl">emboss_single_outputfile_wrapper.pl dotmatcher -asequence $input1 -bsequence $input2 -goutfile $out_file1 -windowsize $windowsize -threshold $threshold -graph png -xygraph png
-auto</command><inputs>
--- a/tools/emboss_5/emboss_garnier.xml
+++ b/tools/emboss_5/emboss_garnier.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: garnier40" name="garnier" version="5.0.0"><description>Predicts protein secondary structure</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>garnier -sequence $input1 -outfile $out_file1 -idc $idc -rformat2 $out_format1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_geecee.xml
+++ b/tools/emboss_5/emboss_geecee.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: geecee41" name="geecee" version="5.0.0"><description>Calculates fractional GC content of nucleic acid sequences</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>geecee -sequence $input1 -outfile $out_file1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_est2genome.xml
+++ b/tools/emboss_5/emboss_est2genome.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: est2genome32" name="est2genome" version="5.0.0"><description>Align EST and genomic DNA sequences</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>est2genome -estsequence $input1 -genomesequence $input2 -outfile $out_file1 -match $match -mismatch $mismatch -gappenalty $gappenalty -intronpenalty $intronpenalty -splicepenalty
$splicepenalty -minscore $minscore -reverse $reverse -splice $splice -mode $mode -best $best -shuffle $shuffle -seed $seed -align $align -width $width -auto</command><inputs>
--- a/tools/emboss_5/emboss_compseq.xml
+++ b/tools/emboss_5/emboss_compseq.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: compseq14" name="compseq" version="5.0.0"><description>Count composition of dimer/trimer/etc words in a sequence</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>compseq -sequence $input1 -outfile $out_file1 -word $word -frame $frame -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_etandem.xml
+++ b/tools/emboss_5/emboss_etandem.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: etandem33" name="etandem" version="5.0.0"><description>Looks for tandem repeats in a nucleotide sequence</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>etandem -sequence $input1 -outfile $out_file1 -origfile $ofile2 -minrepeat $minrepeat -maxrepeat $maxrepeat -threshold $threshold -mismatch $mismatch -uniform $uniform -rformat2 $out_format1 -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_marscan.xml
+++ b/tools/emboss_5/emboss_marscan.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: marscan49" name="marscan" version="5.0.0"><description>Finds MAR/SAR sites in nucleic sequences</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>marscan -sequence $input1 -outfile $out_file1 -rformat2 $out_format1 -auto</command><inputs><param format="data" name="input1" type="data">
--- a/tools/emboss_5/emboss_cai_custom.xml
+++ b/tools/emboss_5/emboss_cai_custom.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: cai_custom6" name="cai custom" version="5.0.0"><description>CAI codon adaptation index using custom codon usage file</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>cai -seqall $input1 -outfile $out_file1 -cfile $input2 -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_equicktandem.xml
+++ b/tools/emboss_5/emboss_equicktandem.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: equicktandem31" name="equicktandem" version="5.0.0"><description>Finds tandem repeats</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>equicktandem -sequence $input1 -outfile $out_file1 -origfile $ofile2 -maxrepeat $maxrepeat -threshold $threshold -rformat2 $out_format1 -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_cai.xml
+++ b/tools/emboss_5/emboss_cai.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: cai6" name="cai" version="5.0.0"><description>CAI codon adaptation index</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command>cai -seqall $input1 -outfile $out_file1 -cfile $cfile -auto</command><inputs><param format="fasta" name="input1" type="data">
--- a/tools/emboss_5/emboss_chaos.xml
+++ b/tools/emboss_5/emboss_chaos.xml
@@ -1,5 +1,6 @@
<tool id="EMBOSS: chaos7" name="chaos" version="5.0.0"><description>Create a chaos game representation plot for a sequence</description>
+ <requirements><requirement type="package" version="5.0.0">emboss</requirement></requirements><command interpreter="perl">emboss_single_outputfile_wrapper.pl chaos -sequence $input1 -graph png -goutfile $out_file1 -auto</command><inputs><param format="data" name="input1" type="data">
1
0

galaxy-dist commit 83227c6bd65c: Data libraries: Use jStore to save folder expansion state
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 Kanwei Li <kanwei(a)gmail.com>
# Date 1286474538 14400
# Node ID 83227c6bd65cdc2832e04b7823d4ee4f11d20ba7
# Parent 3316517f0980642b7706f26ab3a5b4ec461acfde
Data libraries: Use jStore to save folder expansion state
--- a/templates/library/common/browse_library.mako
+++ b/templates/library/common/browse_library.mako
@@ -45,65 +45,90 @@
<%def name="javascripts()">
${parent.javascripts()}
+ ${h.js("class", "jquery.jstore")}
${self.grid_javascripts()}
</%def><%def name="grid_javascripts()"><script type="text/javascript">
- $(function () {
- $("#library-grid").each( function() {
+ $(function() {
+ $.jStore.init("galaxy"); // Auto-select best storage
+ var storage_id = "library-expand-state-${trans.security.encode_id(library.id)}";
+
+ var restore_folder_state = function() {
+ var state = $.jStore.store(storage_id);
+ if (state) {
+ for (var id in state) {
+ if (state[id] === true) {
+ var row = $("#" + id),
+ index = row.parent().children().index(row);
+ row.addClass("expanded").show();
+ row.siblings().filter("tr[parent='" + index + "']").show();
+ }
+ }
+ }
+ };
+
+ var save_folder_state = function() {
+ var state = {};
+ $("tr.folderRow").each( function() {
+ var folder = $(this);
+ state[folder.attr("id")] = folder.hasClass("expanded");
+ });
+ $.jStore.store(storage_id, state);
+ };
+
+ $("#library-grid").each(function() {
// Recursively fill in children and descendents of each row
- var process_row = function( q, parents ) {
+ var process_row = function(q, parents) {
// Find my index
- var index = $(q).parent().children().index( $(q) );
+ var index = q.parent().children().index(q);
// Find my immediate children
- var children = $(q).siblings().filter( "[parent='" + index + "']" );
+ var children = q.siblings().filter("[parent='" + index + "']");
// Recursively handle them
var descendents = children;
children.each( function() {
- child_descendents = process_row( $(this), parents.add( q ) );
- descendents = descendents.add( child_descendents );
+ child_descendents = process_row( $(this), parents.add(q) );
+ descendents = descendents.add(child_descendents);
});
// Set up expand / hide link
- // HACK: assume descendents are invisible. The caller actually
- // ensures this for the root node. However, if we start
- // remembering folder states, we'll need something
- // more sophisticated here.
- var visible = false;
var expand_fn = function() {
- if ( visible ) {
+ if ( q.hasClass("expanded") ) {
descendents.hide();
- descendents.removeClass( "expanded" );
- q.removeClass( "expanded" );
- visible = false;
+ descendents.removeClass("expanded");
+ q.removeClass("expanded");
} else {
children.show();
- q.addClass( "expanded" );
- visible = true;
+ q.addClass("expanded");
}
+ save_folder_state();
};
$(q).find("span.expandLink").click(expand_fn);
$(q).find("span.expandLink a").click(expand_fn);
// Check/uncheck boxes in subfolders.
- q.children( "td" ).children( "input[type=checkbox]" ).click( function() {
+ q.children("td").children("input[type=checkbox]").click( function() {
if ( $(this).is(":checked") ) {
- descendents.find( "input[type=checkbox]").attr( 'checked', true );
+ descendents.find("input[type=checkbox]").attr("checked", true);
} else {
- descendents.find( "input[type=checkbox]").attr( 'checked', false );
+ descendents.find("input[type=checkbox]").attr("checked", false);
// If you uncheck a lower level checkbox, uncheck the boxes above it
// (since deselecting a child means the parent is not fully selected any
// more).
- parents.children( "td" ).children( "input[type=checkbox]" ).attr( "checked", false );
+ parents.children("td").children("input[type=checkbox]").attr("checked", false);
}
});
// return descendents for use by parent
return descendents;
}
- $(this).find( "tbody tr" ).not( "[parent]").each( function() {
+ $(this).find("tbody tr").not("[parent]").each( function() {
descendents = process_row( $(this), $([]) );
descendents.hide();
});
});
+
+ $.jStore.engineReady(function() {
+ restore_folder_state();
+ });
});
// For view info links, use a modal popup
@@ -313,7 +338,7 @@
info_association, inherited = folder.get_info_association( restrict=True )
%>
%if not root_folder and ( not folder.deleted or show_deleted ):
- <tr class="folderRow libraryOrFolderRow"
+ <tr id="folder-${trans.security.encode_id(folder.id)}" class="folderRow libraryOrFolderRow"
%if parent is not None:
parent="${parent}"
style="display: none;"
@@ -372,7 +397,7 @@
%endif
<td>
%if folder.description:
- ${folder.description}</i>
+ ${folder.description}
%endif
<td colspan="3"></td></tr>
1
0

galaxy-dist commit 5a5e35e09eab: Add default file_path to cleanup_datasets.py script. Closes #376
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 Kanwei Li <kanwei(a)gmail.com>
# Date 1286475319 14400
# Node ID 5a5e35e09eab477df04549408b56dd7d0ab024b0
# Parent 83227c6bd65cdc2832e04b7823d4ee4f11d20ba7
Add default file_path to cleanup_datasets.py script. Closes #376
--- a/scripts/cleanup_datasets/cleanup_datasets.py
+++ b/scripts/cleanup_datasets/cleanup_datasets.py
@@ -56,7 +56,7 @@ def main():
database_connection = configuration['database_connection']
else:
database_connection = "sqlite:///%s?isolation_level=IMMEDIATE" % configuration["database_file"]
- file_path = configuration['file_path']
+ file_path = configuration.get('file_path', "database/files")
app = CleanupDatasetsApplication( database_connection=database_connection, file_path=file_path )
cutoff_time = datetime.utcnow() - timedelta( days=options.days )
now = strftime( "%Y-%m-%d %H:%M:%S" )
1
0

galaxy-dist commit 269c46003857: New style for search boxes in grids. Grid items will no longer show outline when hovered upon if there are no actions to be performed.
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 Kanwei Li <kanwei(a)gmail.com>
# Date 1286317291 14400
# Node ID 269c46003857daaf034a41d1f0cd690e0b60df72
# Parent 28dd2c50c02380ed9b3e47b9b598783a0ab03e2c
New style for search boxes in grids. Grid items will no longer show outline when hovered upon if there are no actions to be performed.
--- a/static/june_2007_style/make_style.py
+++ b/static/june_2007_style/make_style.py
@@ -25,7 +25,6 @@ templates = [ ( "base.css.tmpl", "base.c
( "history.css.tmpl", "history.css" ),
( "tool_menu.css.tmpl", "tool_menu.css" ),
( "iphone.css.tmpl", "iphone.css" ),
- ( "reset.css.tmpl", "reset.css" ),
( "autocomplete_tagging.css.tmpl", "autocomplete_tagging.css" ),
( "trackster.css.tmpl", "trackster.css" ) ]
--- a/static/june_2007_style/base.css.tmpl
+++ b/static/june_2007_style/base.css.tmpl
@@ -50,6 +50,10 @@ hr {
border-bottom: dotted $base_text 1px;
}
+table {
+ border-collapse: collapse;
+}
+
th {
text-align: left;
}
--- 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=100,DATA_ERROR="There was an error in indexing this dataset. ",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),PX_PER_CHAR=CONTEXT.measureText("A").width,RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function()
{RIGHT_STRAND_INV=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,c,e,d,b){this.container=a;this.vis_id=d;this.dbkey=b;this.title=e;this.chrom=c;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.
min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{init:function(){var c=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").hide().appendTo(c);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide().appendTo(c);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);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/>").addClass("overview-viewport").appendTo(this.overview);this.overview_box_background=$("<div/>").addClass("overview-boxback").app
endTo(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(){void (0)}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.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.hi
de();a.chrom_select.hide();a.nav_input.css("display","inline-block");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(d){if(d.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=d.chrom_info;var f='<option value="">Select Chrom/Contig</option>';for(i in a.chrom_data){var e=a.chrom_data[i]["chrom"];f+='<option value="'+e+'">'+e+"</option>"}a.chrom_select.html(f);a.intro_div.show();a.content_div.hide();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.viewport_container.bind("dragstart",function(d){this.original_low=a.low;this.current_height=d.clientY;this.current_x=d.offsetX;this.active=(d.clientX<a.viewport_container.width()-16)?true:false}).bind("drag",function(g){if(!this.active){return}var d=$(this);var j=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(j/a.viewport_container.width()*(a.high-a.low));a.move_delta(h)});this.top_labeltrack.bind("dragstart",f
unction(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(j){var f=Math.min(j.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(j.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(k){var f=Math.min(k.clientX,this.drag_origin_x),d=Math.max(k.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),j=a.low;a.low=Math.round(f/g*h)+j;a.high=Math.round(d/g*h)+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))},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(d,a,f){var c=this;var e=$.grep(c.chrom_data,function(h,j){return h.chrom===d})[0];if(e===undefined){return}if(d!==c.chrom){c.chrom=d;if(c.chrom===""){c.intro_div.show();c.content_div.hide()}else{c.intro_div.hide();c.content_div.show()}c.chrom_select.val(c.chrom);c.max_high=e.len;c.reset();c.redraw(true);for(var g in c.tracks){var b=c.tracks[g];if(b.init){b.init()}}}if(a!==undefined&&f!==undefined){c.low=Math.max(a,0);c.high=Math.min(f,c.max_high)}c.overview_viewport.find("canvas").remove();c.redraw()},go_to:function(f){var k=this,b=f.split(":"),h=b[0],j=b[1];if(j!==undefined){try{var g=j.split("-"),a=parseInt(g[0].replace(/,/g,"")),d=parseInt(g[1].replace(/,/g,""))}catch(c){return false}}k.change_chrom(h,a,d)},move_delta:function(c){var a=this;var b=a.high-a.low;i
f(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},update_options:function(){this.has_changes=true;var b=$("ul#sortable-ul").sortable("toArray");for(var c in b){var e=b[c].split("_li")[0].split("track_")[1];this.viewport_container.append($("#track_"+e))}for(var d in view.tracks){var a=view.tracks[d];if(a&&a.update_options){a.update_options(d)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".ya
xislabel").remove()},redraw:function(h){var d=this.high-this.low,b=this.low,f=this.high;if(b<this.max_low){b=this.max_low}if(f>this.max_high){f=this.max_high}if(this.high!==0&&d<this.min_separation){f=b+this.min_separation}this.low=Math.floor(b);this.high=Math.ceil(f);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 e=(this.low/(this.max_high-this.max_low))*this.overview_viewport.width();var g=(this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width();this.overview_box.css({left:e,width:Math.max(12,g)}).show();if(this.overview_highlight){this.overview_highlight.css({left:e,width:g})}this.update_location(this.low,this.high);if(!h){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.
label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);this.high=Math.round(c+a);this.redraw()}});var Track=function(b,a,c){this.name=b;this.parent_element=c;this.view=a;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track");if(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);this.name_div=$("<div class='menubutton popup' />").appendTo(this.header_div);this.name_div.text(this.name)}this.content_div=$("<div class='track-content'>").appendTo(this.container_div);this.parent_e
lement.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)}els
e{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 d=this,c=d.view;if(d.hidden){return}if(d.display_modes!==undefined){if(d.mode_div===undefined){d.mode_div=$("<div class='right-float menubutton popup' />").appendTo(d.header_div);var h=d.display_modes[0];d.mode=h;d.mode_div.text(h);var a=function(j){d.mode_div.text(j);d.mode=j;d.tile_cache.clear();d.draw()};var f={};for(var e in d.display_modes){var g=d.display_modes[e];f[g]=function(j){return function(){a(j)}}(g)}make_popupmenu(d.mode_div,f)}else{d.mode_div.hide()}}var b={};b["Set track as overview"]=function(){c.overview_viewport.find("canvas").remove();d.is_overview=true;d.set_overview();for(var j in c.tracks){if(c.tracks[j]!==d){c.tracks[
j].is_overview=false}}};b["Edit configuration"]=function(){show_modal("Configure Track",d.gen_options(d.track_id),{Cancel:function(){hide_modal()},OK:function(){d.update_options(d.track_id);hide_modal()}})};b.Remove=function(){c.remove_track(d);if(c.num_tracks===0){$("#no-tracks").show()}};make_popupmenu(d.name_div,b)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var j=this.view.low,e=this.view.high,f=e-j,d=this.view.resolution;var l=$("<div style='position: relative;'></div>"),m=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(l),this.max_height=0;var a=Math.floor(j/d/DENSITY);while((a*DENSITY*d)<e){var k=this.content_div.width()+"_"+m+"_"+a;var c=this.tile_cache.get(k);if(c){var g=a*DENSITY*d;var b=(g-j)*m;if(this.left_offset){b-=this.left_offset}c.css({left:b});l.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height+"px")}else{this.delayed_draw(this,k,
j,e,a,d,l,m)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){if(!c.initial_canvas){c.initial_canvas=$(tile_element).clone();var l=tile_element.get(0).getContext("2d");var j=c.initial_canvas.get(0).getContext("2d");var k=l.getImageData(0,0,l.canvas.width,l.canvas.height);j.putImageData(k,0,0);c.set_overview()}c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)},set_overview:function(){var a=this.view;a.overview_viewport.height(a.default_overview_height);a.overview_box.height(a.default_overview_height);if(this.initial_canvas&&this.is_overview){if(!a.overview_highlight){a.overview_highlight=$("<div />").addClass("overview-highlight").appendTo(a.overview_viewport)}a.overview_viewport.append(this.initial_canvas);a.overview_highlight.height(this.initial_canvas.height());a.overview_viewp
ort.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.height_px=12;this.container_div.addClass("reference-track");this.dummy_canvas=$("<canvas></canvas>").get(0).getContext(
"2d");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.prototype,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,e=$("<canvas class='tile'></canvas>"),n=e.get(0).getContext("2d"),j=f+"_"+b;if(o>PX_PER_CHAR){if(this.data_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-this.left_offset});for(var h=0,l=m.leng
th;h<l;h++){var a=Math.round(h*o);n.fillText(m[h],a+this.left_offset,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b,a,c){this.track_type="LineTrack";this.display_modes=["Line","Filled","Intensity"];this.mode="Line";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={min_value:undefined,max_value:undefined,mode:"Line"};if(c.min_value!==undefined){this.prefs.min_value=c.min_value}if(c.max_value!==undefined){this.prefs.max_value=c.max_value}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_v
alue))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(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:"relative",top:"32px",left:"10px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+32+"px",left:"10px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},s
uccess:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(p,r,c,e){if(this.vertical_range===undefined){return}var s=r*DENSITY*p,a=DENSITY*p,b=$("<canvas class='tile'></canvas>"),v=p+"_"+r;if(this.data_cache.get(v)===undefined){this.get_data(p,r);return}var j=this.data_cache.get(v);if(j===null){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var o=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,n=this.vertical_range,t=this.total_frequency,d=this.height_px,m=this.mode;o.beginPath();if(data.length>1){var f=Math.ceil((data[1][0]-data[0][0])*e)}else{var f=10}var u,h;for(var q=0;q<data.length;q++){u=(data[q][0]-s)*e;h=data[q][1];if(m=="Intensity"){if(h===null){continue}if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/n*255);o.fillStyle="rgb("+h+","+h+","+h+")";o.fillRect
(u,0,f,this.height_px)}else{if(h===null){if(k&&m==="Filled"){o.lineTo(u,d)}k=false;continue}else{if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/n*d);if(k){o.lineTo(u,h)}else{k=true;if(m==="Filled"){o.moveTo(u,d);o.lineTo(u,h)}else{o.moveTo(u,h)}}}}}if(m==="Filled"){if(k){o.lineTo(u,d)}o.fill()}else{o.stroke()}c.append(b);return b},gen_options:function(k){var a=$("<div />").addClass("form-row");var e="track_"+k+"_minval",h=$("<label></label>").attr("for",e).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),j=$("<input></input>").attr("id",e).val(b),g="track_"+k+"_maxval",d=$("<label></label>").attr("for",g).text("Max value:"),f=(this.prefs.max_value===undefined?"":this.prefs.max_value),c=$("<input></input>").attr("id",g).val(f);return a.append(h).append(j).append(d).append(c)},update_options:function(c){var a=$("#track_"+c+"_minval").val(),b=$("#track_"+c+"_maxval").val();if(a!==this.prefs.min_value||b!==this.prefs.max_value){this.prefs.min
_value=parseFloat(a);this.prefs.max_value=parseFloat(b);this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+c+"_minval").text(this.prefs.min_value);$("#linetrack_"+c+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=2;this.summary_draw_height=20;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:"black",label_color:"black",show_counts:true};if(c.block_color!
==undefined){this.prefs.block_color=c.block_color}if(c.label_color!==undefined){this.prefs.label_color=c.label_color}if(c.show_counts!==undefined){this.prefs.show_counts=c.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b="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.view.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,h,c,r){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=r;this.s_e_by_tile[a]={}}var n=this.inc_slots[a].w_scale,z=[],l=0,b=$("<canvas></canvas>").
get(0).getContext("2d"),o=this.view.max_low;var B=[];if(this.inc_slots[a].mode!==r){delete this.inc_slots[a];this.inc_slots[a]={mode:r,w_scale:n};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var w=0,x=h.length;w<x;w++){var g=h[w],m=g[0];if(this.inc_slots[a][m]!==undefined){l=Math.max(l,this.inc_slots[a][m]);B.push(this.inc_slots[a][m])}else{z.push(w)}}for(var w=0,x=z.length;w<x;w++){var g=h[z[w]],m=g[0],s=g[1],d=g[2],q=g[3],e=Math.floor((s-o)*n),f=Math.ceil((d-o)*n);if(q!==undefined&&!c){var t=b.measureText(q).width;if(e-t<0){f+=t}else{e-=t}}var v=0;while(true){var p=true;if(this.s_e_by_tile[a][v]!==undefined){for(var u=0,A=this.s_e_by_tile[a][v].length;u<A;u++){var y=this.s_e_by_tile[a][v][u];if(f>y[0]&&e<y[1]){p=false;break}}}if(p){if(this.s_e_by_tile[a][v]===undefined){this.s_e_by_tile[a][v]=[]}this.s_e_by_tile[a][v].push([e,f]);this.inc_slots[a][m]=v;l=Math.max(l,v);break}v++}}return l},rect_or_text:function(n,o,f,m,b,d,k,e,h){n.textAlign="center";var j=Math.rou
nd(o/2);if((this.mode==="Pack"||this.mode==="Auto")&&d!==undefined&&o>PX_PER_CHAR){n.fillStyle=this.prefs.block_color;n.fillRect(k,h+1,e,9);n.fillStyle="#eee";for(var g=0,l=d.length;g<l;g++){if(b+g>=f&&b+g<=m){var a=Math.floor(Math.max(0,(b+g-f)*o));n.fillText(d[g],a+this.left_offset+j,h+9)}}}else{n.fillStyle=this.prefs.block_color;n.fillRect(k,h+4,e,3)}},draw_tile:function(aa,l,o,an){var H=l*DENSITY*aa,ag=(l+1)*DENSITY*aa,G=ag-H;var ah=(!this.initial_canvas?"initial":H+"_"+ag);var C=this.data_cache.get(ah);var d;if(C===undefined||(this.mode!=="Auto"&&C.dataset_type==="summary_tree")){this.data_queue[[H,ag]]=true;this.get_data(H,ag);return}var a=Math.ceil(G*an),O=$("<canvas class='tile'></canvas>"),ac=this.prefs.label_color,g=this.prefs.block_color,n=this.mode,r=25,Y=(n==="Squish")||(n==="Dense")&&(n!=="Pack")||(n==="Auto"&&(C.extra_info==="no_detail")),S=this.left_offset,am,v,ao;if(C.dataset_type==="summary_tree"){v=this.summary_draw_height}else{if(n==="Dense"){v=r;ao=10}el
se{ao=(Y?this.vertical_nodetail_px:this.vertical_detail_px);var s=(an<0.0001?1/view.zoom_res:an);v=this.incremental_slots(s,C.data,Y,n)*ao+r;am=this.inc_slots[s]}}O.css({position:"absolute",top:0,left:(H-this.view.low)*an-S});O.get(0).width=a+S;O.get(0).height=v;o.parent().css("height",Math.max(this.height_px,v)+"px");var D=O.get(0).getContext("2d");D.fillStyle=g;D.font=this.default_font;D.textAlign="right";if(C.dataset_type=="summary_tree"){var N,K=55,af=255-K,h=af*2/3,U=C.data,F=C.max,m=C.avg,b=Math.ceil(C.delta*an);for(var aj=0,B=U.length;aj<B;aj++){var W=Math.floor((U[aj][0]-H)*an);var V=U[aj][1];if(!V){continue}N=Math.floor(af-(V/F)*af);D.fillStyle="rgb("+N+","+N+","+N+")";D.fillRect(W+S,0,b,this.summary_draw_height);if(this.prefs.show_counts&&D.measureText(V).width<b){if(N>h){D.fillStyle="black"}else{D.fillStyle="#ddd"}D.textAlign="center";D.fillText(V,W+S+(b/2),12)}}d="Summary";o.append(O);return O}if(C.message){O.css({border:"solid red","border-width":"2px 2px 2px 0p
x"});D.fillStyle="red";D.textAlign="left";D.fillText(C.message,100+S,ao)}var al=C.data;var ai=0;for(var aj=0,B=al.length;aj<B;aj++){var P=al[aj],M=P[0],ak=P[1],X=P[2],I=P[3];if(ak<=ag&&X>=H){var Z=Math.floor(Math.max(0,(ak-H)*an)),E=Math.ceil(Math.min(a,Math.max(0,(X-H)*an))),T=(n==="Dense"?1:(1+am[M]))*ao;if(C.dataset_type==="bai"){D.fillStyle=g;if(P[4] instanceof Array){var w=Math.floor(Math.max(0,(P[4][0]-H)*an)),L=Math.ceil(Math.min(a,Math.max(0,(P[4][1]-H)*an))),u=Math.floor(Math.max(0,(P[5][0]-H)*an)),q=Math.ceil(Math.min(a,Math.max(0,(P[5][1]-H)*an)));if(P[4][1]>=H&&P[4][0]<=ag){this.rect_or_text(D,an,H,ag,P[4][0],P[4][2],w+S,L-w,T)}if(P[5][1]>=H&&P[5][0]<=ag){this.rect_or_text(D,an,H,ag,P[5][0],P[5][2],u+S,q-u,T)}if(u>L){D.fillStyle="#999";D.fillRect(L+S,T+5,u-L,1)}}else{D.fillStyle=g;this.rect_or_text(D,an,H,ag,ak,I,Z+S,E-Z,T)}if(n!=="Dense"&&!Y&&ak>H){D.fillStyle=this.prefs.label_color;if(l===0&&Z-D.measureText(I).width<0){D.textAlign="left";D.fillText(M,E+2+S,T+8)
}else{D.textAlign="right";D.fillText(M,Z-2+S,T+8)}D.fillStyle=g}}else{if(C.dataset_type==="interval_index"){if(Y){D.fillStyle=g;D.fillRect(Z+S,T+5,E-Z,1)}else{var A=P[4],R=P[5],ab=P[6],f=P[7];var z,ad,J=null,ap=null;if(R&&ab){J=Math.floor(Math.max(0,(R-H)*an));ap=Math.ceil(Math.min(a,Math.max(0,(ab-H)*an)))}if(n!=="Dense"&&I!==undefined&&ak>H){D.fillStyle=ac;if(l===0&&Z-D.measureText(I).width<0){D.textAlign="left";D.fillText(I,E+2+S,T+8)}else{D.textAlign="right";D.fillText(I,Z-2+S,T+8)}D.fillStyle=g}if(f){if(A){if(A=="+"){D.fillStyle=RIGHT_STRAND}else{if(A=="-"){D.fillStyle=LEFT_STRAND}}D.fillRect(Z+S,T,E-Z,10);D.fillStyle=g}for(var ah=0,e=f.length;ah<e;ah++){var p=f[ah],c=Math.floor(Math.max(0,(p[0]-H)*an)),Q=Math.ceil(Math.min(a,Math.max((p[1]-H)*an)));if(c>Q){continue}z=5;ad=3;D.fillRect(c+S,T+ad,Q-c,z);if(J!==undefined&&!(c>ap||Q<J)){z=9;ad=1;var ae=Math.max(c,J),t=Math.min(Q,ap);D.fillRect(ae+S,T+ad,t-ae,z)}}}else{z=9;ad=1;D.fillRect(Z+S,T+ad,E-Z,z);if(P.strand){if(P.st
rand=="+"){D.fillStyle=RIGHT_STRAND_INV}else{if(P.strand=="-"){D.fillStyle=LEFT_STRAND_INV}}D.fillRect(Z+S,T,E-Z,10);D.fillStyle=g}}}}}ai++}}o.append(O);return O},gen_options:function(j){var a=$("<div />").addClass("form-row");var e="track_"+j+"_block_color",l=$("<label />").attr("for",e).text("Block color:"),m=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),k="track_"+j+"_label_color",g=$("<label />").attr("for",k).text("Text color:"),h=$("<input />").attr("id",k).attr("name",k).val(this.prefs.label_color),f="track_"+j+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(l).append(m).append(g).append(h).append(d)},update_options:function(e){var b=$("#track_"+e+"_block_color").val(),d=$("#track_"+e+"_label_color").val(),c=$("#track_"+e+"_mode option:selected"
).val(),a=$("#track_"+e+"_show_count").attr("checked");if(b!==this.prefs.block_color||d!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=d;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,c){FeatureTrack.call(this,d,b,a,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
+var DENSITY=200,FEATURE_LEVELS=10,MAX_FEATURE_DEPTH=100,DATA_ERROR="There was an error in indexing this dataset. ",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),PX_PER_CHAR=CONTEXT.measureText("A").width,RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function()
{RIGHT_STRAND_INV=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,c,e,d,b){this.container=a;this.vis_id=d;this.dbkey=b;this.title=e;this.chrom=c;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.
min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{init:function(){var c=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").hide().appendTo(c);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide().appendTo(c);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);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/>").addClass("overview-viewport").appendTo(this.overview);this.overview_box_background=$("<div/>").addClass("overview-boxback").app
endTo(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(){void (0)}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.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.hi
de();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(d){if(d.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=d.chrom_info;var f='<option value="">Select Chrom/Contig</option>';for(i in a.chrom_data){var e=a.chrom_data[i]["chrom"];f+='<option value="'+e+'">'+e+"</option>"}a.chrom_select.html(f);a.intro_div.show();a.content_div.hide();a.chrom_select.b
ind("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.viewport_container.bind("dragstart",function(d){this.original_low=a.low;this.current_height=d.clientY;this.current_x=d.offsetX;this.active=(d.clientX<a.viewport_container.width()-16)?true:false}).bind("drag",function(g){if(!this.active){return}var d=$(this);var j=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(j/a.viewport_container.width()*(a.high-a.low));a.move_delta(h)});this.top_labeltra
ck.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(j){var f=Math.min(j.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(j.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(k){var f=Math.min(k.clientX,this.drag_origin_x),d=Math.max(k.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),j=a.low;a.low=Math.round(f/g*h)+j;a.high=Math.round(d/g*h)+j;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(this,this.top_labeltrack));thi
s.add_label_track(new LabelTrack(this,this.nav_labeltrack))},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(d,a,f){var c=this;var e=$.grep(c.chrom_data,function(h,j){return h.chrom===d})[0];if(e===undefined){return}if(d!==c.chrom){c.chrom=d;if(c.chrom===""){c.intro_div.show();c.content_div.hide()}else{c.intro_div.hide();c.content_div.show()}c.chrom_select.val(c.chrom);c.max_high=e.len;c.reset();c.redraw(true);for(var g in c.tracks){var b=c.tracks[g];if(b.init){b.init()}}}if(a!==undefined&&f!==undefined){c.low=Math.max(a,0);c.high=Math.min(f,c.max_high)}c.overview_viewport.find("canvas").remove();c.redraw()},go_to:function(f){var k=this,b=f.split(":"),h=b[0],j=b[1];if(j!==undefined){try{var g=j.split("-"),a=parseInt(g[0].replace(/,/g,"")),d=parseInt(g[1].replace(/,/g,""))}catch(c){return false}}k.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_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1;this.num_tracks+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){this.has_changes=true;a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[this.tracks.indexOf(a)];this.num_tracks-=1},update_options:function(){this.has_changes=true;var b=$("ul#sortable-ul").sortable("toArray");for(var c in b){var e=b[c].split("_li")[0].split("track_")[1];this.viewport_container.append($("#track_"+e))}for(var d in view.tracks){var a=view.tracks[d];if(a&&a.update_options){a.update_options(d)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewpor
t_container.find(".yaxislabel").remove()},redraw:function(h){var d=this.high-this.low,b=this.low,f=this.high;if(b<this.max_low){b=this.max_low}if(f>this.max_high){f=this.max_high}if(this.high!==0&&d<this.min_separation){f=b+this.min_separation}this.low=Math.floor(b);this.high=Math.ceil(f);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 e=(this.low/(this.max_high-this.max_low))*this.overview_viewport.width();var g=(this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width();this.overview_box.css({left:e,width:Math.max(12,g)}).show();if(this.overview_highlight){this.overview_highlight.css({left:e,width:g})}this.update_location(this.low,this.high);if(!h){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.
length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);this.high=Math.round(c+a);this.redraw()}});var Track=function(b,a,c){this.name=b;this.parent_element=c;this.view=a;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track");if(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);this.name_div=$("<div class='menubutton popup' />").appendTo(this.header_div);this.name_div.text(this.name)}this.content_div=$("<div class='track-content'>").appendTo(this.contain
er_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_di
v.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 d=this,c=d.view;if(d.hidden){return}if(d.display_modes!==undefined){if(d.mode_div===undefined){d.mode_div=$("<div class='right-float menubutton popup' />").appendTo(d.header_div);var h=d.display_modes[0];d.mode=h;d.mode_div.text(h);var a=function(j){d.mode_div.text(j);d.mode=j;d.tile_cache.clear();d.draw()};var f={};for(var e in d.display_modes){var g=d.display_modes[e];f[g]=function(j){return function(){a(j)}}(g)}make_popupmenu(d.mode_div,f)}else{d.mode_div.hide()}}var b={};b["Set track as overview"]=function(){c.overview_viewport.find("canvas").remove();d.is_overview=true;d.set_overview();for(var j in c.tracks){if(c.tra
cks[j]!==d){c.tracks[j].is_overview=false}}};b["Edit configuration"]=function(){var k=function(){hide_modal()};var j=function(){d.update_options(d.track_id);hide_modal()};show_modal("Configure Track",d.gen_options(d.track_id),{Cancel:k,OK:j})};b.Remove=function(){c.remove_track(d);if(c.num_tracks===0){$("#no-tracks").show()}};make_popupmenu(d.name_div,b)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var j=this.view.low,e=this.view.high,f=e-j,d=this.view.resolution;var l=$("<div style='position: relative;'></div>"),m=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(l),this.max_height=0;var a=Math.floor(j/d/DENSITY);while((a*DENSITY*d)<e){var k=this.content_div.width()+"_"+m+"_"+a;var c=this.tile_cache.get(k);if(c){var g=a*DENSITY*d;var b=(g-j)*m;if(this.left_offset){b-=this.left_offset}c.css({left:b});l.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height
+"px")}else{this.delayed_draw(this,k,j,e,a,d,l,m)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){if(!c.initial_canvas){c.initial_canvas=$(tile_element).clone();var l=tile_element.get(0).getContext("2d");var j=c.initial_canvas.get(0).getContext("2d");var k=l.getImageData(0,0,l.canvas.width,l.canvas.height);j.putImageData(k,0,0);c.set_overview()}c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)},set_overview:function(){var a=this.view;a.overview_viewport.height(a.default_overview_height);a.overview_box.height(a.default_overview_height);if(this.initial_canvas&&this.is_overview){if(!a.overview_highlight){a.overview_highlight=$("<div />").addClass("overview-highlight").appendTo(a.overview_viewport)}a.overview_viewport.append(this.initial_canvas);a.overview_highlight.height(this.init
ial_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.height_px=12;this.container_div.addClass("reference-track");this.dummy_canvas=$("<
canvas></canvas>").get(0).getContext("2d");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.prototype,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,e=$("<canvas class='tile'></canvas>"),n=e.get(0).getContext("2d"),j=f+"_"+b;if(o>PX_PER_CHAR){if(this.data_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-th
is.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o);n.fillText(m[h],a+this.left_offset,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b,a,c){this.track_type="LineTrack";this.display_modes=["Line","Filled","Intensity"];this.mode="Line";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={min_value:undefined,max_value:undefined,mode:"Line"};if(c.min_value!==undefined){this.prefs.min_value=c.min_value}if(c.max_value!==undefined){this.prefs.max_value=c.max_value}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");data=c.data;if(isNaN(parseFloat(a.prefs.min_va
lue))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(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:"relative",top:"32px",left:"10px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+32+"px",left:"10px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_
id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(p,r,c,e){if(this.vertical_range===undefined){return}var s=r*DENSITY*p,a=DENSITY*p,b=$("<canvas class='tile'></canvas>"),v=p+"_"+r;if(this.data_cache.get(v)===undefined){this.get_data(p,r);return}var j=this.data_cache.get(v);if(j===null){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var o=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,n=this.vertical_range,t=this.total_frequency,d=this.height_px,m=this.mode;o.beginPath();if(data.length>1){var f=Math.ceil((data[1][0]-data[0][0])*e)}else{var f=10}var u,h;for(var q=0;q<data.length;q++){u=(data[q][0]-s)*e;h=data[q][1];if(m=="Intensity"){if(h===null){continue}if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/n*255);o.fillStyl
e="rgb("+h+","+h+","+h+")";o.fillRect(u,0,f,this.height_px)}else{if(h===null){if(k&&m==="Filled"){o.lineTo(u,d)}k=false;continue}else{if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/n*d);if(k){o.lineTo(u,h)}else{k=true;if(m==="Filled"){o.moveTo(u,d);o.lineTo(u,h)}else{o.moveTo(u,h)}}}}}if(m==="Filled"){if(k){o.lineTo(u,d)}o.fill()}else{o.stroke()}c.append(b);return b},gen_options:function(k){var a=$("<div />").addClass("form-row");var e="track_"+k+"_minval",h=$("<label></label>").attr("for",e).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),j=$("<input></input>").attr("id",e).val(b),g="track_"+k+"_maxval",d=$("<label></label>").attr("for",g).text("Max value:"),f=(this.prefs.max_value===undefined?"":this.prefs.max_value),c=$("<input></input>").attr("id",g).val(f);return a.append(h).append(j).append(d).append(c)},update_options:function(c){var a=$("#track_"+c+"_minval").val(),b=$("#track_"+c+"_maxval").val();if(a!==this.prefs.min_value||b!=
=this.prefs.max_value){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(b);this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+c+"_minval").text(this.prefs.min_value);$("#linetrack_"+c+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=2;this.summary_draw_height=20;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:"black",label_color:"black
",show_counts:true};if(c.block_color!==undefined){this.prefs.block_color=c.block_color}if(c.label_color!==undefined){this.prefs.label_color=c.label_color}if(c.show_counts!==undefined){this.prefs.show_counts=c.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b="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.view.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,h,c,r){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=r;this.s_e_by_tile[a]={}}var n=this.inc_slots[a].w_sca
le,z=[],l=0,b=$("<canvas></canvas>").get(0).getContext("2d"),o=this.view.max_low;var B=[];if(this.inc_slots[a].mode!==r){delete this.inc_slots[a];this.inc_slots[a]={mode:r,w_scale:n};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var w=0,x=h.length;w<x;w++){var g=h[w],m=g[0];if(this.inc_slots[a][m]!==undefined){l=Math.max(l,this.inc_slots[a][m]);B.push(this.inc_slots[a][m])}else{z.push(w)}}for(var w=0,x=z.length;w<x;w++){var g=h[z[w]],m=g[0],s=g[1],d=g[2],q=g[3],e=Math.floor((s-o)*n),f=Math.ceil((d-o)*n);if(q!==undefined&&!c){var t=b.measureText(q).width;if(e-t<0){f+=t}else{e-=t}}var v=0;while(true){var p=true;if(this.s_e_by_tile[a][v]!==undefined){for(var u=0,A=this.s_e_by_tile[a][v].length;u<A;u++){var y=this.s_e_by_tile[a][v][u];if(f>y[0]&&e<y[1]){p=false;break}}}if(p){if(this.s_e_by_tile[a][v]===undefined){this.s_e_by_tile[a][v]=[]}this.s_e_by_tile[a][v].push([e,f]);this.inc_slots[a][m]=v;l=Math.max(l,v);break}v++}}return l},rect_or_text:function(n,o,f,m,b,d,k,e,h
){n.textAlign="center";var j=Math.round(o/2);if((this.mode==="Pack"||this.mode==="Auto")&&d!==undefined&&o>PX_PER_CHAR){n.fillStyle=this.prefs.block_color;n.fillRect(k,h+1,e,9);n.fillStyle="#eee";for(var g=0,l=d.length;g<l;g++){if(b+g>=f&&b+g<=m){var a=Math.floor(Math.max(0,(b+g-f)*o));n.fillText(d[g],a+this.left_offset+j,h+9)}}}else{n.fillStyle=this.prefs.block_color;n.fillRect(k,h+4,e,3)}},draw_tile:function(aa,l,o,an){var H=l*DENSITY*aa,ag=(l+1)*DENSITY*aa,G=ag-H;var ah=(!this.initial_canvas?"initial":H+"_"+ag);var C=this.data_cache.get(ah);var d;if(C===undefined||(this.mode!=="Auto"&&C.dataset_type==="summary_tree")){this.data_queue[[H,ag]]=true;this.get_data(H,ag);return}var a=Math.ceil(G*an),O=$("<canvas class='tile'></canvas>"),ac=this.prefs.label_color,g=this.prefs.block_color,n=this.mode,r=25,Y=(n==="Squish")||(n==="Dense")&&(n!=="Pack")||(n==="Auto"&&(C.extra_info==="no_detail")),S=this.left_offset,am,v,ao;if(C.dataset_type==="summary_tree"){v=this.summary_draw_hei
ght}else{if(n==="Dense"){v=r;ao=10}else{ao=(Y?this.vertical_nodetail_px:this.vertical_detail_px);var s=(an<0.0001?1/view.zoom_res:an);v=this.incremental_slots(s,C.data,Y,n)*ao+r;am=this.inc_slots[s]}}O.css({position:"absolute",top:0,left:(H-this.view.low)*an-S});O.get(0).width=a+S;O.get(0).height=v;o.parent().css("height",Math.max(this.height_px,v)+"px");var D=O.get(0).getContext("2d");D.fillStyle=g;D.font=this.default_font;D.textAlign="right";if(C.dataset_type=="summary_tree"){var N,K=55,af=255-K,h=af*2/3,U=C.data,F=C.max,m=C.avg,b=Math.ceil(C.delta*an);for(var aj=0,B=U.length;aj<B;aj++){var W=Math.floor((U[aj][0]-H)*an);var V=U[aj][1];if(!V){continue}N=Math.floor(af-(V/F)*af);D.fillStyle="rgb("+N+","+N+","+N+")";D.fillRect(W+S,0,b,this.summary_draw_height);if(this.prefs.show_counts&&D.measureText(V).width<b){if(N>h){D.fillStyle="black"}else{D.fillStyle="#ddd"}D.textAlign="center";D.fillText(V,W+S+(b/2),12)}}d="Summary";o.append(O);return O}if(C.message){O.css({border:"soli
d red","border-width":"2px 2px 2px 0px"});D.fillStyle="red";D.textAlign="left";D.fillText(C.message,100+S,ao)}var al=C.data;var ai=0;for(var aj=0,B=al.length;aj<B;aj++){var P=al[aj],M=P[0],ak=P[1],X=P[2],I=P[3];if(ak<=ag&&X>=H){var Z=Math.floor(Math.max(0,(ak-H)*an)),E=Math.ceil(Math.min(a,Math.max(0,(X-H)*an))),T=(n==="Dense"?1:(1+am[M]))*ao;if(C.dataset_type==="bai"){D.fillStyle=g;if(P[4] instanceof Array){var w=Math.floor(Math.max(0,(P[4][0]-H)*an)),L=Math.ceil(Math.min(a,Math.max(0,(P[4][1]-H)*an))),u=Math.floor(Math.max(0,(P[5][0]-H)*an)),q=Math.ceil(Math.min(a,Math.max(0,(P[5][1]-H)*an)));if(P[4][1]>=H&&P[4][0]<=ag){this.rect_or_text(D,an,H,ag,P[4][0],P[4][2],w+S,L-w,T)}if(P[5][1]>=H&&P[5][0]<=ag){this.rect_or_text(D,an,H,ag,P[5][0],P[5][2],u+S,q-u,T)}if(u>L){D.fillStyle="#999";D.fillRect(L+S,T+5,u-L,1)}}else{D.fillStyle=g;this.rect_or_text(D,an,H,ag,ak,I,Z+S,E-Z,T)}if(n!=="Dense"&&!Y&&ak>H){D.fillStyle=this.prefs.label_color;if(l===0&&Z-D.measureText(I).width<0){D.tex
tAlign="left";D.fillText(M,E+2+S,T+8)}else{D.textAlign="right";D.fillText(M,Z-2+S,T+8)}D.fillStyle=g}}else{if(C.dataset_type==="interval_index"){if(Y){D.fillStyle=g;D.fillRect(Z+S,T+5,E-Z,1)}else{var A=P[4],R=P[5],ab=P[6],f=P[7];var z,ad,J=null,ap=null;if(R&&ab){J=Math.floor(Math.max(0,(R-H)*an));ap=Math.ceil(Math.min(a,Math.max(0,(ab-H)*an)))}if(n!=="Dense"&&I!==undefined&&ak>H){D.fillStyle=ac;if(l===0&&Z-D.measureText(I).width<0){D.textAlign="left";D.fillText(I,E+2+S,T+8)}else{D.textAlign="right";D.fillText(I,Z-2+S,T+8)}D.fillStyle=g}if(f){if(A){if(A=="+"){D.fillStyle=RIGHT_STRAND}else{if(A=="-"){D.fillStyle=LEFT_STRAND}}D.fillRect(Z+S,T,E-Z,10);D.fillStyle=g}for(var ah=0,e=f.length;ah<e;ah++){var p=f[ah],c=Math.floor(Math.max(0,(p[0]-H)*an)),Q=Math.ceil(Math.min(a,Math.max((p[1]-H)*an)));if(c>Q){continue}z=5;ad=3;D.fillRect(c+S,T+ad,Q-c,z);if(J!==undefined&&!(c>ap||Q<J)){z=9;ad=1;var ae=Math.max(c,J),t=Math.min(Q,ap);D.fillRect(ae+S,T+ad,t-ae,z)}}}else{z=9;ad=1;D.fillRect
(Z+S,T+ad,E-Z,z);if(P.strand){if(P.strand=="+"){D.fillStyle=RIGHT_STRAND_INV}else{if(P.strand=="-"){D.fillStyle=LEFT_STRAND_INV}}D.fillRect(Z+S,T,E-Z,10);D.fillStyle=g}}}}}ai++}}o.append(O);return O},gen_options:function(j){var a=$("<div />").addClass("form-row");var e="track_"+j+"_block_color",l=$("<label />").attr("for",e).text("Block color:"),m=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),k="track_"+j+"_label_color",g=$("<label />").attr("for",k).text("Text color:"),h=$("<input />").attr("id",k).attr("name",k).val(this.prefs.label_color),f="track_"+j+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(l).append(m).append(g).append(h).append(d)},update_options:function(e){var b=$("#track_"+e+"_block_color").val(),d=$("#track_"+e+"_label_color").val(),c=
$("#track_"+e+"_mode option:selected").val(),a=$("#track_"+e+"_show_count").attr("checked");if(b!==this.prefs.block_color||d!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=d;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,c){FeatureTrack.call(this,d,b,a,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -131,6 +131,7 @@ var View = function( container, chrom, t
view.location_span.hide();
view.chrom_select.hide();
view.nav_input.css("display", "inline-block");
+ view.nav_input.select();
view.nav_input.focus();
});
if (this.vis_id !== undefined) {
@@ -539,9 +540,12 @@ var TiledTrack = function() {
}
};
track_dropdown["Edit configuration"] = function() {
+ var cancel_fn = function() { hide_modal(); };
+ var ok_fn = function() { track.update_options(track.track_id); hide_modal(); };
+
show_modal("Configure Track", track.gen_options(track.track_id), {
- "Cancel": function() { hide_modal(); },
- "OK": function() { track.update_options(track.track_id); hide_modal(); }
+ "Cancel": cancel_fn,
+ "OK": ok_fn
});
};
track_dropdown["Remove"] = function() {
Binary file static/images/fugue/magnifier-left.png has changed
--- a/static/june_2007_style/blue/reset.css
+++ /dev/null
@@ -1,4 +0,0 @@
-body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}
-select,input,button,textarea{font:99% arial,helvetica,clean,sans-serif;}
-table{font-size:inherit;font:100%;}
-pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}
--- a/templates/grid_common.mako
+++ b/templates/grid_common.mako
@@ -14,7 +14,7 @@
%if column.filterable == "advanced":
<td align="left" style="padding-left: 10px">${column_label}:</td>
%endif
- <td>
+ <td style="padding: 0;">
%if isinstance(column, TextColumn):
<form class="text-filter-form" column_key="${column.key}" action="${url( dict() )}" method="get" >
## Carry forward filtering criteria with hidden inputs.
@@ -40,7 +40,7 @@
<span class='text-filter-val'>
${cur_filter_dict[column.key]}
<% filter_all = GridColumnFilter( "", { column.key : "All" } ) %>
- <a href="${url( filter_all.get_url_args() )}"><img src="${h.url_for('/static/images/delete_tag_icon_gray.png')}"/></a>
+ <a href="${url( filter_all.get_url_args() )}"><span class="delete-search-icon" /></a></span>
%endif
%elif isinstance( column_filter, list ):
@@ -54,17 +54,17 @@
del new_filter[ i ]
new_column_filter = GridColumnFilter( "", { column.key : h.to_json_string( new_filter ) } )
%>
- <a href="${url( new_column_filter.get_url_args() )}"><img src="${h.url_for('/static/images/delete_tag_icon_gray.png')}"/></a>
+ <a href="${url( new_column_filter.get_url_args() )}"><span class="delete-search-icon" /></a></span>
%endfor
%endif
%endif
</span>
## Print input field for column.
- <span>
+ <span class="search-box"><% value = iff( column.filterable == "standard", column.label.lower(), "") %>
- <input class="no-padding-or-margin" id="input-${column.key}-filter" name="f-${column.key}" type="text" value="${value}" size="15"/>
- <input class='submit-image' type='image' src='${h.url_for('/static/images/mag_glass.png')}' alt='Filter'/>
+ <input class="search-box-input" id="input-${column.key}-filter" name="f-${column.key}" type="text" value="${value}" size="15"/>
+ <button class="submit-image" type="submit" title='Search'/></span></form>
%else:
@@ -98,7 +98,7 @@
## Standard search.
<div><table><tr>
- <td>
+ <td style="padding: 0;"><table>
%for column in grid.columns:
%if column.filterable == "standard":
@@ -124,7 +124,7 @@
%>
%if show_advanced_search_link:
<% args = { "advanced-search" : True } %>
- | <a href="${url( args )}" class="advanced-search-toggle">Advanced Search</a>
+ <a style="margin-left: 10px;" href="${url( args )}" class="advanced-search-toggle">Advanced Search</a>
%endif
</td></tr></table>
@@ -144,12 +144,11 @@
cur_filter_dict[column.key] != default_filter_dict[column.key]:
advanced_search_display = "block"
%>
- <div id="more-search-options" style="display: ${advanced_search_display}; padding-top: 5px">
- <table style="border: 1px solid gray;">
+ <div id="more-search-options" style="display: ${advanced_search_display}; margin-top: 5px; border: 1px solid #ccc;">
+ <table><tr><td style="text-align: left" colspan="100">
- Advanced Search |
<% args = { "advanced-search" : False } %>
- <a href="${url( args )}" class="advanced-search-toggle">Close</a>
+ <a href="${url( args )}" class="advanced-search-toggle">Close Advanced Search</a>
## Link to clear all filters.
##|
##<%
--- a/static/june_2007_style/blue/base.css
+++ b/static/june_2007_style/blue/base.css
@@ -11,6 +11,7 @@ a:link,a:visited,a:active{color:#303030;
h1,h2,h3,h4{color:#023858;}
h1:first-child,h2:first-child,h3:first-child,h4:first-child{margin-top:0px;}
hr{border:none;height:0px;border-bottom:dotted #303030 1px;}
+table{border-collapse:collapse;}
th{text-align:left;}
div.toolForm{border:solid #d8b365 1px;}
div.toolFormTitle{font-weight:bold;padding:5px;padding-left:10px;padding-right:10px;background:#ebd9b2;background-repeat:repeat-x;background-position:top;border-bottom:solid #d8b365 1px;}
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -304,8 +304,7 @@
}
// Add button that displays filter and provides a button to delete it.
- var t = $("<span>" + value +
- " <a href='#'><img src='${h.url_for('/static/images/delete_tag_icon_gray.png')}'/></a></span>");
+ var t = $("<span>" + value + "<a href='#'><img class='delete-search-icon' /></a></span>");
t.addClass('text-filter-val');
t.click(function() {
// Remove filter condition.
@@ -614,50 +613,78 @@
${parent.stylesheets()}
${h.css( "autocomplete_tagging", "jquery.rating" )}
<style>
- ## Not generic to all grids -- move to base?
- .count-box {
- min-width: 1.1em;
- padding: 5px;
- border-width: 1px;
- border-style: solid;
- text-align: center;
- display: inline-block;
- }
- .text-filter-val {
- border: solid 1px #AAAAAA;
- padding: 1px 3px 1px 3px;
- margin-right: 5px;
- -moz-border-radius: .5em;
- -webkit-border-radius: .5em;
- font-style: italic;
- }
- .page-link a, .inactive-link {
- padding: 0px 7px 0px 7px;
- }
- .inactive-link, .current-filter {
- font-style: italic;
- }
- .submit-image {
- vertical-align: text-bottom;
- margin: 0;
- padding: 0;
- }
- .no-padding-or-margin {
- margin: 0;
- padding: 0;
- }
- .gray-background {
- background-color: #DDDDDD;
- }
- .loading-elt-overlay {
- background-color : white;
- opacity : 0.5;
- width : 100%;
- height : 100%;
- z-index : 14000;
- position : absolute;
- display: none;
- }
+ .count-box {
+ min-width: 1.1em;
+ padding: 5px;
+ border-width: 1px;
+ border-style: solid;
+ text-align: center;
+ display: inline-block;
+ }
+ .text-filter-val {
+ border: solid 1px #AAAAAA;
+ padding: 1px 3px 1px 3px;
+ margin-right: 5px;
+ -moz-border-radius: .5em;
+ -webkit-border-radius: .5em;
+ font-style: italic;
+
+ }
+ .page-link a, .inactive-link {
+ padding: 0px 7px 0px 7px;
+ }
+ .inactive-link, .current-filter {
+ font-style: italic;
+ }
+ .submit-image {
+ background: url(${h.url_for('/static/images/fugue/magnifier-left.png')}) no-repeat right transparent;
+ background-color: #eee;
+ width: 20px;
+ height: 20px;
+ cursor: pointer;
+ border: 0;
+ border-left: 1px solid #ccc;
+ margin: 0;
+ padding: 0;
+ display: block;
+ float: right;
+ }
+ #more-search-options td {
+ padding: 3px;
+ }
+ .delete-search-icon {
+ background: url(${h.url_for("/static/images/delete_tag_icon_gray.png")}) center no-repeat;
+ display: inline-block;
+ width: 10px;
+ cursor: pointer;
+ height: 16px;
+ vertical-align: middle;
+ }
+ .search-box-input {
+ border: 0;
+ margin: 0;
+ margin-left: 2px;
+ padding: 0;
+ float: left;
+ }
+ .search-box {
+ vertical-align: bottom;
+ display: inline-block;
+ padding: 0;
+ border: 1px solid #aaa;
+ }
+ .gray-background {
+ background-color: #DDDDDD;
+ }
+ .loading-elt-overlay {
+ background-color : white;
+ opacity : 0.5;
+ width : 100%;
+ height : 100%;
+ z-index : 14000;
+ position : absolute;
+ display: none;
+ }
</style></%def>
@@ -824,7 +851,9 @@
cls = "menubutton split"
%>
- %if href:
+ %if len(grid.operations) == 0:
+ <td><a class="label" href="${href}">${v}</a></td>
+ %elif href:
<td><div id="${id}" class="${cls}" style="float: left;"><a class="label" href="${href}">${v}</a></div></td>
%else:
<td><div id="${id}" class="${cls}"><label for="${encoded_id}">${v}</label></div></td>
--- a/templates/library/common/common.mako
+++ b/templates/library/common/common.mako
@@ -472,7 +472,7 @@
<%def name="render_actions_on_multiple_items()"><tfoot><tr>
- <td colspan="4" style="padding-left: 42px;">
+ <td colspan="5" style="padding-left: 42px;">
For selected items:
<select name="do_action" id="action_on_selected_items">
%if ( trans.user_is_admin() and cntrller=='library_admin' ):
--- a/static/june_2007_style/reset.css.tmpl
+++ /dev/null
@@ -1,5 +0,0 @@
-/* Copyright (c) 2008, Yahoo! Inc. All rights reserved. */
-## yui fonts
-body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}select,input,button,textarea{font:99% arial,helvetica,clean,sans-serif;}table{font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}
-## yui reset... commented out???
-/*body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}ol,ul {list-style:none;}caption,th {text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;}*/
1
0

galaxy-dist commit 75a3e2a75cda: Add support for displaying BAM files at Ensembl
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 chapmanb
# Date 1286377408 14400
# Node ID 75a3e2a75cdaf8872ef3cafd745524dab2872962
# Parent a89b59ecb9e06778ee39f0b1b2aed38613c127b6
Add support for displaying BAM files at Ensembl
--- a/tool-data/shared/ensembl/ensembl_sites.txt
+++ b/tool-data/shared/ensembl/ensembl_sites.txt
@@ -2,3 +2,4 @@
#http://www.ensembl.org/info/docs/webcode/linking.html
ensembl_Current Current http://www.ensembl.org/ hg19,felCat3,galGal3,bosTau4,canFam2,loxAfr3,cavPor3,equCab2,anoCar1,oryLat2,mm9,monDom5,ponAbe2,susScr2,ornAna1,oryCun2,rn4,rheMac2,gasAcu1,tetNig2,xenTro2,taeGut1,danRer5,ci2,dm3,ce6,sacCer2 Homo_sapiens,Felis_catus,Gallus_gallus,Bos_taurus,Canis_familiaris,Loxodonta_africana,Cavia_porcellus,Equus_caballus,Anolis_carolinensis,Oryzias_latipes,Mus_musculus,Monodelphis_domestica,Pongo_pygmaeus,Sus_scrofa,Ornithorhynchus_anatinus,Oryctolagus_cuniculus,Rattus_norvegicus,Macaca_mulatta,Gasterosteus_aculeatus,Tetraodon_nigroviridis,Xenopus_tropicalis,Taeniopygia_guttata,Danio_rerio,Ciona_intestinalis,Drosophila_melanogaster,Caenorhabditis_elegans,Saccharomyces_cerevisiae
ensembl_May_2009 May 2009 http://may2009.archive.ensembl.org/ hg18 Homo_sapiens
+ensembl_plants Plants http://plants.ensembl.org/ araTha_tair9 Arabidopsis_thaliana
--- a/datatypes_conf.xml.sample
+++ b/datatypes_conf.xml.sample
@@ -8,6 +8,7 @@
<converter file="bam_to_bai.xml" target_datatype="bai"/><converter file="bam_to_summary_tree_converter.xml" target_datatype="summary_tree" depends_on="bai"/><display file="ucsc/bam.xml" />
+ <display file="ensembl/ensembl_bam.xml" /></datatype><datatype extension="bed" type="galaxy.datatypes.interval:Bed" display_in_upload="true"><converter file="bed_to_gff_converter.xml" target_datatype="gff"/>
--- /dev/null
+++ b/display_applications/ensembl/ensembl_bam.xml
@@ -0,0 +1,25 @@
+<display id="ensembl_bam" version="1.0.0" name="display at Ensembl">
+ <!-- Current Ensembl method of attaching user data via URL; archives older than ~November 2008 will use a different method -->
+ <!-- Load links from file: one line to one link -->
+ <dynamic_links from_file="tool-data/shared/ensembl/ensembl_sites.txt" skip_startswith="#" id="0" name="1">
+
+ <!-- Define parameters by column from file, allow splitting on builds -->
+ <dynamic_param name="site_id" value="0"/>
+ <dynamic_param name="site_name" value="1"/>
+ <dynamic_param name="site_link" value="2"/>
+ <dynamic_param name="site_dbkeys" value="3" split="True" separator="," />
+ <dynamic_param name="site_organisms" value="4" split="True" separator="," />
+
+ <!-- Filter out some of the links based upon matching site_dbkeys to dataset dbkey -->
+ <filter>${dataset.dbkey in $site_dbkeys}</filter>
+
+ <!-- We define url and params as normal, but values defined in dynamic_param are available by specified name -->
+ <url>${site_link}${site_organism}/Location/View?contigviewbottom=bam:${bam_file.qp}=normal</url>
+
+ <param type="data" name="bam_file" url="galaxy_${DATASET_HASH}.bam" strip_https="True" />
+ <param type="data" name="bai_file" url="galaxy_${DATASET_HASH}.bam.bai" metadata="bam_index" strip_https="True" />
+ <param type="template" name="site_organism" strip="True" >
+ $site_organisms[ $site_dbkeys.index( $bam_file.dbkey ) ]
+ </param>
+ </dynamic_links>
+</display>
1
0

galaxy-dist commit 9cada14f8765: Make VCF (variant call format) a Galaxy datatype and enable very basic VCF support in trackster. VCF datatype is sniffable and can be converted to summary tree and interval index. In trackster, VCF files are represented as single-base pair feature tracks.
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 jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1286396635 14400
# Node ID 9cada14f87657b951ccc9a76af5de03263ba72d9
# Parent 75a3e2a75cdaf8872ef3cafd745524dab2872962
Make VCF (variant call format) a Galaxy datatype and enable very basic VCF support in trackster. VCF datatype is sniffable and can be converted to summary tree and interval index. In trackster, VCF files are represented as single-base pair feature tracks.
--- a/lib/galaxy/datatypes/tabular.py
+++ b/lib/galaxy/datatypes/tabular.py
@@ -11,6 +11,7 @@ from galaxy import util
from cgi import escape
from galaxy.datatypes import metadata
from galaxy.datatypes.metadata import MetadataElement
+import galaxy_utils.sequence.vcf
from sniff import *
log = logging.getLogger(__name__)
@@ -459,3 +460,18 @@ class ElandMulti( Tabular ):
def sniff( self, filename ):
return False
+
+class Vcf( Tabular ):
+ file_ext = 'vcf'
+
+ def sniff( self, filename ):
+ try:
+ # If reader can read and parse file, it's VCF.
+ for line in list( galaxy_utils.sequence.vcf.Reader( open( filename ) ) ):
+ pass
+ return True
+ except:
+ return False
+
+ def get_track_type( self ):
+ return "FeatureTrack", {"data": "interval_index", "index": "summary_tree"}
--- a/lib/galaxy/visualization/tracks/data/summary_tree.py
+++ b/lib/galaxy/visualization/tracks/data/summary_tree.py
@@ -19,6 +19,10 @@ class SummaryTreeDataProvider( object ):
if st is None:
st = summary_tree_from_file( self.dataset.file_name )
CACHE[filename] = st
+
+ # If chrom is not found in blocks, try removing the first three
+ # characters (e.g. 'chr') and see if that works. This enables the
+ # provider to handle chrome names defined as chrXXX and as XXX.
if chrom in st.chrom_blocks:
pass
elif chrom[3:] in st.chrom_blocks:
--- a/lib/galaxy/visualization/tracks/data/interval_index.py
+++ b/lib/galaxy/visualization/tracks/data/interval_index.py
@@ -8,6 +8,7 @@ Payload format: [ uid (offset), start, e
import pkg_resources; pkg_resources.require( "bx-python" )
from bx.interval_index_file import Indexes
from galaxy.datatypes.interval import Bed, Gff
+from galaxy.datatypes.tabular import Vcf
MAX_VALS = 5000 # only display first MAX_VALS features
@@ -18,13 +19,19 @@ class IntervalIndexDataProvider( object
def get_data( self, chrom, start, end, **kwargs ):
start, end = int(start), int(end)
- chrom = str(chrom)
source = open( self.original_dataset.file_name )
index = Indexes( self.converted_dataset.file_name )
results = []
count = 0
message = None
+ # If chrom is not found in indexes, try removing the first three
+ # characters (e.g. 'chr') and see if that works. This enables the
+ # provider to handle chrome names defined as chrXXX and as XXX.
+ chrom = str(chrom)
+ if chrom not in index.indexes and chrom[3:] in index.indexes:
+ chrom = chrom[3:]
+
for start, end, offset in index.find(chrom, start, end):
if count >= MAX_VALS:
message = "Only the first %s features are being displayed." % MAX_VALS
@@ -34,6 +41,7 @@ class IntervalIndexDataProvider( object
feature = source.readline().split()
payload = [ offset, start, end ]
# TODO: can we use column metadata to fill out payload?
+ # TODO: use function to set payload data
if "no_detail" not in kwargs:
length = len(feature)
if isinstance( self.original_dataset.datatype, Gff ):
@@ -58,7 +66,10 @@ class IntervalIndexDataProvider( object
block_starts = [ int(n) for n in feature[11].split(',') if n != '' ]
blocks = zip(block_sizes, block_starts)
payload.append( [ (start + block[1], start + block[1] + block[0]) for block in blocks] )
-
+ elif isinstance( self.original_dataset.datatype, Vcf ):
+ # VCF dataset.
+ payload.append( feature[2] ) # name
+
results.append(payload)
return { 'data': results, 'message': message }
--- a/datatypes_conf.xml.sample
+++ b/datatypes_conf.xml.sample
@@ -111,6 +111,10 @@
<datatype extension="tabular" type="galaxy.datatypes.tabular:Tabular" display_in_upload="true"/><datatype extension="txt" type="galaxy.datatypes.data:Text" display_in_upload="true"/><datatype extension="blastxml" type="galaxy.datatypes.xml:BlastXml" display_in_upload="true"/>
+ <datatype extension="vcf" type="galaxy.datatypes.tabular:Vcf" display_in_upload="true">
+ <converter file="vcf_to_interval_index_converter.xml" target_datatype="interval_index"/>
+ <converter file="vcf_to_summary_tree_converter.xml" target_datatype="summary_tree"/>
+ </datatype><datatype extension="velvet" type="galaxy.datatypes.assembly:Velvet" display_in_upload="false"/><datatype extension="wig" type="galaxy.datatypes.interval:Wiggle" display_in_upload="true"><converter file="wiggle_to_array_tree_converter.xml" target_datatype="array_tree"/>
@@ -285,6 +289,7 @@
<sniffer type="galaxy.datatypes.tabular:Pileup"/><sniffer type="galaxy.datatypes.interval:Interval"/><sniffer type="galaxy.datatypes.tabular:Sam"/>
+ <sniffer type="galaxy.datatypes.tabular:Vcf"/><!--
Keep this commented until the sniff method in the assembly.py
module is fixed to not read the entire file.
--- a/lib/galaxy/web/controllers/tracks.py
+++ b/lib/galaxy/web/controllers/tracks.py
@@ -9,7 +9,7 @@ is redirected to the browser interface,
"""
-import math, re, logging, glob, pkg_resources
+import re, pkg_resources
pkg_resources.require( "bx-python" )
from bx.seq.twobit import TwoBitFile
@@ -17,7 +17,7 @@ from galaxy import model
from galaxy.util.json import to_json_string, from_json_string
from galaxy.web.base.controller import *
from galaxy.web.framework import simplejson
-from galaxy.web.framework.helpers import time_ago, grids
+from galaxy.web.framework.helpers import grids
from galaxy.util.bunch import Bunch
from galaxy.visualization.tracks.data.array_tree import ArrayTreeDataProvider
--- /dev/null
+++ b/lib/galaxy/datatypes/converters/vcf_to_interval_index_converter.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+
+"""
+Convert from VCF file to interval index file.
+"""
+
+from __future__ import division
+
+import optparse
+from galaxy import eggs
+import pkg_resources; pkg_resources.require( "bx-python" )
+import galaxy_utils.sequence.vcf
+from bx.interval_index_file import Indexes
+
+def main():
+ # Read options, args.
+ parser = optparse.OptionParser()
+ (options, args) = parser.parse_args()
+ in_file, out_file = args
+
+ # Do conversion.
+ index = Indexes()
+ reader = galaxy_utils.sequence.vcf.Reader( open( in_file ) )
+ offset = reader.metadata_len
+ for vcf_line in reader:
+ # VCF format provides a chrom and 1-based position for each variant.
+ # IntervalIndex expects 0-based coordinates.
+ index.add( vcf_line.chrom, vcf_line.pos-1, vcf_line.pos, offset )
+ offset += len( vcf_line.raw_line )
+
+ index.write( open( out_file, "w" ) )
+
+if __name__ == "__main__":
+ main()
+
--- /dev/null
+++ b/lib/galaxy/datatypes/converters/vcf_to_summary_tree_converter.xml
@@ -0,0 +1,14 @@
+<tool id="CONVERTER_vcf_to_summary_tree_0" name="Convert VCF to Summary Tree" version="1.0.0" hidden="true">
+ <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description>
+ <command interpreter="python">vcf_to_summary_tree_converter.py $input1 $output1</command>
+ <inputs>
+ <page>
+ <param format="vcf" name="input1" type="data" label="Choose VCF file"/>
+ </page>
+ </inputs>
+ <outputs>
+ <data format="summary_tree" name="output1"/>
+ </outputs>
+ <help>
+ </help>
+</tool>
--- /dev/null
+++ b/lib/galaxy/datatypes/converters/vcf_to_summary_tree_converter.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+
+"""
+Convert from VCF file to summary tree file.
+
+usage: %prog in_file out_file
+"""
+from __future__ import division
+
+import optparse
+import galaxy_utils.sequence.vcf
+from galaxy.visualization.tracks.summary import SummaryTree
+
+def main():
+ # Read options, args.
+ parser = optparse.OptionParser()
+ (options, args) = parser.parse_args()
+ in_file, out_file = args
+
+ # Do conversion.
+ st = SummaryTree(block_size=25, levels=6, draw_cutoff=150, detail_cutoff=30)
+ for line in list( galaxy_utils.sequence.vcf.Reader( open( in_file ) ) ):
+ # VCF format provides a chrom and 1-based position for each variant.
+ # SummaryTree expects 0-based coordinates.
+ st.insert_range( line.chrom, long( line.pos-1 ), long( line.pos ) )
+
+ st.write(out_file)
+
+if __name__ == "__main__":
+ main()
--- /dev/null
+++ b/lib/galaxy/datatypes/converters/vcf_to_interval_index_converter.xml
@@ -0,0 +1,14 @@
+<tool id="CONVERTER_vcf_to_interval_index_0" name="Convert VCF to Interval Index" version="1.0.0" hidden="true">
+ <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description>
+ <command interpreter="python">vcf_to_interval_index_converter.py $input1 $output1</command>
+ <inputs>
+ <page>
+ <param format="vcf" name="input1" type="data" label="Choose VCF file"/>
+ </page>
+ </inputs>
+ <outputs>
+ <data format="interval_index" name="output1"/>
+ </outputs>
+ <help>
+ </help>
+</tool>
--- a/lib/galaxy_utils/sequence/vcf.py
+++ b/lib/galaxy_utils/sequence/vcf.py
@@ -23,6 +23,8 @@ class VariantCall33( VariantCall ):
required_header_length = len( required_header_fields )
def __init__( self, vcf_line, metadata, sample_names ):
+ # Raw line is needed for indexing file.
+ self.raw_line = vcf_line
self.line = vcf_line.rstrip( '\n\r' )
self.metadata = metadata
self.sample_names = sample_names
@@ -59,10 +61,14 @@ class Reader( object ):
self.vcf_file = fh
self.metadata = {}
self.header_fields = None
+ self.metadata_len = 0
self.sample_names = []
self.vcf_class = None
+
+ # Read file metadata.
while True:
line = self.vcf_file.readline()
+ self.metadata_len += len( line )
assert line, 'Invalid VCF file provided.'
line = line.rstrip( '\r\n' )
if self.vcf_class and line.startswith( self.vcf_class.header_startswith ):
1
0

galaxy-dist commit 28dd2c50c023: updates to DAVID, LPS, and formatHelp help text
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 Richard Burhans <burhans(a)bx.psu.edu>
# Date 1286294376 14400
# Node ID 28dd2c50c02380ed9b3e47b9b598783a0ab03e2c
# Parent 7698203440dec7457f6fd273a36f30ed89e05821
updates to DAVID, LPS, and formatHelp help text
--- a/tools/human_genome_variation/linkToDavid.xml
+++ b/tools/human_genome_variation/linkToDavid.xml
@@ -72,7 +72,7 @@ The list is limited to 400 IDs.
**Dataset formats**
-The input dataset is tabular_ format. The output dataset is html_ format with
+The input dataset is in tabular_ format. The output dataset is html_ with
a link to the DAVID website as described below.
(`Dataset missing?`_)
--- a/static/formatHelp.html
+++ b/static/formatHelp.html
@@ -1,5 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd"><html>
-<head><title>Galaxy Data Formats</title>
+<head>
+<title>Galaxy Data Formats</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta http-equiv="Content-Style-Type" content="text/css">
+<style type="text/css">
+ hr { margin-top: 3ex; margin-bottom: 1ex; border: 1px inset }
+</style></head><body><h2>Galaxy Data Formats</h2>
@@ -18,16 +26,15 @@ data, or even the correct columns needed
by format at least makes the list to select from a bit shorter.
<p>
Some of the formats are defined hierarchically, going from very
-general ones like <a href="#tab">tabular</a> (which includes any text
+general ones like <a href="#tab">Tabular</a> (which includes any text
file with tab-separated columns), to more restrictive sub-formats
-like <a href="#interval">interval</a> (where three of the columns
+like <a href="#interval">Interval</a> (where three of the columns
must be the chromosome, start position, and end position), and on
-to even more specific ones such as <a href="#bed">BED</a> or
-<a href="#gff">GFF</a> that have additional requirements. So for
-example if a tool's required input format is tabular, then all of
-your history items whose format is recorded as tabular will be
-listed, along with those in all sub-formats that also qualify as
-tabular (interval, BED, GFF, etc.).
+to even more specific ones such as <a href="#bed">BED</a> that have
+additional requirements. So for example if a tool's required input
+format is Tabular, then all of your history items whose format is
+recorded as Tabular will be listed, along with those in all
+sub-formats that also qualify as Tabular (Interval, BED, GFF, etc.).
<p>
There are two usual methods for changing a dataset's format in
Galaxy: if the file contents are already in the required format but
@@ -37,7 +44,7 @@ manually by clicking on the pencil icon
history. Or, if the file contents really are in a different format,
Galaxy provides a number of format conversion tools (e.g. in the
Text Manipulation and Convert Formats categories). For instance,
-if the tool you want to run requires tabular but your columns are
+if the tool you want to run requires Tabular but your columns are
delimited by spaces or commas, you can use the "Convert delimiters
to TAB" tool under Text Manipulation to reformat your data. However
if your files are in a completely unsupported format, then you need
@@ -47,7 +54,7 @@ to convert them yourself before uploadin
<h3>Format Descriptions</h3><ul>
-<li><a href="#ab1">Ab1</a>
+<li><a href="#ab1">AB1</a><li><a href="#axt">AXT</a><li><a href="#bam">BAM</a><li><a href="#bed">BED</a>
@@ -55,19 +62,19 @@ to convert them yourself before uploadin
<li><a href="#binseq">Binseq.zip</a><li><a href="#fasta">FASTA</a><li><a href="#fastqsolexa">FastqSolexa</a>
-<li><a href="#fped">fped</a>
+<li><a href="#fped">FPED</a><li><a href="#gff">GFF</a><li><a href="#gff3">GFF3</a><li><a href="#gtf">GTF</a><li><a href="#html">HTML</a><li><a href="#interval">Interval</a><li><a href="#lav">LAV</a>
-<li><a href="#lped">lped</a>
+<li><a href="#lped">LPED</a><li><a href="#maf">MAF</a>
-<li><a href="#pbed">pbed</a>
+<li><a href="#pbed">PBED</a><li><a href="#psl">PSL</a>
-<li><a href="#scf">Scf</a>
-<li><a href="#sff">Sff</a>
+<li><a href="#scf">SCF</a>
+<li><a href="#sff">SFF</a><li><a href="#table">Table</a><li><a href="#tab">Tabular</a><li><a href="#txtseqzip">Txtseq.zip</a>
@@ -75,17 +82,23 @@ to convert them yourself before uploadin
<li><a href="#text">Other text type</a></ul><p>
+
+<div><a name="ab1"></a></div><hr>
+<strong>AB1</strong>
+<p>
+This is one of the ABIF family of binary sequence formats from
+Applied Biosystems Inc.
+<!-- Their PDF
+<a href="http://www.appliedbiosystems.com/support/software_community/ABIF_File_Forma…"
+>format specification</a> is unfortunately password-protected. -->
+Files should have a '<code>.ab1</code>' file extension. You must
+manually select this file format when uploading the file.
+<p>
-<strong>Ab1</strong>
-<a name="ab1"/>
-<p>
-A binary sequence file in 'ab1' format with a '.ab1' file extension.
-You must manually select this file format when uploading the file.
-<hr/>
-
+<div><a name="axt"></a></div>
+<hr><strong>AXT</strong>
-<a name="axt"/><p>
Used for pairwise alignment output from BLASTZ, after post-processing.
Each alignment block contains three lines: a summary line and two
@@ -94,44 +107,53 @@ The summary line contains chromosomal po
about the alignment, and consists of nine required fields.
<a href="http://main.genome-browser.bx.psu.edu/goldenPath/help/axt.html"
>More information</a>
+<!-- (not available on Main)
<dl><dt>Can be converted to:
<dd><ul>
-<li>FASTA<br/>
-Convert Formats→AXT to FASTA
-<li>LAV<br/>
-Convert Formats→AXT to LAV
+<li>FASTA<br>
+Convert Formats → AXT to FASTA
+<li>LAV<br>
+Convert Formats → AXT to LAV
</ul></dl>
-<hr/>
+-->
+<p>
+<div><a name="bam"></a></div>
+<hr><strong>BAM</strong>
-<a name="bam"/><p>
-A binary file compressed in the BGZF format with a '.bam' file
-extension.
-<a href="http://samtools.sourceforge.net/SAM1.pdf">SAM</a> format
-is the human readable text version of these files.
+A binary alignment file compressed in the BGZF format with a
+'<code>.bam</code>' file extension.
+<!-- You must manually select this file format when uploading the file. -->
+<a href="http://samtools.sourceforge.net/SAM1.pdf">SAM</a>
+is the human-readable text version of this format.
<dl><dt>Can be converted to:
<dd><ul>
-<li>pileup<br/>
-NGS: SAM Tools→Generate pileup<br/>
-<li>interval<br/>
-First you have to go to pileup as above then
-NGS: SAM Tools→Pileup-to-Interval
+<li>SAM<br>
+NGS: SAM Tools → BAM-to-SAM
+<li>Pileup<br>
+NGS: SAM Tools → Generate pileup
+<li>Interval<br>
+First convert to Pileup as above, then use
+NGS: SAM Tools → Pileup-to-Interval
</ul></dl>
-<hr/>
+<p>
+<div><a name="bed"></a></div>
+<hr><strong>BED</strong>
-<a name="bed"/><p><ul>
-<li> also qualifies as tabular
-<li> also qualifies as interval
+<li> also qualifies as Tabular
+<li> also qualifies as Interval
</ul>
This tab-separated format describes a genomic interval, but has
strict field specifications for use in genome browsers. BED files
can have from 3 to 12 columns, but the order of the columns matters,
and only the end ones can be omitted. Some groups of columns must
-be all present or all absent.
+be all present or all absent. As in Interval format (but unlike
+GFF and its relatives), the interval endpoints use a 0-based,
+half-open numbering system.
<a href="http://main.genome-browser.bx.psu.edu/goldenPath/help/hgTracksHelp.html#BED"
>Field specifications</a><p>
@@ -142,17 +164,18 @@ chr22 2000 6000 cloneB 900 - 2000 6000 0
</pre><dl><dt>Can be converted to:
<dd><ul>
-<li>GFF<br/>
-Convert Formats→BED-to-GFF
+<li>GFF<br>
+Convert Formats → BED-to-GFF
</ul></dl>
-<hr/>
+<p>
+<div><a name="bedgraph"></a></div>
+<hr><strong>BedGraph</strong>
-<a name="bedgraph"/><p><ul>
-<li> also qualifies as tabular
-<li> also qualifies as interval
+<li> also qualifies as Tabular
+<li> also qualifies as Interval
<li> also qualifies as BED
</ul><a href="http://main.genome-browser.bx.psu.edu/goldenPath/help/bedgraph.html"
@@ -160,26 +183,28 @@ Convert Formats→BED-to-GFF
that is displayed as a wiggle score in tracks. Unlike in Wiggle
format, the exact value of this score can be retrieved after being
loaded as a track.
-<hr/>
+<p>
+<div><a name="binseq"></a></div>
+<hr><strong>Binseq.zip</strong>
-<a name="binseq"/><p>
-A zipped archive consisting of binary sequence files in either
-'ab1' or 'scf' format. All files in this archive must have the same
-file extension which is one of '.ab1' or '.scf'. You must manually
-select this file format when uploading the file.
-<hr/>
+A zipped archive consisting of binary sequence files in either AB1
+or SCF format. All files in this archive must have the same file
+extension which is one of '<code>.ab1</code>' or '<code>.scf</code>'.
+You must manually select this file format when uploading the file.
+<p>
+<div><a name="fasta"></a></div>
+<hr><strong>FASTA</strong>
-<a name="fasta"/><p>
A sequence in
<a href="http://www.ncbi.nlm.nih.gov/blast/fasta.shtml">FASTA</a>
format consists of a single-line description, followed by lines of
sequence data. The first character of the description line is a
-greater-than (">") symbol. All lines should be shorter than 80
-characters.
+greater-than ('<code>></code>') symbol. All lines should be
+shorter than 80 characters.
<pre>
>sequence1
atgcgtttgcgtgc
@@ -190,16 +215,17 @@ tggcgcggtga
</pre><dl><dt>Can be converted to:
<dd><ul>
-<li>tabular<br/>
-Convert Formats→FASTA-to-Tabular
+<li>Tabular<br>
+Convert Formats → FASTA-to-Tabular
</ul></dl>
-<hr/>
+<p>
+<div><a name="fastqsolexa"></a></div>
+<hr><strong>FastqSolexa</strong>
-<a name="fastqsolexa"/><p><a href="http://maq.sourceforge.net/fastq.shtml">FastqSolexa</a>
-is the Illumina (Solexa) variant of the Fastq format, which stores
+is the Illumina (Solexa) variant of the FASTQ format, which stores
sequences and quality scores in a single file.
<pre>
@seq1
@@ -224,82 +250,97 @@ 40 15 40 17 6 36 40 40 40 25 40 9 35 33
</pre><dl><dt>Can be converted to:
<dd><ul>
-<li>FASTA<br/>
-Convert Formats→FASTQ to FASTA
+<li>FASTA<br>
+NGS: QC and manipulation → Generic FASTQ manipulation → FASTQ to FASTA
+<li>Tabular<br>
+NGS: QC and manipulation → Generic FASTQ manipulation → FASTQ to Tabular
</ul></dl>
-<hr/>
+<p>
-<strong>fped</strong>
-<a name="fped"/>
+<div><a name="fped"></a></div>
+<hr>
+<strong>FPED</strong><p>
Also known as the FBAT format, for use with the
<a href="http://biosun1.harvard.edu/~fbat/fbat.htm">FBAT</a> program.
It consists of a pedigree file and a phenotype file.
-<hr/>
+<p>
+<div><a name="gff"></a></div>
+<hr><strong>GFF</strong>
-<a name="gff"/><p><ul>
-<li> also qualifies as tabular
-<li> also qualifies as interval
+<li> also qualifies as Tabular
</ul>
GFF is a tab-separated format somewhat similar to BED, but it has
different columns and is more flexible. There are
<a href="http://main.genome-browser.bx.psu.edu/FAQ/FAQformat#format3"
>nine required fields</a>.
+Note that unlike Interval and BED, GFF and its relatives (GFF3, GTF)
+use 1-based inclusive coordinates to specify genomic intervals.
<dl><dt>Can be converted to:
<dd><ul>
-<li>BED<br/>
-Convert Formats→GFF-to-BED
+<li>BED<br>
+Convert Formats → GFF-to-BED
</ul></dl>
-<hr/>
+<p>
+<div><a name="gff3"></a></div>
+<hr><strong>GFF3</strong>
-<a name="gff3"/><p><ul>
-<li> also qualifies as tabular
-<li> also qualifies as interval
+<li> also qualifies as Tabular
</ul>
The <a href="http://www.sequenceontology.org/gff3.shtml">GFF3</a>
-format addresses the most common extensions to GFF, while preserving
-backward compatibility with previous formats.
-<hr/>
+format addresses the most common extensions to GFF, while attempting
+to preserve compatibility with previous formats.
+Note that unlike Interval and BED, GFF and its relatives (GFF3, GTF)
+use 1-based inclusive coordinates to specify genomic intervals.
+<p>
+<div><a name="gtf"></a></div>
+<hr><strong>GTF</strong>
-<a name="gtf"/><p><ul>
-<li> also qualifies as tabular
-<li> also qualifies as interval
+<li> also qualifies as Tabular
</ul><a href="http://main.genome-browser.bx.psu.edu/FAQ/FAQformat#format4"
->GTF</a> is a format for describing genes and other features
-associated with DNA, RNA, and protein sequences.
+>GTF</a> is a format for describing genes and other features associated
+with DNA, RNA, and protein sequences. It is a refinement to GFF that
+tightens the specification.
+Note that unlike Interval and BED, GFF and its relatives (GFF3, GTF)
+use 1-based inclusive coordinates to specify genomic intervals.
+<!-- (not available on Main)
<dl><dt>Can be converted to:
<dd><ul>
-<li>BedGraph<br/>
-Convert Formats→GTF-to-BEDGraph
+<li>BedGraph<br>
+Convert Formats → GTF-to-BEDGraph
</ul></dl>
-<hr/>
+-->
+<p>
+<div><a name="html"></a></div>
+<hr><strong>HTML</strong>
-<a name="html"/><p>
This format is an HTML web page. Click the eye icon next to the
dataset to view it in your browser.
-<hr/>
+<p>
+<div><a name="interval"></a></div>
+<hr><strong>Interval</strong>
-<a name="interval"><p><ul>
-<li> also qualifies as tabular
+<li> also qualifies as Tabular
</ul>
This Galaxy format represents genomic intervals. It is tab-separated,
but has the added requirement that three of the columns must be the
-chromosome name, start position, and end position. An optional
+chromosome name, start position, and end position, where the positions
+use a 0-based, half-open numbering system (see below). An optional
strand column can also be specified, and an initial header row can
be used to label the columns, which do not have to be in any special
order. Arbitrary additional columns can also be present.
@@ -317,7 +358,8 @@ Required fields:
</ul>
Optional:
<ul>
-<li>STRAND - Defines the strand, either '+' or '-'.
+<li>STRAND - Defines the strand, either '<code>+</code>' or
+'<code>-</code>'.
<li>Header row
</ul>
Example:
@@ -328,173 +370,202 @@ Example:
</pre><dl><dt>Can be converted to:
<dd><ul>
-<li>BED<br/>
+<li>BED<br>
The exact changes needed and tools to run will vary with what fields
-are in the interval file and what type of BED you are converting to.
-In general you will likely use Text Manipulation→Compute, Cut,
+are in the Interval file and what type of BED you are converting to.
+In general you will likely use Text Manipulation → Compute, Cut,
or Merge Columns.
</ul></dl>
-<hr/>
+<p>
+<div><a name="lav"></a></div>
+<hr><strong>LAV</strong>
-<a name="lav"/><p><a href="http://www.bx.psu.edu/miller_lab/dist/lav_format.html">LAV</a>
is the raw pairwise alignment format that is output by BLASTZ. The
first line begins with <code>#:lav</code>.
+<!-- (not available on Main)
<dl><dt>Can be converted to:
<dd><ul>
-<li>BED<br/>
-Convert Formats→LAV to BED
+<li>BED<br>
+Convert Formats → LAV to BED
</ul></dl>
-<hr/>
+-->
+<p>
-<strong>lped</strong>
-<a name="lped"/>
+<div><a name="lped"></a></div>
+<hr>
+<strong>LPED</strong><p>
-This is the linkage pedigree format, which consists of separate
-<code>map</code> and <code>ped</code> files. Together these files
-describe SNPs; the map file contains the position and an identifier
-for the SNP, while the pedigree file has the alleles.
-To upload this format into Galaxy, do not use auto-detect for the
-file format; instead select <code>lped</code>. You will then be
-given two sections for uploading files, one for the pedigree file
-and one for the map file. For more information, see
-<a href="http://www.broadinstitute.org/science/programs/medical-and-population-genet…">linkage pedigree</a>,
-<a href="http://pngu.mgh.harvard.edu/~purcell/plink/data.shtml#map">map</a>,
-and/or <a href="http://pngu.mgh.harvard.edu/~purcell/plink/data.shtml#ped">ped</a>.
+This is the linkage pedigree format, which consists of separate MAP and PED
+files. Together these files describe SNPs; the map file contains the position
+and an identifier for the SNP, while the pedigree file has the alleles. To
+upload this format into Galaxy, do not use Auto-detect for the file format;
+instead select <code>lped</code>. You will then be given two sections for
+uploading files, one for the pedigree file and one for the map file. For more
+information, see
+<a href="http://www.broadinstitute.org/science/programs/medical-and-population-genet…"
+>linkage pedigree</a>,
+<a href="http://pngu.mgh.harvard.edu/~purcell/plink/data.shtml#map">MAP</a>,
+and/or <a href="http://pngu.mgh.harvard.edu/~purcell/plink/data.shtml#ped">PED</a>.
<dl><dt>Can be converted to:
<dd><ul>
-<li>pbed<br/>Automatic
-<li>fped<br/>Automatic
+<li>PBED<br>Automatic
+<li>FPED<br>Automatic
</ul></dl>
-<hr/>
+<p>
+<div><a name="maf"></a></div>
+<hr><strong>MAF</strong>
-<a name="maf"/><p>
-Multiple alignment format that is output by TBA and Multiz. The
-first line begins with <code>##maf</code>. This word is followed by
-whitespace-separated "variable=value pairs". There should be no
-whitespace surrounding the "=".
<a href="http://main.genome-browser.bx.psu.edu/FAQ/FAQformat#format5"
->More information</a>
+>MAF</a> is the multi-sequence alignment format that is output by TBA
+and Multiz. The first line begins with '<code>##maf</code>'. This
+word is followed by whitespace-separated "variable<code>=</code>value"
+pairs. There should be no whitespace surrounding the '<code>=</code>'.
<dl><dt>Can be converted to:
<dd><ul>
-<li>BED<br/>
-Convert Formats→MAF to BED
-<li>interval<br/>
-Convert Formats→MAF to Interval
-<li>FASTA<br/>
-Convert Formats→MAF to FASTA
+<li>BED<br>
+Convert Formats → MAF to BED
+<li>Interval<br>
+Convert Formats → MAF to Interval
+<li>FASTA<br>
+Convert Formats → MAF to FASTA
</ul></dl>
-<hr/>
+<p>
-<strong>pbed</strong>
-<a name="pbed"/>
+<div><a name="pbed"></a></div>
+<hr>
+<strong>PBED</strong><p>
-This is the binary version of the lped file format.
+This is the binary version of the LPED format.
<dl><dt>Can be converted to:
<dd><ul>
-<li>lped<br/>Automatic
+<li>LPED<br>Automatic
</ul></dl>
-<hr/>
+<p>
+<div><a name="psl"></a></div>
+<hr><strong>PSL</strong>
-<a name="psl"/><p><a href="http://main.genome-browser.bx.psu.edu/FAQ/FAQformat#format2">PSL</a>
format is used for alignments returned by
<a href="http://genome.ucsc.edu/cgi-bin/hgBlat?command=start">BLAT</a>.
It does not include any sequence.
-<hr/>
+<p>
-<strong>Scf</strong>
-<a name="scf"/>
+<div><a name="scf"></a></div>
+<hr>
+<strong>SCF</strong><p>
-A binary sequence file in 'scf' format with a '.scf' file extension.
-You must manually select this file format when uploading the file.
+This is a binary sequence format originally designed for the Staden
+sequence handling software package. Files should have a
+'<code>.scf</code>' file extension. You must manually select this
+file format when uploading the file.
<a href="http://staden.sourceforge.net/manual/formats_unix_2.html"
>More information</a>
-<hr/>
+<p>
-<strong>Sff</strong>
-<a name="sff"/>
+<div><a name="sff"></a></div>
+<hr>
+<strong>SFF</strong><p>
-A binary file in 'Standard Flowgram Format' with a '.sff' file extension.
+This is a binary sequence format used by the Roche 454 GS FLX
+sequencing machine, and is documented on p. 528 of their
+<a href="http://sequence.otago.ac.nz/download/GS_FLX_Software_Manual.pdf"
+>software manual</a>. Files should have a '<code>.sff</code>' file
+extension.
+<!-- You must manually select this file format when uploading the file. --><dl><dt>Can be converted to:
<dd><ul>
-<li>FASTA<br/>
-Convert Formats→SFF converter
-<li>FASTQ<br/>
-Convert Formats→SFF converter
+<li>FASTA<br>
+Convert Formats → SFF converter
+<li>FASTQ<br>
+Convert Formats → SFF converter
</ul></dl>
-<hr/>
+<p>
+<div><a name="table"></a></div>
+<hr><strong>Table</strong>
-<a name="table"/><p>
Text data separated into columns by something other than tabs.
-<hr/>
+<p>
+<div><a name="tab"></a></div>
+<hr><strong>Tabular (tab-delimited)</strong>
-<a name="tab"/><p>
One or more columns of text data separated by tabs.
<dl><dt>Can be converted to:
<dd><ul>
-<li>FASTA<br/>
-Convert Formats→Tabular-to-FASTA<br/>
-The tabular file must have a title and sequence column.
-<li>interval<br/>
-If the tabular file has the chromosome, or is all on one chromosome,
-and has a position you can create an interval file (e.g. for SNPs).
-If it is all on one chromosome, use Text Manipulation→Add column
-to add a chromosome column. If the given position is 1-based, use
-Text Manipulation→Compute with the position column minus 1 to
-get the start, and use the original given column for the end.
-If the given position is 0-based, use it as the start, and compute
-that plus 1 to get the end.
+<li>FASTA<br>
+Convert Formats → Tabular-to-FASTA<br>
+The Tabular file must have a title and sequence column.
+<li>FASTQ<br>
+NGS: QC and manipulation → Generic FASTQ manipulation → Tabular to FASTQ
+<li>Interval<br>
+If the Tabular file has a chromosome column (or is all on one
+chromosome) and has a position column, you can create an Interval
+file (e.g. for SNPs). If it is all on one chromosome, use
+Text Manipulation → Add column to add a CHROM column.
+If the given position is 1-based, use
+Text Manipulation → Compute with the position column minus 1 to
+get the START, and use the original given column for the END.
+If the given position is 0-based, use it as the START, and compute
+that plus 1 to get the END.
</ul></dl>
-<hr/>
+<p>
+<div><a name="txtseqzip"></a></div>
+<hr><strong>Txtseq.zip</strong>
-<a name="txtseqzip"/><p>
A zipped archive consisting of flat text sequence files. All files
-in this archive must have the same file extension of '.txt'. You
-must manually select this file format when uploading the file.
-<hr/>
+in this archive must have the same file extension of
+'<code>.txt</code>'. You must manually select this file format when
+uploading the file.
+<p>
+<div><a name="wig"></a></div>
+<hr><strong>Wiggle custom track</strong>
-<a name="wig"/><p>
-The wiggle format is line-oriented. Wiggle data is preceded by a
-track definition line, which specifies the type of wiggle. There
-are three different types, for different uses.
+Wiggle tracks are typically used to display per-nucleotide scores
+in a genome browser. The Wiggle format for custom tracks is
+line-oriented, and the wiggle data is preceded by a track definition
+line that specifies which of three different types is being used.
<a href="http://main.genome-browser.bx.psu.edu/goldenPath/help/wiggle.html"
>More information</a><dl><dt>Can be converted to:
<dd><ul>
-<li>interval<br/>
-Convert Formats→Wiggle-to-Interval<br/>
-As a second step this could be converted to BED-3 or BED-4 by removing
-columns, using Text Manipulation→Cut columns from a table.
+<li>Interval<br>
+Get Genomic Scores → Wiggle-to-Interval
+<li>As a second step this could be converted to 3- or 4-column BED,
+by removing extra columns using
+Text Manipulation → Cut columns from a table.
</ul></dl>
-<hr/>
+<p>
+<div><a name="text"></a></div>
+<hr><strong>Other text type</strong>
-<a name="text"/><p>
Any text file.
<dl><dt>Can be converted to:
<dd><ul>
-<li>tabular<br/>
-If this has fields separated by spaces, commas, or some other
-delimiter it can be converted to tabular using
-Text Manipulation→Convert delimiters to TAB
+<li>Tabular<br>
+If the text has fields separated by spaces, commas, or some other
+delimiter, it can be converted to Tabular by using
+Text Manipulation → Convert delimiters to TAB.
</ul></dl>
+<p>
+
<!-- blank lines so internal links will jump farther to end -->
-<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
+<br><br><br><br><br><br><br><br><br><br><br><br>
+<br><br><br><br><br><br><br><br><br><br><br><br></body></html>
--- a/tools/human_genome_variation/lps.xml
+++ b/tools/human_genome_variation/lps.xml
@@ -224,9 +224,9 @@ Let **x** be a row from your input datas
from the results file. To compute the probability that row **x** has
a label value of +1:
- Probability(row **x** has label value = +1) = 1 / [1 + exp{**x** \* **b**\[1..n-1\] + **b**\[n\]}]
+ Probability(row **x** has label value = +1) = 1 / [1 + exp{**x** \* **b**\[1..N-1\] + **b**\[N\]}]
-where **x** \* **b**\[1..n-1\] represents matrix multiplication.
+where **x** \* **b**\[1..N-1\] represents matrix multiplication.
The second output dataset, called the log file, is a text file which
contains additional data about the fitted L1-regularized logistic
1
0

galaxy-dist commit 41cf53bdcca2: Visual tweaks to new search UI
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 Kanwei Li <kanwei(a)gmail.com>
# Date 1286332784 14400
# Node ID 41cf53bdcca21a255c94ae90d20a9bd492cf6741
# Parent 269c46003857daaf034a41d1f0cd690e0b60df72
Visual tweaks to new search UI
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -91,6 +91,10 @@
%endif
%endif
+ $(".search-box-input").live("click", function() {
+ $(this).css("font-style", "normal");
+ });
+
//
// Code to handle grid operations: filtering, sorting, paging, and operations.
//
@@ -304,7 +308,7 @@
}
// Add button that displays filter and provides a button to delete it.
- var t = $("<span>" + value + "<a href='#'><img class='delete-search-icon' /></a></span>");
+ var t = $("<span>" + value + "<a href='javascript:void(0);'><span class='delete-search-icon' /></a></span>");
t.addClass('text-filter-val');
t.click(function() {
// Remove filter condition.
@@ -623,12 +627,11 @@
}
.text-filter-val {
border: solid 1px #AAAAAA;
- padding: 1px 3px 1px 3px;
+ padding: 1px 2px 1px 3px;
margin-right: 5px;
-moz-border-radius: .5em;
-webkit-border-radius: .5em;
font-style: italic;
-
}
.page-link a, .inactive-link {
padding: 0px 7px 0px 7px;
@@ -652,20 +655,26 @@
#more-search-options td {
padding: 3px;
}
+ #more-search-options table {
+ border-collapse: separate;
+ }
.delete-search-icon {
background: url(${h.url_for("/static/images/delete_tag_icon_gray.png")}) center no-repeat;
display: inline-block;
width: 10px;
cursor: pointer;
- height: 16px;
+ height: 18px;
vertical-align: middle;
+ margin-left: 2px;
+
}
.search-box-input {
border: 0;
- margin: 0;
- margin-left: 2px;
- padding: 0;
+ margin: 1px 0 0 2px;
float: left;
+ outline: medium none;
+ font-style: italic;
+ font-size: inherit;
}
.search-box {
vertical-align: bottom;
1
0

galaxy-dist commit a89b59ecb9e0: trackster: Can now reorder tracks inline. Put cancel button in modal dialogs to the left instead of right
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 Kanwei Li <kanwei(a)gmail.com>
# Date 1286384865 14400
# Node ID a89b59ecb9e06778ee39f0b1b2aed38613c127b6
# Parent 642edc1ae4f50a0a7e6d00904654733f4172fbf7
trackster: Can now reorder tracks inline. Put cancel button in modal dialogs to the left instead of right
--- a/static/scripts/packed/galaxy.base.js
+++ b/static/scripts/packed/galaxy.base.js
@@ -1,1 +1,1 @@
-function obj_length(c){if(c.length!==undefined){return c.length}var b=0;for(var a in c){b++}return b}$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function ensure_popup_helper(){if($("#popup-helper").length===0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return fa
lse};$(b).bind("click",c)}function make_popupmenu(c,b){ensure_popup_helper();var a=$("<ul id='"+c.attr("id")+"-menu'></ul>");if(obj_length(b)<=0){$("<li/>").html("No options").appendTo(a)}$.each(b,function(f,e){if(e){$("<li/>").html(f).click(e).appendTo(a)}else{$("<li class='head'/>").html(f).appendTo(a)}});var d=$("<div class='popmenu-wrapper'>");d.append(a).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(c,d)}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));a.find("a").bind("click",function(b){b.stopPropagation();return true});make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}funct
ion naturalSort(i,g){var n=/(-?[0-9\.]+)/g,j=i.toString().toLowerCase()||"",f=g.toString().toLowerCase()||"",k=String.fromCharCode(0),l=j.replace(n,k+"$1"+k).split(k),e=f.replace(n,k+"$1"+k).split(k),d=(new Date(j)).getTime(),m=d?(new Date(f)).getTime():null;if(m){if(d<m){return -1}else{if(d>m){return 1}}}for(var h=0,c=Math.max(l.length,e.length);h<c;h++){oFxNcL=parseFloat(l[h])||l[h];oFyNcL=parseFloat(e[h])||e[h];if(oFxNcL<oFyNcL){return -1}else{if(oFxNcL>oFyNcL){return 1}}}return 0}function replace_big_select_inputs(a,b){if(!jQuery().autocomplete){return}if(a===undefined){a=20}if(b===undefined){b=3000}$("select").each(function(){var d=$(this);var g=d.find("option").length;if((g<a)||(g>b)){return}if(d.attr("multiple")==true){return}if(d.hasClass("no-autocomplete")){return}var m=d.attr("value");var c=$("<input type='text' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",d.attr("name"));c.attr("id",d.attr("id"));c.click(function(){var n=$(this).
val();$(this).val("Loading...");$(this).showAllInCache();$(this).val(n);$(this).select()});var e=[];var i={};d.children("option").each(function(){var o=$(this).text();var n=$(this).attr("value");e.push(o);i[o]=n;i[n]=n;if(n==m){c.attr("value",o)}});if(m==""||m=="?"){c.attr("value","Click to Search or Select")}if(d.attr("name")=="dbkey"){e=e.sort(naturalSort)}var f={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:b,minChars:0,hideForLessThanMinChars:false};c.autocomplete(e,f);d.replaceWith(c);var k=function(){var o=c.attr("value");var n=i[o];if(n!==null&&n!==undefined){c.attr("value",n)}else{if(m!=""){c.attr("value",m)}else{c.attr("value","?")}}};c.parents("form").submit(function(){k()});$(document).bind("convert_dbkeys",function(){k()});if(d.attr("refresh_on_change")=="true"){var h=d.attr("refresh_on_change_values"),l=d.attr("last_selected_value");if(h!==undefined){h=h.split(",")}var j=function(){var n=i[c.attr("value")];if(n!==null&&n!==undefined){if
($.inArray(n,h)===-1&&$.inArray(l,h)===-1){return}c.attr("value",n);$(window).trigger("refresh_on_change");c.parents("form").submit()}};c.bind("result",j);c.keyup(function(n){if(n.keyCode===13){j()}});c.keydown(function(n){if(n.keyCode===13){return false}})}})}function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text($.trim(k))}else{j=$("<input type='text'></input>").attr({value:$.trim(k),size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){if(o!=""){l.text(o)}else{l.html("<em>None</em>")}if(b){b(j)}}}
)}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStore.store("history_expand_state");if(e){for(var g in e){$("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStore.remove("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id;var h=$(this).children("div.historyItemBody");var i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){var k=$.jStore.store("history_expand_state");if(k){delete k[j];$.jStore.store("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){var k=$.jStore.store("history_expand_state");i
f(k===undefined){k={}}k[j]=true;$.jStore.store("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStore.store("history_expand_state");if(h===undefined){h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStore.store("history_expand_state",h)}).show()};if(a){b()}else{$.jStore.init("galaxy");$.jStore.engineReady(function(){b()})}}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length==0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!="recently_used_wrappe
r"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.val("search tools");b.css("font-style","italic")}}var GalaxyAsync=function(a){this.url_dict={};this.log_action=(a===undefined?false:a)};GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(".trackster-add").live("click",function(){var b=this,a=$(this);$.ajax({url:a.attr("data-
url"),dataType:"html",error:function(){alert("Could not add this dataset to browser.")},success:function(c){var d=window.parent;d.show_modal("Add to Browser:",c,{"Insert into selected":function(){$(d.document).find("input[name=id]:checked").each(function(){var e=$(this).val();d.location=a.attr("action-url")+"&id="+e})},"Insert into new browser":function(){d.location=a.attr("new-url")},Cancel:function(){d.hide_modal()}})}})});$(document).ready(function(){$("select[refresh_on_change='true']").change(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");a.get(0).form.submit()});$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tipsy){$(".tooltip").tipsy({gravity:"s"})}make_popup_menus();replace_big_select_inputs(20,1500)});
+function obj_length(c){if(c.length!==undefined){return c.length}var b=0;for(var a in c){b++}return b}$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function ensure_popup_helper(){if($("#popup-helper").length===0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return fa
lse};$(b).bind("click",c)}function make_popupmenu(c,b){ensure_popup_helper();var a=$("<ul id='"+c.attr("id")+"-menu'></ul>");if(obj_length(b)<=0){$("<li/>").html("No options").appendTo(a)}$.each(b,function(f,e){if(e){$("<li/>").html(f).click(e).appendTo(a)}else{$("<li class='head'/>").html(f).appendTo(a)}});var d=$("<div class='popmenu-wrapper'>");d.append(a).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(c,d)}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));a.find("a").bind("click",function(b){b.stopPropagation();return true});make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}funct
ion naturalSort(i,g){var n=/(-?[0-9\.]+)/g,j=i.toString().toLowerCase()||"",f=g.toString().toLowerCase()||"",k=String.fromCharCode(0),l=j.replace(n,k+"$1"+k).split(k),e=f.replace(n,k+"$1"+k).split(k),d=(new Date(j)).getTime(),m=d?(new Date(f)).getTime():null;if(m){if(d<m){return -1}else{if(d>m){return 1}}}for(var h=0,c=Math.max(l.length,e.length);h<c;h++){oFxNcL=parseFloat(l[h])||l[h];oFyNcL=parseFloat(e[h])||e[h];if(oFxNcL<oFyNcL){return -1}else{if(oFxNcL>oFyNcL){return 1}}}return 0}function replace_big_select_inputs(a,b){if(!jQuery().autocomplete){return}if(a===undefined){a=20}if(b===undefined){b=3000}$("select").each(function(){var d=$(this);var g=d.find("option").length;if((g<a)||(g>b)){return}if(d.attr("multiple")==true){return}if(d.hasClass("no-autocomplete")){return}var m=d.attr("value");var c=$("<input type='text' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",d.attr("name"));c.attr("id",d.attr("id"));c.click(function(){var n=$(this).
val();$(this).val("Loading...");$(this).showAllInCache();$(this).val(n);$(this).select()});var e=[];var i={};d.children("option").each(function(){var o=$(this).text();var n=$(this).attr("value");e.push(o);i[o]=n;i[n]=n;if(n==m){c.attr("value",o)}});if(m==""||m=="?"){c.attr("value","Click to Search or Select")}if(d.attr("name")=="dbkey"){e=e.sort(naturalSort)}var f={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:b,minChars:0,hideForLessThanMinChars:false};c.autocomplete(e,f);d.replaceWith(c);var k=function(){var o=c.attr("value");var n=i[o];if(n!==null&&n!==undefined){c.attr("value",n)}else{if(m!=""){c.attr("value",m)}else{c.attr("value","?")}}};c.parents("form").submit(function(){k()});$(document).bind("convert_dbkeys",function(){k()});if(d.attr("refresh_on_change")=="true"){var h=d.attr("refresh_on_change_values"),l=d.attr("last_selected_value");if(h!==undefined){h=h.split(",")}var j=function(){var n=i[c.attr("value")];if(n!==null&&n!==undefined){if
($.inArray(n,h)===-1&&$.inArray(l,h)===-1){return}c.attr("value",n);$(window).trigger("refresh_on_change");c.parents("form").submit()}};c.bind("result",j);c.keyup(function(n){if(n.keyCode===13){j()}});c.keydown(function(n){if(n.keyCode===13){return false}})}})}function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text($.trim(k))}else{j=$("<input type='text'></input>").attr({value:$.trim(k),size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){if(o!=""){l.text(o)}else{l.html("<em>None</em>")}if(b){b(j)}}}
)}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStore.store("history_expand_state");if(e){for(var g in e){$("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStore.remove("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id;var h=$(this).children("div.historyItemBody");var i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){var k=$.jStore.store("history_expand_state");if(k){delete k[j];$.jStore.store("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){var k=$.jStore.store("history_expand_state");i
f(k===undefined){k={}}k[j]=true;$.jStore.store("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStore.store("history_expand_state");if(h===undefined){h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStore.store("history_expand_state",h)}).show()};if(a){b()}else{$.jStore.init("galaxy");$.jStore.engineReady(function(){b()})}}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length==0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!="recently_used_wrappe
r"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.val("search tools");b.css("font-style","italic")}}var GalaxyAsync=function(a){this.url_dict={};this.log_action=(a===undefined?false:a)};GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(".trackster-add").live("click",function(){var b=this,a=$(this);$.ajax({url:a.attr("data-
url"),dataType:"html",error:function(){alert("Could not add this dataset to browser.")},success:function(c){var d=window.parent;d.show_modal("Add to Browser:",c,{Cancel:function(){d.hide_modal()},"Insert into selected":function(){$(d.document).find("input[name=id]:checked").each(function(){var e=$(this).val();d.location=a.attr("action-url")+"&id="+e})},"Insert into new browser":function(){d.location=a.attr("new-url")}})}})});$(document).ready(function(){$("select[refresh_on_change='true']").change(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");a.get(0).form.submit()});$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tipsy){$(".tooltip").tipsy({gravity:"s"})}make_popup_menus();replace_big_select_inputs(20,1500)});
--- a/static/scripts/packed/trackster.js
+++ b/static/scripts/packed/trackster.js
@@ -1,1 +1,1 @@
-var DENSITY=200,FEATURE_LEVELS=10,MAX_FEATURE_DEPTH=100,DATA_ERROR="There was an error in indexing this dataset. ",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),PX_PER_CHAR=CONTEXT.measureText("A").width,RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function()
{RIGHT_STRAND_INV=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,c,e,d,b){this.container=a;this.vis_id=d;this.dbkey=b;this.title=e;this.chrom=c;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.
min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{init:function(){var c=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").hide().appendTo(c);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide().appendTo(c);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);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/>").addClass("overview-viewport").appendTo(this.overview);this.overview_box_background=$("<div/>").addClass("overview-boxback").app
endTo(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(){void (0)}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.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.hi
de();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(d){if(d.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=d.chrom_info;var f='<option value="">Select Chrom/Contig</option>';for(i in a.chrom_data){var e=a.chrom_data[i]["chrom"];f+='<option value="'+e+'">'+e+"</option>"}a.chrom_select.html(f);a.intro_div.show();a.content_div.hide();a.chrom_select.b
ind("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.viewport_container.bind("dragstart",function(d){this.original_low=a.low;this.current_height=d.clientY;this.current_x=d.offsetX;this.active=(d.clientX<a.viewport_container.width()-16)?true:false}).bind("drag",function(g){if(!this.active){return}var d=$(this);var j=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(j/a.viewport_container.width()*(a.high-a.low));a.move_delta(h)});this.top_labeltra
ck.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(j){var f=Math.min(j.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(j.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(k){var f=Math.min(k.clientX,this.drag_origin_x),d=Math.max(k.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),j=a.low;a.low=Math.round(f/g*h)+j;a.high=Math.round(d/g*h)+j;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(this,this.top_labeltrack));thi
s.add_label_track(new LabelTrack(this,this.nav_labeltrack))},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(d,a,f){var c=this;var e=$.grep(c.chrom_data,function(h,j){return h.chrom===d})[0];if(e===undefined){return}if(d!==c.chrom){c.chrom=d;if(c.chrom===""){c.intro_div.show();c.content_div.hide()}else{c.intro_div.hide();c.content_div.show()}c.chrom_select.val(c.chrom);c.max_high=e.len;c.reset();c.redraw(true);for(var g in c.tracks){var b=c.tracks[g];if(b.init){b.init()}}}if(a!==undefined&&f!==undefined){c.low=Math.max(a,0);c.high=Math.min(f,c.max_high)}c.overview_viewport.find("canvas").remove();c.redraw()},go_to:function(f){var k=this,b=f.split(":"),h=b[0],j=b[1];if(j!==undefined){try{var g=j.split("-"),a=parseInt(g[0].replace(/,/g,"")),d=parseInt(g[1].replace(/,/g,""))}catch(c){return false}}k.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_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1;this.num_tracks+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){this.has_changes=true;a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[this.tracks.indexOf(a)];this.num_tracks-=1},update_options:function(){this.has_changes=true;var b=$("ul#sortable-ul").sortable("toArray");for(var c in b){var e=b[c].split("_li")[0].split("track_")[1];this.viewport_container.append($("#track_"+e))}for(var d in view.tracks){var a=view.tracks[d];if(a&&a.update_options){a.update_options(d)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewpor
t_container.find(".yaxislabel").remove()},redraw:function(h){var d=this.high-this.low,b=this.low,f=this.high;if(b<this.max_low){b=this.max_low}if(f>this.max_high){f=this.max_high}if(this.high!==0&&d<this.min_separation){f=b+this.min_separation}this.low=Math.floor(b);this.high=Math.ceil(f);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 e=(this.low/(this.max_high-this.max_low))*this.overview_viewport.width();var g=(this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width();this.overview_box.css({left:e,width:Math.max(12,g)}).show();if(this.overview_highlight){this.overview_highlight.css({left:e,width:g})}this.update_location(this.low,this.high);if(!h){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.
length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);this.high=Math.round(c+a);this.redraw()}});var Track=function(b,a,c){this.name=b;this.parent_element=c;this.view=a;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track");if(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);this.name_div=$("<div class='menubutton popup' />").appendTo(this.header_div);this.name_div.text(this.name)}this.content_div=$("<div class='track-content'>").appendTo(this.contain
er_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_di
v.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 d=this,c=d.view;if(d.hidden){return}if(d.display_modes!==undefined){if(d.mode_div===undefined){d.mode_div=$("<div class='right-float menubutton popup' />").appendTo(d.header_div);var h=d.display_modes[0];d.mode=h;d.mode_div.text(h);var a=function(j){d.mode_div.text(j);d.mode=j;d.tile_cache.clear();d.draw()};var f={};for(var e in d.display_modes){var g=d.display_modes[e];f[g]=function(j){return function(){a(j)}}(g)}make_popupmenu(d.mode_div,f)}else{d.mode_div.hide()}}var b={};b["Set track as overview"]=function(){c.overview_viewport.find("canvas").remove();d.is_overview=true;d.set_overview();for(var j in c.tracks){if(c.tra
cks[j]!==d){c.tracks[j].is_overview=false}}};b["Edit configuration"]=function(){var k=function(){hide_modal()};var j=function(){d.update_options(d.track_id);hide_modal()};show_modal("Configure Track",d.gen_options(d.track_id),{Cancel:k,OK:j})};b.Remove=function(){c.remove_track(d);if(c.num_tracks===0){$("#no-tracks").show()}};make_popupmenu(d.name_div,b)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var j=this.view.low,e=this.view.high,f=e-j,d=this.view.resolution;var l=$("<div style='position: relative;'></div>"),m=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(l),this.max_height=0;var a=Math.floor(j/d/DENSITY);while((a*DENSITY*d)<e){var k=this.content_div.width()+"_"+m+"_"+a;var c=this.tile_cache.get(k);if(c){var g=a*DENSITY*d;var b=(g-j)*m;if(this.left_offset){b-=this.left_offset}c.css({left:b});l.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height
+"px")}else{this.delayed_draw(this,k,j,e,a,d,l,m)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){if(!c.initial_canvas){c.initial_canvas=$(tile_element).clone();var l=tile_element.get(0).getContext("2d");var j=c.initial_canvas.get(0).getContext("2d");var k=l.getImageData(0,0,l.canvas.width,l.canvas.height);j.putImageData(k,0,0);c.set_overview()}c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)},set_overview:function(){var a=this.view;a.overview_viewport.height(a.default_overview_height);a.overview_box.height(a.default_overview_height);if(this.initial_canvas&&this.is_overview){if(!a.overview_highlight){a.overview_highlight=$("<div />").addClass("overview-highlight").appendTo(a.overview_viewport)}a.overview_viewport.append(this.initial_canvas);a.overview_highlight.height(this.init
ial_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.height_px=12;this.container_div.addClass("reference-track");this.dummy_canvas=$("<
canvas></canvas>").get(0).getContext("2d");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.prototype,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,e=$("<canvas class='tile'></canvas>"),n=e.get(0).getContext("2d"),j=f+"_"+b;if(o>PX_PER_CHAR){if(this.data_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-th
is.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o);n.fillText(m[h],a+this.left_offset,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b,a,c){this.track_type="LineTrack";this.display_modes=["Line","Filled","Intensity"];this.mode="Line";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={min_value:undefined,max_value:undefined,mode:"Line"};if(c.min_value!==undefined){this.prefs.min_value=c.min_value}if(c.max_value!==undefined){this.prefs.max_value=c.max_value}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");data=c.data;if(isNaN(parseFloat(a.prefs.min_va
lue))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(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:"relative",top:"32px",left:"10px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+32+"px",left:"10px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_
id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(p,r,c,e){if(this.vertical_range===undefined){return}var s=r*DENSITY*p,a=DENSITY*p,b=$("<canvas class='tile'></canvas>"),v=p+"_"+r;if(this.data_cache.get(v)===undefined){this.get_data(p,r);return}var j=this.data_cache.get(v);if(j===null){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var o=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,n=this.vertical_range,t=this.total_frequency,d=this.height_px,m=this.mode;o.beginPath();if(data.length>1){var f=Math.ceil((data[1][0]-data[0][0])*e)}else{var f=10}var u,h;for(var q=0;q<data.length;q++){u=(data[q][0]-s)*e;h=data[q][1];if(m=="Intensity"){if(h===null){continue}if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/n*255);o.fillStyl
e="rgb("+h+","+h+","+h+")";o.fillRect(u,0,f,this.height_px)}else{if(h===null){if(k&&m==="Filled"){o.lineTo(u,d)}k=false;continue}else{if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/n*d);if(k){o.lineTo(u,h)}else{k=true;if(m==="Filled"){o.moveTo(u,d);o.lineTo(u,h)}else{o.moveTo(u,h)}}}}}if(m==="Filled"){if(k){o.lineTo(u,d)}o.fill()}else{o.stroke()}c.append(b);return b},gen_options:function(k){var a=$("<div />").addClass("form-row");var e="track_"+k+"_minval",h=$("<label></label>").attr("for",e).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),j=$("<input></input>").attr("id",e).val(b),g="track_"+k+"_maxval",d=$("<label></label>").attr("for",g).text("Max value:"),f=(this.prefs.max_value===undefined?"":this.prefs.max_value),c=$("<input></input>").attr("id",g).val(f);return a.append(h).append(j).append(d).append(c)},update_options:function(c){var a=$("#track_"+c+"_minval").val(),b=$("#track_"+c+"_maxval").val();if(a!==this.prefs.min_value||b!=
=this.prefs.max_value){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(b);this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+c+"_minval").text(this.prefs.min_value);$("#linetrack_"+c+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=2;this.summary_draw_height=20;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:"black",label_color:"black
",show_counts:true};if(c.block_color!==undefined){this.prefs.block_color=c.block_color}if(c.label_color!==undefined){this.prefs.label_color=c.label_color}if(c.show_counts!==undefined){this.prefs.show_counts=c.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b="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.view.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,h,c,r){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=r;this.s_e_by_tile[a]={}}var n=this.inc_slots[a].w_sca
le,z=[],l=0,b=$("<canvas></canvas>").get(0).getContext("2d"),o=this.view.max_low;var B=[];if(this.inc_slots[a].mode!==r){delete this.inc_slots[a];this.inc_slots[a]={mode:r,w_scale:n};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var w=0,x=h.length;w<x;w++){var g=h[w],m=g[0];if(this.inc_slots[a][m]!==undefined){l=Math.max(l,this.inc_slots[a][m]);B.push(this.inc_slots[a][m])}else{z.push(w)}}for(var w=0,x=z.length;w<x;w++){var g=h[z[w]],m=g[0],s=g[1],d=g[2],q=g[3],e=Math.floor((s-o)*n),f=Math.ceil((d-o)*n);if(q!==undefined&&!c){var t=b.measureText(q).width;if(e-t<0){f+=t}else{e-=t}}var v=0;while(true){var p=true;if(this.s_e_by_tile[a][v]!==undefined){for(var u=0,A=this.s_e_by_tile[a][v].length;u<A;u++){var y=this.s_e_by_tile[a][v][u];if(f>y[0]&&e<y[1]){p=false;break}}}if(p){if(this.s_e_by_tile[a][v]===undefined){this.s_e_by_tile[a][v]=[]}this.s_e_by_tile[a][v].push([e,f]);this.inc_slots[a][m]=v;l=Math.max(l,v);break}v++}}return l},rect_or_text:function(n,o,f,m,b,d,k,e,h
){n.textAlign="center";var j=Math.round(o/2);if((this.mode==="Pack"||this.mode==="Auto")&&d!==undefined&&o>PX_PER_CHAR){n.fillStyle=this.prefs.block_color;n.fillRect(k,h+1,e,9);n.fillStyle="#eee";for(var g=0,l=d.length;g<l;g++){if(b+g>=f&&b+g<=m){var a=Math.floor(Math.max(0,(b+g-f)*o));n.fillText(d[g],a+this.left_offset+j,h+9)}}}else{n.fillStyle=this.prefs.block_color;n.fillRect(k,h+4,e,3)}},draw_tile:function(aa,l,o,an){var H=l*DENSITY*aa,ag=(l+1)*DENSITY*aa,G=ag-H;var ah=(!this.initial_canvas?"initial":H+"_"+ag);var C=this.data_cache.get(ah);var d;if(C===undefined||(this.mode!=="Auto"&&C.dataset_type==="summary_tree")){this.data_queue[[H,ag]]=true;this.get_data(H,ag);return}var a=Math.ceil(G*an),O=$("<canvas class='tile'></canvas>"),ac=this.prefs.label_color,g=this.prefs.block_color,n=this.mode,r=25,Y=(n==="Squish")||(n==="Dense")&&(n!=="Pack")||(n==="Auto"&&(C.extra_info==="no_detail")),S=this.left_offset,am,v,ao;if(C.dataset_type==="summary_tree"){v=this.summary_draw_hei
ght}else{if(n==="Dense"){v=r;ao=10}else{ao=(Y?this.vertical_nodetail_px:this.vertical_detail_px);var s=(an<0.0001?1/view.zoom_res:an);v=this.incremental_slots(s,C.data,Y,n)*ao+r;am=this.inc_slots[s]}}O.css({position:"absolute",top:0,left:(H-this.view.low)*an-S});O.get(0).width=a+S;O.get(0).height=v;o.parent().css("height",Math.max(this.height_px,v)+"px");var D=O.get(0).getContext("2d");D.fillStyle=g;D.font=this.default_font;D.textAlign="right";if(C.dataset_type=="summary_tree"){var N,K=55,af=255-K,h=af*2/3,U=C.data,F=C.max,m=C.avg,b=Math.ceil(C.delta*an);for(var aj=0,B=U.length;aj<B;aj++){var W=Math.floor((U[aj][0]-H)*an);var V=U[aj][1];if(!V){continue}N=Math.floor(af-(V/F)*af);D.fillStyle="rgb("+N+","+N+","+N+")";D.fillRect(W+S,0,b,this.summary_draw_height);if(this.prefs.show_counts&&D.measureText(V).width<b){if(N>h){D.fillStyle="black"}else{D.fillStyle="#ddd"}D.textAlign="center";D.fillText(V,W+S+(b/2),12)}}d="Summary";o.append(O);return O}if(C.message){O.css({border:"soli
d red","border-width":"2px 2px 2px 0px"});D.fillStyle="red";D.textAlign="left";D.fillText(C.message,100+S,ao)}var al=C.data;var ai=0;for(var aj=0,B=al.length;aj<B;aj++){var P=al[aj],M=P[0],ak=P[1],X=P[2],I=P[3];if(ak<=ag&&X>=H){var Z=Math.floor(Math.max(0,(ak-H)*an)),E=Math.ceil(Math.min(a,Math.max(0,(X-H)*an))),T=(n==="Dense"?1:(1+am[M]))*ao;if(C.dataset_type==="bai"){D.fillStyle=g;if(P[4] instanceof Array){var w=Math.floor(Math.max(0,(P[4][0]-H)*an)),L=Math.ceil(Math.min(a,Math.max(0,(P[4][1]-H)*an))),u=Math.floor(Math.max(0,(P[5][0]-H)*an)),q=Math.ceil(Math.min(a,Math.max(0,(P[5][1]-H)*an)));if(P[4][1]>=H&&P[4][0]<=ag){this.rect_or_text(D,an,H,ag,P[4][0],P[4][2],w+S,L-w,T)}if(P[5][1]>=H&&P[5][0]<=ag){this.rect_or_text(D,an,H,ag,P[5][0],P[5][2],u+S,q-u,T)}if(u>L){D.fillStyle="#999";D.fillRect(L+S,T+5,u-L,1)}}else{D.fillStyle=g;this.rect_or_text(D,an,H,ag,ak,I,Z+S,E-Z,T)}if(n!=="Dense"&&!Y&&ak>H){D.fillStyle=this.prefs.label_color;if(l===0&&Z-D.measureText(I).width<0){D.tex
tAlign="left";D.fillText(M,E+2+S,T+8)}else{D.textAlign="right";D.fillText(M,Z-2+S,T+8)}D.fillStyle=g}}else{if(C.dataset_type==="interval_index"){if(Y){D.fillStyle=g;D.fillRect(Z+S,T+5,E-Z,1)}else{var A=P[4],R=P[5],ab=P[6],f=P[7];var z,ad,J=null,ap=null;if(R&&ab){J=Math.floor(Math.max(0,(R-H)*an));ap=Math.ceil(Math.min(a,Math.max(0,(ab-H)*an)))}if(n!=="Dense"&&I!==undefined&&ak>H){D.fillStyle=ac;if(l===0&&Z-D.measureText(I).width<0){D.textAlign="left";D.fillText(I,E+2+S,T+8)}else{D.textAlign="right";D.fillText(I,Z-2+S,T+8)}D.fillStyle=g}if(f){if(A){if(A=="+"){D.fillStyle=RIGHT_STRAND}else{if(A=="-"){D.fillStyle=LEFT_STRAND}}D.fillRect(Z+S,T,E-Z,10);D.fillStyle=g}for(var ah=0,e=f.length;ah<e;ah++){var p=f[ah],c=Math.floor(Math.max(0,(p[0]-H)*an)),Q=Math.ceil(Math.min(a,Math.max((p[1]-H)*an)));if(c>Q){continue}z=5;ad=3;D.fillRect(c+S,T+ad,Q-c,z);if(J!==undefined&&!(c>ap||Q<J)){z=9;ad=1;var ae=Math.max(c,J),t=Math.min(Q,ap);D.fillRect(ae+S,T+ad,t-ae,z)}}}else{z=9;ad=1;D.fillRect
(Z+S,T+ad,E-Z,z);if(P.strand){if(P.strand=="+"){D.fillStyle=RIGHT_STRAND_INV}else{if(P.strand=="-"){D.fillStyle=LEFT_STRAND_INV}}D.fillRect(Z+S,T,E-Z,10);D.fillStyle=g}}}}}ai++}}o.append(O);return O},gen_options:function(j){var a=$("<div />").addClass("form-row");var e="track_"+j+"_block_color",l=$("<label />").attr("for",e).text("Block color:"),m=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),k="track_"+j+"_label_color",g=$("<label />").attr("for",k).text("Text color:"),h=$("<input />").attr("id",k).attr("name",k).val(this.prefs.label_color),f="track_"+j+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(l).append(m).append(g).append(h).append(d)},update_options:function(e){var b=$("#track_"+e+"_block_color").val(),d=$("#track_"+e+"_label_color").val(),c=
$("#track_"+e+"_mode option:selected").val(),a=$("#track_"+e+"_show_count").attr("checked");if(b!==this.prefs.block_color||d!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=d;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,c){FeatureTrack.call(this,d,b,a,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
+var DENSITY=200,FEATURE_LEVELS=10,MAX_FEATURE_DEPTH=100,DATA_ERROR="There was an error in indexing this dataset. ",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),PX_PER_CHAR=CONTEXT.measureText("A").width,RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src=image_path+"/visualization/strand_right_inv.png";right_img_inv.onload=function()
{RIGHT_STRAND_INV=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,c,e,d,b){this.container=a;this.vis_id=d;this.dbkey=b;this.title=e;this.chrom=c;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.
min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.prototype,{init:function(){var c=this.container,a=this;this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(c);this.content_div=$("<div/>").addClass("content").css("position","relative").hide().appendTo(c);this.intro_div=$("<div/>").addClass("intro").text("Select a chrom from the dropdown below").hide().appendTo(c);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);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/>").addClass("overview-viewport").appendTo(this.overview);this.overview_box_background=$("<div/>").addClass("overview-boxback").app
endTo(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(){void (0)}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);var b=function(d){if(d.type==="focusout"||(d.keyCode||d.which)===13||(d.keyCode||d.which)===27){if((d.keyCode||d.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.hi
de();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(d){if(d.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=d.chrom_info;var f='<option value="">Select Chrom/Contig</option>';for(i in a.chrom_data){var e=a.chrom_data[i]["chrom"];f+='<option value="'+e+'">'+e+"</option>"}a.chrom_select.html(f);a.intro_div.show();a.content_div.hide();a.chrom_select.b
ind("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.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 j=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(j/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(j){var f=Math.min(j.clientX,this.drag_origin_x)-a.container.offset().left,d=Math.max(j.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(k){var f=Math.min(k.clientX,this.drag_origin_x),d=Math.max(k.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),j=a.low;a.low=Math.round(f/g*h)+j;a.high=Math.round(d/g*h)+j;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(th
is,this.top_labeltrack));this.add_label_track(new LabelTrack(this,this.nav_labeltrack))},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(d,a,f){var c=this;var e=$.grep(c.chrom_data,function(h,j){return h.chrom===d})[0];if(e===undefined){return}if(d!==c.chrom){c.chrom=d;if(c.chrom===""){c.intro_div.show();c.content_div.hide()}else{c.intro_div.hide();c.content_div.show()}c.chrom_select.val(c.chrom);c.max_high=e.len;c.reset();c.redraw(true);for(var g in c.tracks){var b=c.tracks[g];if(b.init){b.init()}}}if(a!==undefined&&f!==undefined){c.low=Math.max(a,0);c.high=Math.min(f,c.max_high)}c.overview_viewport.find("canvas").remove();c.redraw()},go_to:function(f){var k=this,b=f.split(":"),h=b[0],j=b[1];if(j!==undefined){try{var g=j.split("-"),a=parseInt(g[0].replace(/,/g,"")),d=parseInt(g[1].replace(/,/g,""))}catch(c){return false}}k.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_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 d=this.high-this.low,b=this.low,f=this.high;if(b<this.max_low){b=this.max_low}if(f>this.max_high){f=this.max_high}if(this.high!==0&&d<this.min_separation){f=b+this.min_separation}this.low=Math.floor(b);th
is.high=Math.ceil(f);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 e=(this.low/(this.max_high-this.max_low))*this.overview_viewport.width();var g=(this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width();this.overview_box.css({left:e,width:Math.max(12,g)}).show();if(this.overview_highlight){this.overview_highlight.css({left:e,width:g})}this.update_location(this.low,this.high);if(!h){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+thi
s.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);this.high=Math.round(c+a);this.redraw()}});var Track=function(b,a,c){this.name=b;this.parent_element=c;this.view=a;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.container_div=$("<div />").addClass("track");if(!this.hidden){this.header_div=$("<div class='track-header' />").appendTo(this.container_div);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.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.con
tent_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("he
ight",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 d=this,c=d.view;if(d.hidden){return}if(d.display_modes!==undefined){if(d.mode_div===undefined){d.mode_div=$("<div class='right-float menubutton popup' />").appendTo(d.header_div);var h=d.display_modes[0];d.mode=h;d.mode_div.text(h);var a=function(j){d.mode_div.text(j);d.mode=j;d.tile_cache.clear();d.draw()};var f={};for(var e in d.display_modes){var g=d.display_modes[e];f[g]=function(j){return function(){a(j)}}(g)}make_popupmenu(d.mode_div,f)}else{d.mode_div.hide()}}var b={};b["Set track as overview"]=function(){c.overview_viewport.find("canvas").remove();d.is_overview=true;d.set_overview();for(var j in c.tracks){if(c.tracks[j]!==d){c.tracks[j].is_overview=false}}};b["Edit configuration"]=function(){var k=function(){hide_modal()};var j=function(){d.update_options(d.track_id);hide_modal()};show_modal("Configure Trac
k",d.gen_options(d.track_id),{Cancel:k,OK:j})};b.Remove=function(){c.remove_track(d);if(c.num_tracks===0){$("#no-tracks").show()}};make_popupmenu(d.name_div,b)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var j=this.view.low,e=this.view.high,f=e-j,d=this.view.resolution;var l=$("<div style='position: relative;'></div>"),m=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(l),this.max_height=0;var a=Math.floor(j/d/DENSITY);while((a*DENSITY*d)<e){var k=this.content_div.width()+"_"+m+"_"+a;var c=this.tile_cache.get(k);if(c){var g=a*DENSITY*d;var b=(g-j)*m;if(this.left_offset){b-=this.left_offset}c.css({left:b});l.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height+"px")}else{this.delayed_draw(this,k,j,e,a,d,l,m)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_el
ement){if(!c.initial_canvas){c.initial_canvas=$(tile_element).clone();var l=tile_element.get(0).getContext("2d");var j=c.initial_canvas.get(0).getContext("2d");var k=l.getImageData(0,0,l.canvas.width,l.canvas.height);j.putImageData(k,0,0);c.set_overview()}c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)},set_overview:function(){var a=this.view;a.overview_viewport.height(a.default_overview_height);a.overview_box.height(a.default_overview_height);if(this.initial_canvas&&this.is_overview){if(!a.overview_highlight){a.overview_highlight=$("<div />").addClass("overview-highlight").appendTo(a.overview_viewport)}a.overview_viewport.append(this.initial_canvas);a.overview_highlight.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.h
idden=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.height_px=12;this.container_div.addClass("reference-track");this.dummy_canvas=$("<canvas></canvas>").get(0).getContext("2d");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.proto
type,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,e=$("<canvas class='tile'></canvas>"),n=e.get(0).getContext("2d"),j=f+"_"+b;if(o>PX_PER_CHAR){if(this.data_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){this.content_div.css("height","0px");return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o-this.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o);n.fillText(m[h],a+this.left_offset,10)}k.append(e);return e}this.content_div.css("height","0px")}});var LineTrack=function(d,b
,a,c){this.track_type="LineTrack";this.display_modes=["Line","Filled","Intensity"];this.mode="Line";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={min_value:undefined,max_value:undefined,mode:"Line"};if(c.min_value!==undefined){this.prefs.min_value=c.min_value}if(c.max_value!==undefined){this.prefs.max_value=c.max_value}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.
vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(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:"relative",top:"32px",left:"10px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+32+"px",left:"10px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(p,r,c,
e){if(this.vertical_range===undefined){return}var s=r*DENSITY*p,a=DENSITY*p,b=$("<canvas class='tile'></canvas>"),v=p+"_"+r;if(this.data_cache.get(v)===undefined){this.get_data(p,r);return}var j=this.data_cache.get(v);if(j===null){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var o=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,n=this.vertical_range,t=this.total_frequency,d=this.height_px,m=this.mode;o.beginPath();if(data.length>1){var f=Math.ceil((data[1][0]-data[0][0])*e)}else{var f=10}var u,h;for(var q=0;q<data.length;q++){u=(data[q][0]-s)*e;h=data[q][1];if(m=="Intensity"){if(h===null){continue}if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/n*255);o.fillStyle="rgb("+h+","+h+","+h+")";o.fillRect(u,0,f,this.height_px)}else{if(h===null){if(k&&m==="Filled"){o.lineTo(u,d)}k=false;continue}else{if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/n*d);if(k)
{o.lineTo(u,h)}else{k=true;if(m==="Filled"){o.moveTo(u,d);o.lineTo(u,h)}else{o.moveTo(u,h)}}}}}if(m==="Filled"){if(k){o.lineTo(u,d)}o.fill()}else{o.stroke()}c.append(b);return b},gen_options:function(k){var a=$("<div />").addClass("form-row");var e="track_"+k+"_minval",h=$("<label></label>").attr("for",e).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),j=$("<input></input>").attr("id",e).val(b),g="track_"+k+"_maxval",d=$("<label></label>").attr("for",g).text("Max value:"),f=(this.prefs.max_value===undefined?"":this.prefs.max_value),c=$("<input></input>").attr("id",g).val(f);return a.append(h).append(j).append(d).append(c)},update_options:function(c){var a=$("#track_"+c+"_minval").val(),b=$("#track_"+c+"_maxval").val();if(a!==this.prefs.min_value||b!==this.prefs.max_value){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(b);this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+c+"_minval").text(this.pre
fs.min_value);$("#linetrack_"+c+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,c){this.track_type="FeatureTrack";this.display_modes=["Auto","Dense","Squish","Pack"];Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=2;this.summary_draw_height=20;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:"black",label_color:"black",show_counts:true};if(c.block_color!==undefined){this.prefs.block_color=c.block_color}if(c.label_color!==undefined){this.prefs.label_color=c.label_color}if(c.show_counts!==undefined){this.prefs.sh
ow_counts=c.show_counts}};$.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.view.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,h,c,r){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=a;this.inc_slots[a].mode=r;this.s_e_by_tile[a]={}}var n=this.inc_slots[a].w_scale,z=[],l=0,b=$("<canvas></canvas>").get(0).getContext("2d"),o=this.view.max_low;var B=[];if(this.inc_slots[a].mode!==r){delete this.inc_slots[a];this.inc_slots[a]={mode:r,w_scale:n};delete this.s_
e_by_tile[a];this.s_e_by_tile[a]={}}for(var w=0,x=h.length;w<x;w++){var g=h[w],m=g[0];if(this.inc_slots[a][m]!==undefined){l=Math.max(l,this.inc_slots[a][m]);B.push(this.inc_slots[a][m])}else{z.push(w)}}for(var w=0,x=z.length;w<x;w++){var g=h[z[w]],m=g[0],s=g[1],d=g[2],q=g[3],e=Math.floor((s-o)*n),f=Math.ceil((d-o)*n);if(q!==undefined&&!c){var t=b.measureText(q).width;if(e-t<0){f+=t}else{e-=t}}var v=0;while(true){var p=true;if(this.s_e_by_tile[a][v]!==undefined){for(var u=0,A=this.s_e_by_tile[a][v].length;u<A;u++){var y=this.s_e_by_tile[a][v][u];if(f>y[0]&&e<y[1]){p=false;break}}}if(p){if(this.s_e_by_tile[a][v]===undefined){this.s_e_by_tile[a][v]=[]}this.s_e_by_tile[a][v].push([e,f]);this.inc_slots[a][m]=v;l=Math.max(l,v);break}v++}}return l},rect_or_text:function(n,o,f,m,b,d,k,e,h){n.textAlign="center";var j=Math.round(o/2);if((this.mode==="Pack"||this.mode==="Auto")&&d!==undefined&&o>PX_PER_CHAR){n.fillStyle=this.prefs.block_color;n.fillRect(k,h+1,e,9);n.fillStyle="#eee";f
or(var g=0,l=d.length;g<l;g++){if(b+g>=f&&b+g<=m){var a=Math.floor(Math.max(0,(b+g-f)*o));n.fillText(d[g],a+this.left_offset+j,h+9)}}}else{n.fillStyle=this.prefs.block_color;n.fillRect(k,h+4,e,3)}},draw_tile:function(aa,l,o,an){var H=l*DENSITY*aa,ag=(l+1)*DENSITY*aa,G=ag-H;var ah=(!this.initial_canvas?"initial":H+"_"+ag);var C=this.data_cache.get(ah);var d;if(C===undefined||(this.mode!=="Auto"&&C.dataset_type==="summary_tree")){this.data_queue[[H,ag]]=true;this.get_data(H,ag);return}var a=Math.ceil(G*an),O=$("<canvas class='tile'></canvas>"),ac=this.prefs.label_color,g=this.prefs.block_color,n=this.mode,r=25,Y=(n==="Squish")||(n==="Dense")&&(n!=="Pack")||(n==="Auto"&&(C.extra_info==="no_detail")),S=this.left_offset,am,v,ao;if(C.dataset_type==="summary_tree"){v=this.summary_draw_height}else{if(n==="Dense"){v=r;ao=10}else{ao=(Y?this.vertical_nodetail_px:this.vertical_detail_px);var s=(an<0.0001?1/view.zoom_res:an);v=this.incremental_slots(s,C.data,Y,n)*ao+r;am=this.inc_slots[s
]}}O.css({position:"absolute",top:0,left:(H-this.view.low)*an-S});O.get(0).width=a+S;O.get(0).height=v;o.parent().css("height",Math.max(this.height_px,v)+"px");var D=O.get(0).getContext("2d");D.fillStyle=g;D.font=this.default_font;D.textAlign="right";if(C.dataset_type=="summary_tree"){var N,K=55,af=255-K,h=af*2/3,U=C.data,F=C.max,m=C.avg,b=Math.ceil(C.delta*an);for(var aj=0,B=U.length;aj<B;aj++){var W=Math.floor((U[aj][0]-H)*an);var V=U[aj][1];if(!V){continue}N=Math.floor(af-(V/F)*af);D.fillStyle="rgb("+N+","+N+","+N+")";D.fillRect(W+S,0,b,this.summary_draw_height);if(this.prefs.show_counts&&D.measureText(V).width<b){if(N>h){D.fillStyle="black"}else{D.fillStyle="#ddd"}D.textAlign="center";D.fillText(V,W+S+(b/2),12)}}d="Summary";o.append(O);return O}if(C.message){O.css({border:"solid red","border-width":"2px 2px 2px 0px"});D.fillStyle="red";D.textAlign="left";D.fillText(C.message,100+S,ao)}var al=C.data;var ai=0;for(var aj=0,B=al.length;aj<B;aj++){var P=al[aj],M=P[0],ak=P[1],
X=P[2],I=P[3];if(ak<=ag&&X>=H){var Z=Math.floor(Math.max(0,(ak-H)*an)),E=Math.ceil(Math.min(a,Math.max(0,(X-H)*an))),T=(n==="Dense"?1:(1+am[M]))*ao;if(C.dataset_type==="bai"){D.fillStyle=g;if(P[4] instanceof Array){var w=Math.floor(Math.max(0,(P[4][0]-H)*an)),L=Math.ceil(Math.min(a,Math.max(0,(P[4][1]-H)*an))),u=Math.floor(Math.max(0,(P[5][0]-H)*an)),q=Math.ceil(Math.min(a,Math.max(0,(P[5][1]-H)*an)));if(P[4][1]>=H&&P[4][0]<=ag){this.rect_or_text(D,an,H,ag,P[4][0],P[4][2],w+S,L-w,T)}if(P[5][1]>=H&&P[5][0]<=ag){this.rect_or_text(D,an,H,ag,P[5][0],P[5][2],u+S,q-u,T)}if(u>L){D.fillStyle="#999";D.fillRect(L+S,T+5,u-L,1)}}else{D.fillStyle=g;this.rect_or_text(D,an,H,ag,ak,I,Z+S,E-Z,T)}if(n!=="Dense"&&!Y&&ak>H){D.fillStyle=this.prefs.label_color;if(l===0&&Z-D.measureText(I).width<0){D.textAlign="left";D.fillText(M,E+2+S,T+8)}else{D.textAlign="right";D.fillText(M,Z-2+S,T+8)}D.fillStyle=g}}else{if(C.dataset_type==="interval_index"){if(Y){D.fillStyle=g;D.fillRect(Z+S,T+5,E-Z,1)}else{v
ar A=P[4],R=P[5],ab=P[6],f=P[7];var z,ad,J=null,ap=null;if(R&&ab){J=Math.floor(Math.max(0,(R-H)*an));ap=Math.ceil(Math.min(a,Math.max(0,(ab-H)*an)))}if(n!=="Dense"&&I!==undefined&&ak>H){D.fillStyle=ac;if(l===0&&Z-D.measureText(I).width<0){D.textAlign="left";D.fillText(I,E+2+S,T+8)}else{D.textAlign="right";D.fillText(I,Z-2+S,T+8)}D.fillStyle=g}if(f){if(A){if(A=="+"){D.fillStyle=RIGHT_STRAND}else{if(A=="-"){D.fillStyle=LEFT_STRAND}}D.fillRect(Z+S,T,E-Z,10);D.fillStyle=g}for(var ah=0,e=f.length;ah<e;ah++){var p=f[ah],c=Math.floor(Math.max(0,(p[0]-H)*an)),Q=Math.ceil(Math.min(a,Math.max((p[1]-H)*an)));if(c>Q){continue}z=5;ad=3;D.fillRect(c+S,T+ad,Q-c,z);if(J!==undefined&&!(c>ap||Q<J)){z=9;ad=1;var ae=Math.max(c,J),t=Math.min(Q,ap);D.fillRect(ae+S,T+ad,t-ae,z)}}}else{z=9;ad=1;D.fillRect(Z+S,T+ad,E-Z,z);if(P.strand){if(P.strand=="+"){D.fillStyle=RIGHT_STRAND_INV}else{if(P.strand=="-"){D.fillStyle=LEFT_STRAND_INV}}D.fillRect(Z+S,T,E-Z,10);D.fillStyle=g}}}}}ai++}}o.append(O);return
O},gen_options:function(j){var a=$("<div />").addClass("form-row");var e="track_"+j+"_block_color",l=$("<label />").attr("for",e).text("Block color:"),m=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),k="track_"+j+"_label_color",g=$("<label />").attr("for",k).text("Text color:"),h=$("<input />").attr("id",k).attr("name",k).val(this.prefs.label_color),f="track_"+j+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(l).append(m).append(g).append(h).append(d)},update_options:function(e){var b=$("#track_"+e+"_block_color").val(),d=$("#track_"+e+"_label_color").val(),c=$("#track_"+e+"_mode option:selected").val(),a=$("#track_"+e+"_show_count").attr("checked");if(b!==this.prefs.block_color||d!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_c
olor=b;this.prefs.label_color=d;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,c){FeatureTrack.call(this,d,b,a,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -199,9 +199,9 @@ var View = function( container, chrom, t
this.original_low = view.low;
this.current_height = e.clientY;
this.current_x = e.offsetX;
- this.active = (e.clientX < view.viewport_container.width() - 16) ? true : false; // Fix webkit scrollbar
+ this.enable_pan = (e.clientX < view.viewport_container.width() - 16) ? true : false; // Fix webkit scrollbar
}).bind( "drag", function( e ) {
- if (!this.active) { return; }
+ if (!this.enable_pan || this.in_reordering) { return; }
var container = $(this);
var delta = e.offsetX - this.current_x;
var new_scroll = container.scrollTop() - (e.clientY - this.current_height);
@@ -338,7 +338,7 @@ var View = function( container, chrom, t
track.container_div.fadeOut('slow', function() { $(this).remove(); });
delete this.tracks[this.tracks.indexOf(track)];
this.num_tracks -= 1;
- },
+ },/* No longer needed as config is done inline, one track at a time
update_options: function() {
this.has_changes = true;
var sorted = $("ul#sortable-ul").sortable('toArray');
@@ -353,7 +353,7 @@ var View = function( container, chrom, t
track.update_options(track_id);
}
}
- },
+ },*/
reset: function() {
this.low = this.max_low;
this.high = this.max_high;
@@ -439,6 +439,7 @@ var Track = function (name, view, parent
this.container_div = $("<div />").addClass('track')
if (!this.hidden) {
this.header_div = $("<div class='track-header' />").appendTo(this.container_div);
+ 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);
}
@@ -555,6 +556,7 @@ var TiledTrack = function() {
}
};
make_popupmenu(track.name_div, track_dropdown);
+
/*
if (track.overview_check_div === undefined) {
track.overview_check_div = $("<div class='right-float' />").css("margin-top", "-3px").appendTo(track.header_div);
--- a/static/scripts/galaxy.base.js
+++ b/static/scripts/galaxy.base.js
@@ -551,6 +551,9 @@ GalaxyAsync.prototype.log_user_action =
success: function(table_html) {
var parent = window.parent;
parent.show_modal("Add to Browser:", table_html, {
+ "Cancel": function() {
+ parent.hide_modal();
+ },
"Insert into selected": function() {
$(parent.document).find('input[name=id]:checked').each(function() {
var vis_id = $(this).val();
@@ -559,9 +562,6 @@ GalaxyAsync.prototype.log_user_action =
},
"Insert into new browser": function() {
parent.location = dataset_jquery.attr("new-url");
- },
- "Cancel": function() {
- parent.hide_modal();
}
});
}
--- a/static/june_2007_style/blue/trackster.css
+++ b/static/june_2007_style/blue/trackster.css
@@ -6,6 +6,7 @@
.nav-controls a{padding:0 0.4em;}
.nav-input{font-size:12px;width:30em;z-index:1000;}
.location{display:inline-block;width:15em;margin:0px 10px;}
+.draghandle{float:left;background:transparent url(../images/visualization/draggable_horizontal.png) center center no-repeat;width:10px;height:12px;margin-right:5px;}
.intro{margin-top:200px;margin-left:auto;margin-right:auto;color:#555;text-align:center;font-size:16px;}
.overview{width:100%;margin:0px;color:white;}
.overview-viewport{position:relative;height:14px;background:white;border-bottom:solid gray 1px;margin:0;}
@@ -16,7 +17,7 @@
.yaxislabel{color:#777;}
.line-track .track-content{border-top:1px solid #ddd;border-bottom:1px solid #ddd;}
.track{background:white;}
-.track-header{text-align:left;padding:4px;color:#666;}
+.track-header{text-align:left;padding:4px 0px;color:#666;}
.track-content{overflow:hidden;text-align:center;}
.track.error{margin-bottom:2px;background-color:#ECB4AF;}
.track.nodata{margin-bottom:2px;background-color:#ddd;}
--- a/static/june_2007_style/trackster.css.tmpl
+++ b/static/june_2007_style/trackster.css.tmpl
@@ -34,6 +34,13 @@
width: 15em;
margin: 0px 10px;
}
+.draghandle {
+ float: left;
+ background: transparent url(../images/visualization/draggable_horizontal.png) center center no-repeat;
+ width: 10px;
+ height: 12px;
+ margin-right: 5px;
+}
.intro {
margin-top: 200px;
margin-left: auto;
@@ -107,7 +114,7 @@
.track-header {
text-align: left;
- padding: 4px;
+ padding: 4px 0px;
color: #666;
}
--- a/templates/tracks/browser.mako
+++ b/templates/tracks/browser.mako
@@ -114,12 +114,14 @@
$("#no-tracks").show();
}
$("#title").text(view.title + " (" + view.dbkey + ")");
- $("ul#sortable-ul").sortable({
- update: function(event, ui) {
- for (var track_id in view.tracks) {
- var track = view.tracks[track_id];
- }
- }
+ $(".viewport-container").sortable({
+ start: function(e, ui) {
+ view.in_reordering = true;
+ },
+ stop: function(e, ui) {
+ view.in_reordering = false;
+ },
+ handle: ".draghandle",
});
window.onbeforeunload = function() {
@@ -157,6 +159,9 @@
error: function() { alert( "Grid refresh failed" ); },
success: function(table_html) {
show_modal("Add Track — Select Dataset(s)", table_html, {
+ "Cancel": function() {
+ hide_modal();
+ },
"Insert": function() {
$('input[name=id]:checked').each(function() {
var item_id = $(this).val();
@@ -169,21 +174,18 @@
});
hide_modal();
- },
- "Cancel": function() {
- hide_modal();
}
});
}
});
});
-
+
$("#save-button").bind("click", function(e) {
- view.update_options();
- var sorted = $("ul#sortable-ul").sortable('toArray');
- var payload = [];
+ var sorted = $(".viewport-container").sortable('toArray'),
+ payload = [];
+
for (var i in sorted) {
- var track_id = parseInt(sorted[i].split("track_")[1].split("_li")[0]),
+ var track_id = parseInt(sorted[i].split("track_")[1]),
track = view.tracks[track_id];
payload.push( {
1
0