galaxy-commits
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
November 2010
- 1 participants
- 286 discussions
galaxy-dist commit 1b397ec7f199: 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 1286889424 14400
# Node ID 1b397ec7f199b8a0581e52af694a12985deb0a64
# Parent 3c74359b7bb72c0d9cfc53e3f0b7a9e3ea426b87
sample tracking bug fixes:
- dataset select page now fixed
- find samples page now fixed for regular user
--- a/templates/requests/common/manage_request.mako
+++ b/templates/requests/common/manage_request.mako
@@ -157,7 +157,7 @@
%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>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', show_page=True, request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
%endif
%endif
</div>
--- a/templates/requests/find_samples_index.mako
+++ b/templates/requests/find_samples_index.mako
@@ -10,5 +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_common', action='find_samples' )}"></iframe>
+ <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', cntrller='requests')}"></iframe></%def>
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -382,7 +382,7 @@ class RequestsAdmin( BaseController, Use
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 ):
+ def __build_sample_id_select_field( self, trans, 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
@@ -392,15 +392,15 @@ class RequestsAdmin( BaseController, Use
status = params.get( 'status', 'done' )
request_id = kwd.get( 'request_id', None )
try:
- request = trans.sa_session.query( trans.model.Request ).get( request_id )
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
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 )
+ sample_id_select_field = self.__build_sample_id_select_field( trans, request, selected_value )
if not folder_path:
- return trans.fill_template( '/admin/requests/dataset_transfer.mako',
+ return trans.fill_template( '/admin/requests/get_data.mako',
cntrller='requests_admin',
request=request,
sample_id_select_field=sample_id_select_field,
@@ -413,7 +413,7 @@ class RequestsAdmin( BaseController, Use
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/dataset_transfer.mako',
+ return trans.fill_template( '/admin/requests/get_data.mako',
cntrller='requests_admin',
request=request,
sample_id_select_field=sample_id_select_field,
@@ -424,7 +424,7 @@ class RequestsAdmin( BaseController, Use
elif params.get( 'browse_button', False ):
# get the filenames from the remote host
files = self.__get_files( trans, request.type, folder_path )
- return trans.fill_template( '/admin/requests/dataset_transfer.mako',
+ return trans.fill_template( '/admin/requests/get_data.mako',
cntrller='requests_admin',
request=request,
sample_id_select_field=sample_id_select_field,
@@ -435,7 +435,7 @@ class RequestsAdmin( BaseController, Use
elif params.get( 'folder_up', False ):
# get the filenames from the remote host
files = self.__get_files( trans, request.type, folder_path )
- return trans.fill_template( '/admin/requests/dataset_transfer.mako',
+ return trans.fill_template( '/admin/requests/get_data.mako',
cntrller='requests_admin',
request=request,
sample_id_select_field=sample_id_select_field,
@@ -449,7 +449,7 @@ class RequestsAdmin( BaseController, Use
folder_path = self.__check_path( folder_path )
# get the filenames from the remote host
files = self.__get_files( trans, request.type, folder_path )
- return trans.fill_template( '/admin/requests/dataset_transfer.mako',
+ return trans.fill_template( '/admin/requests/get_data.mako',
cntrller='requests_admin',
request=request,
sample_id_select_field=sample_id_select_field,
--- a/templates/admin/requests/get_data.mako
+++ b/templates/admin/requests/get_data.mako
@@ -96,7 +96,7 @@
</li></ul>
-<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" >
+<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">
--- a/templates/requests/common/find_samples.mako
+++ b/templates/requests/common/find_samples.mako
@@ -66,7 +66,7 @@
%for sample in samples:
<div class="form-row">
Sample: <b>${sample.name}</b> | Barcode: ${sample.bar_code}<br/>
- State: ${sample.state}<br/>
+ State: ${sample.state.name}<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>
1
0
galaxy-dist commit 0e7dbb91575d: 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 1286891943 14400
# Node ID 0e7dbb91575d9c8abf39f759cd7bdf54efadcd80
# Parent 1b397ec7f199b8a0581e52af694a12985deb0a64
sample_tracking bug fixes:
- undelete request now working
- email notification settings for a request can now be changed.
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -516,7 +516,7 @@ class RequestsCommon( BaseController, Us
trans.sa_session.add( s )
trans.sa_session.flush()
num_undeleted += 1
- message += '%i requests have been undeleted.' % num_deleted
+ message += '%i requests have been undeleted.' % num_undeleted
return trans.response.send_redirect( web.url_for( controller=cntrller,
action='browse_requests',
status=status,
@@ -570,7 +570,7 @@ class RequestsCommon( BaseController, Us
email_addresses.append( util.restore_text( email_address ) )
# Make sure email addresses are valid
err_msg = ''
- for email_address in additional_email_addresses:
+ for email_address in email_addresses:
err_msg += self.__validate_email( email_address )
if err_msg:
status = 'error'
1
0
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
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 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 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 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
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 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