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 b7f712cecaa9: Prevent Rename Dataset Action from allowing a blank input.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1289590343 18000
# Node ID b7f712cecaa9f527f1161f923ebe798eba526cb6
# Parent fc1f67023d86a69ea7229501b093d61748630f4c
Prevent Rename Dataset Action from allowing a blank input.
--- a/lib/galaxy/jobs/actions/post.py
+++ b/lib/galaxy/jobs/actions/post.py
@@ -60,14 +60,12 @@ class DefaultJobAction(object):
class EmailAction(DefaultJobAction):
name = "EmailAction"
verbose_name = "Email Notification"
-
@classmethod
def execute(cls, app, sa_session, action, job):
smtp_server = app.config.smtp_server
- if action.action_arguments:
- if action.action_arguments.has_key('host'):
- host = action.action_arguments['host']
+ if action.action_arguments and action.action_arguments.has_key('host'):
+ host = action.action_arguments['host']
else:
host = 'usegalaxy.org'
if smtp_server is None:
@@ -107,9 +105,8 @@ class EmailAction(DefaultJobAction):
@classmethod
def get_short_str(cls, pja):
- if pja.action_arguments:
- if pja.action_arguments.has_key('host'):
- return "Email the current user from server %s when this job is complete." % pja.action_arguments['host']
+ if pja.action_arguments and pja.action_arguments.has_key('host'):
+ return "Email the current user from server %s when this job is complete." % pja.action_arguments['host']
else:
return "Email the current user when this job is complete."
@@ -153,9 +150,11 @@ class RenameDatasetAction(DefaultJobActi
@classmethod
def execute(cls, app, sa_session, action, job):
- for dataset_assoc in job.output_datasets:
- if action.output_name == '' or dataset_assoc.name == action.output_name:
- dataset_assoc.dataset.name = action.action_arguments['newname']
+ # Prevent renaming a dataset to the empty string.
+ if action.action_arguments and action.action_arguments.has_key('newname') and action.action_arguments['newname'] != '':
+ for dataset_assoc in job.output_datasets:
+ if action.output_name == '' or dataset_assoc.name == action.output_name:
+ dataset_assoc.dataset.name = action.action_arguments['newname']
@classmethod
def get_config_form(cls, trans):
@@ -173,7 +172,11 @@ class RenameDatasetAction(DefaultJobActi
@classmethod
def get_short_str(cls, pja):
- return "Rename output '%s' to '%s'." % (pja.output_name, pja.action_arguments['newname'])
+ # Prevent renaming a dataset to the empty string.
+ if pja.action_arguments and pja.action_arguments.has_key('newname') and pja.action_arguments['newname'] != '':
+ return "Rename output '%s' to '%s'." % (pja.output_name, pja.action_arguments['newname'])
+ else:
+ return "Rename action used without a new name specified. Output name will be unchanged."
class HideDatasetAction(DefaultJobAction):
1
0
galaxy-dist commit cadf13f67c65: More changes to sample tracking functional tests
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 1289597856 18000
# Node ID cadf13f67c6593d99f8134ba10baa211a026439b
# Parent ecc1cefbccba6135f824f9573f06c130fd6d08b4
More changes to sample tracking functional tests
- assign target data library, editing samples, assign sample barcode
--- a/test/functional/test_sample_tracking.py
+++ b/test/functional/test_sample_tracking.py
@@ -4,7 +4,9 @@ from base.twilltestcase import *
from base.test_db_util import *
sample_states = [ ( 'New', 'Sample entered into the system' ),
- ( 'Received', 'Sample tube received' ),
+ ( 'Received', 'Sample tube received' ),
+ ( 'Library Started', 'Sample library preparation' ),
+ ( 'Run Started', 'Sequence run in progress' ),
( 'Done', 'Sequence run complete' ) ]
address_dict = dict( short_desc="Office",
name="James Bond",
@@ -93,6 +95,49 @@ class TestFormsAndRequests( TwillTestCas
global role2
role2 = get_role_by_name( name )
assert role2 is not None, 'Problem retrieving role named "Role2" from the database'
+ def test_006_create_library_and_folder( self ):
+ """Testing creating the target data library and folder"""
+ # Logged in as admin_user
+ for index in range( 0, 2 ):
+ name = 'library%s' % str( index + 1 )
+ description = '%s description' % name
+ synopsis = '%s synopsis' % name
+ self.create_library( name=name, description=description, synopsis=synopsis )
+ # Get the libraries for later use
+ global library1
+ library1 = get_library( 'library1', 'library1 description', 'library1 synopsis' )
+ assert library1 is not None, 'Problem retrieving library (library1) from the database'
+ global library2
+ library2 = get_library( 'library2', 'library2 description', 'library2 synopsis' )
+ assert library2 is not None, 'Problem retrieving library (library2) from the database'
+ # setup add_library_item permission to regular_user1
+ # Set permissions on the library, sort for later testing.
+ permissions_in = [ 'LIBRARY_ACCESS' ]
+ permissions_out = []
+ # Role1 members are: admin_user, regular_user1, regular_user3.
+ # Each of these users will be permitted for LIBRARY_ACCESS, LIBRARY_ADD on
+ # library1 and library2.
+ for library in [ library1, library2 ]:
+ self.library_permissions( self.security.encode_id( library.id ),
+ library.name,
+ str( role1.id ),
+ permissions_in,
+ permissions_out )
+ # adding a folder
+ for library in [ library1, library2 ]:
+ name = "%s_folder1" % library.name
+ description = "%s description" % name
+ self.add_folder( 'library_admin',
+ self.security.encode_id( library.id ),
+ self.security.encode_id( library.root_folder.id ),
+ name=name,
+ description=description )
+ global library1_folder1
+ library1_folder1 = get_folder( library1.root_folder.id, 'library1_folder1', 'library1_folder1 description' )
+ assert library1_folder1 is not None, 'Problem retrieving library folder named "library1_folder1" from the database'
+ global library2_folder1
+ library2_folder1 = get_folder( library2.root_folder.id, 'library2_folder1', 'library2_folder1 description' )
+ assert library2_folder1 is not None, 'Problem retrieving library folder named "library2_folder1" from the database'
#
# ====== Form definition test methods ================================================
#
@@ -283,25 +328,25 @@ class TestFormsAndRequests( TwillTestCas
self.view_request( cntrller='requests',
request_id=self.security.encode_id( request1.id ),
strings_displayed=[ 'Sequencing request "%s"' % request1.name,
- 'There are no samples.',
- sample_form_layout_grid_name ],
+ 'There are no samples.' ],
strings_not_displayed=[ request1.states.SUBMITTED,
request1.states.COMPLETE,
- request1.states.REJECTED ] )
+ request1.states.REJECTED,
+ 'Submit request' ] ) # this button should NOT show up as there are no samples yet
# check if the request is showing in the 'new' filter
self.check_request_grid( cntrller='requests',
state=request1.states.NEW,
strings_displayed=[ request1.name ] )
self.view_request_history( cntrller='requests',
request_id=self.security.encode_id( request1.id ),
- strings_displayed=[ 'History of Sequencing Request "%s"' % request1.name,
+ strings_displayed=[ 'History of sequencing request "%s"' % request1.name,
request1.states.NEW,
'Request created' ],
strings_not_displayed=[ request1.states.SUBMITTED,
request1.states.COMPLETE,
request1.states.REJECTED ] )
def test_030_edit_basic_request_info( self ):
- """Testing editing the basic information of a sequencing request"""
+ """Testing editing the basic information and email settings of a sequencing request"""
# logged in as regular_user1
fields = [ 'option2', str( user_address1.id ), 'field3 value (edited)' ]
new_name=request1.name + ' (Renamed)'
@@ -315,43 +360,186 @@ class TestFormsAndRequests( TwillTestCas
strings_displayed=[ 'Edit sequencing request "%s"' % request1.name ],
strings_displayed_after_submit=[ new_name, new_desc ] )
refresh( request1 )
+ # now check email notification settings
+ check_sample_states = [ ( request1.type.states[0].name, request1.type.states[0].id, True ),
+ ( request1.type.states[2].name, request1.type.states[2].id, True ),
+ ( request1.type.states[4].name, request1.type.states[4].id, True ) ]#[ ( state.id, True ) for state in request1.type.states ]
+ strings_displayed = [ 'Edit sequencing request "%s"' % request1.name,
+ 'Email notification settings' ]
+ additional_emails = [ 'test@.bx.psu.edu', 'test2@.bx.psu.edu' ]
+ strings_displayed_after_submit = [ "The changes made to the email notification settings have been saved",
+ '\r\n'.join( additional_emails ) ]
+ self.edit_request_email_settings( cntrller='requests',
+ request_id=self.security.encode_id( request1.id ),
+ check_request_owner=True,
+ additional_emails='\r\n'.join( additional_emails ),
+ check_sample_states=check_sample_states,
+ strings_displayed=strings_displayed,
+ strings_displayed_after_submit=strings_displayed_after_submit )
+ # lastly check the details in the request page
+ strings_displayed = [ 'Sequencing request "%s"' % new_name,
+ new_desc ]
+ for field in fields:
+ strings_displayed.append( field )
+ for state_name, id, is_checked in check_sample_states:
+ strings_displayed.append( state_name )
+ for email in additional_emails:
+ strings_displayed.append( email )
+ self.view_request( cntrller='requests',
+ request_id=self.security.encode_id( request1.id ),
+ strings_displayed=strings_displayed,
+ strings_not_displayed=[] )
def test_035_add_samples_to_request( self ):
"""Testing adding samples to request"""
# logged in as regular_user1
# Sample fields - the tuple represents a sample name and a list of sample form field values
- sample_value_tuples = [ ( 'Sample1', [ 'option1', 'sample1 field2 value', 'sample1 field3 value' ] ),
- ( 'Sample2', [ 'option2', 'sample2 field2 value', 'sample2 field3 value' ] ),
- ( 'Sample3', [ 'option1', 'sample3 field2 value', 'sample3 field3 value' ] ) ]
+ target_library_info = dict(library=self.security.encode_id(library2.id),
+ folder=self.security.encode_id(library2_folder1.id) )
+ sample_value_tuples = \
+ [ ( 'Sample1', target_library_info, [ 'option1', 'sample1 field2 value', 'sample1 field3 value' ] ),
+ ( 'Sample2', target_library_info, [ 'option2', 'sample2 field2 value', 'sample2 field3 value' ] ),
+ ( 'Sample3', target_library_info, [ 'option1', 'sample3 field2 value', 'sample3 field3 value' ] ) ]
strings_displayed_after_submit = [ 'Unsubmitted' ]
- for sample_name, field_values in sample_value_tuples:
+ for sample_name, lib_info, field_values in sample_value_tuples:
strings_displayed_after_submit.append( sample_name )
+ # add the sample values too
+ for values in field_values:
+ strings_displayed_after_submit.append( values )
# Add samples to the request
self.add_samples( cntrller='requests',
request_id=self.security.encode_id( request1.id ),
- request_name=request1.name,
sample_value_tuples=sample_value_tuples,
strings_displayed=[ 'Add Samples to Request "%s"' % request1.name,
'<input type="text" name="sample_0_name" value="Sample_1" size="10"/>' ], # sample name input field
strings_displayed_after_submit=strings_displayed_after_submit )
-# def test_040_edit_samples_of_new_request( self ):
-# """Testing editing the sample information of new request1"""
-# # logged in as regular_user1
-# pass
-# def test_035_submit_request( self ):
-# """Testing editing a sequence run request"""
-# # logged in as regular_user1
-# self.submit_request( cntrller='requests',
-# request_id=self.security.encode_id( request1.id ),
-# request_name=request1.name,
-# strings_displayed_after_submit=[ 'The request has been submitted.' ] )
-# refresh( request1 )
-# # Make sure the request is showing in the 'submitted' filter
-# self.check_request_grid( cntrller='requests',
-# state=request1.states.SUBMITTED,
-# strings_displayed=[ request1.name ] )
-# # Make sure the request's state is now set to 'submitted'
-# assert request1.state is not request1.states.SUBMITTED, "The state of the request '%s' should be set to '%s'" \
-# % ( request1.name, request1.states.SUBMITTED )
+ # check the new sample field values on the request page
+ strings_displayed = [ 'Sequencing request "%s"' % request1.name,
+ 'Submit request' ] # this button should appear now
+ strings_displayed.extend( strings_displayed_after_submit )
+ strings_displayed_count = []
+ strings_displayed_count.append( ( library2.name, len( sample_value_tuples ) ) )
+ strings_displayed_count.append( ( library2_folder1.name, len( sample_value_tuples ) ) )
+ self.view_request( cntrller='requests',
+ request_id=self.security.encode_id( request1.id ),
+ strings_displayed=strings_displayed,
+ strings_displayed_count=strings_displayed_count )
+ def test_040_edit_samples_of_new_request( self ):
+ """Testing editing the sample information of new request1"""
+ # logged in as regular_user1
+ # target data library - change it to library1
+ target_library_info = dict(library=self.security.encode_id(library1.id),
+ folder=self.security.encode_id(library1_folder1.id) )
+ new_sample_value_tuples = \
+ [ ( 'Sample1_renamed', target_library_info, [ 'option2', 'sample1 field2 value edited', 'sample1 field3 value edited' ] ),
+ ( 'Sample2_renamed', target_library_info, [ 'option1', 'sample2 field2 value edited', 'sample2 field3 value edited' ] ),
+ ( 'Sample3_renamed', target_library_info, [ 'option2', 'sample3 field2 value edited', 'sample3 field3 value edited' ] ) ]
+ strings_displayed_after_submit = [ 'Unsubmitted' ]
+ for sample_name, lib_info, field_values in new_sample_value_tuples:
+ strings_displayed_after_submit.append( sample_name )
+ # add the sample values too
+ for values in field_values:
+ strings_displayed_after_submit.append( values )
+ # Add samples to the request
+ self.edit_samples( cntrller='requests',
+ request_id=self.security.encode_id( request1.id ),
+ sample_value_tuples=new_sample_value_tuples,
+ strings_displayed=[ 'Edit Current Samples of Request "%s"' % request1.name,
+ '<input type="text" name="sample_0_name" value="Sample1" size="10"/>' ], # sample name input field
+ strings_displayed_after_submit=strings_displayed_after_submit )
+ # check the changed sample field values on the request page
+ strings_displayed = [ 'Sequencing request "%s"' % request1.name ]
+ strings_displayed.extend( strings_displayed_after_submit )
+ strings_displayed_count = []
+ strings_displayed_count.append( ( library1.name, len( new_sample_value_tuples ) ) )
+ strings_displayed_count.append( ( library1_folder1.name, len( new_sample_value_tuples ) ) )
+ self.view_request( cntrller='requests',
+ request_id=self.security.encode_id( request1.id ),
+ strings_displayed=strings_displayed,
+ strings_displayed_count=strings_displayed_count )
+ def test_045_submit_request( self ):
+ """Testing submitting a sequencing request"""
+ # logged in as regular_user1
+ self.submit_request( cntrller='requests',
+ request_id=self.security.encode_id( request1.id ),
+ request_name=request1.name,
+ strings_displayed_after_submit=[ 'The request has been submitted.' ] )
+ refresh( request1 )
+ # Make sure the request is showing in the 'submitted' filter
+ self.check_request_grid( cntrller='requests',
+ state=request1.states.SUBMITTED,
+ strings_displayed=[ request1.name ] )
+ # Make sure the request's state is now set to 'submitted'
+ assert request1.state is not request1.states.SUBMITTED, "The state of the request '%s' should be set to '%s'" \
+ % ( request1.name, request1.states.SUBMITTED )
+ # the sample state should appear once for each sample
+ strings_displayed_count = [ ( request1.type.states[0].name, len( request1.samples ) ) ]
+ # after submission, these buttons should not appear
+ strings_not_displayed = [ 'Add sample', 'Submit request' ]
+ # check the request page
+ self.view_request( cntrller='requests',
+ request_id=self.security.encode_id( request1.id ),
+ strings_displayed=[ request1.states.SUBMITTED ],
+ strings_displayed_count=strings_displayed_count,
+ strings_not_displayed=strings_not_displayed )
+ strings_displayed=[ 'History of sequencing request "%s"' % request1.name,
+ 'Request submitted by %s' % regular_user1.email,
+ 'Request created' ]
+ strings_displayed_count = [ ( request1.states.SUBMITTED, 1 ) ]
+ self.view_request_history( cntrller='requests',
+ request_id=self.security.encode_id( request1.id ),
+ strings_displayed=strings_displayed,
+ strings_displayed_count=strings_displayed_count,
+ strings_not_displayed=[ request1.states.COMPLETE,
+ request1.states.REJECTED ] )
+ #
+ # ====== Sequencing request test methods - Admin perspective ================
+ #
+ def test_050_receive_request_as_admin( self ):
+ """Testing receiving a sequencing request and assigning it barcodes"""
+ self.logout()
+ self.login( email=admin_user.email )
+ self.check_request_grid( cntrller='requests_admin',
+ state=request1.states.SUBMITTED,
+ strings_displayed=[ request1.name ] )
+ strings_displayed = [ request1.states.SUBMITTED,
+ 'Reject this request' ]
+ strings_not_displayed = [ 'Add sample' ]
+ self.view_request( cntrller='requests_admin',
+ request_id=self.security.encode_id( request1.id ),
+ strings_not_displayed=strings_not_displayed )
+ # Set bar codes for the samples
+ bar_codes = [ '10001', '10002', '10003' ]
+ strings_displayed_after_submit = [ 'Changes made to the samples have been saved.' ]
+ strings_displayed_after_submit.extend( bar_codes )
+ self.add_bar_codes( cntrller='requests_admin',
+ request_id=self.security.encode_id( request1.id ),
+ bar_codes=bar_codes,
+ strings_displayed=[ 'Edit Current Samples of Request "%s"' % request1.name ],
+ strings_displayed_after_submit=strings_displayed_after_submit )
+ # the second sample state should appear once for each sample
+ strings_displayed_count = [ ( request1.type.states[1].name, len( request1.samples ) ),
+ ( request1.type.states[0].name, 0 ) ]
+ # check the request page
+ self.view_request( cntrller='requests_admin',
+ request_id=self.security.encode_id( request1.id ),
+ strings_displayed=bar_codes,
+ strings_displayed_count=strings_displayed_count )
+ # the sample state descriptions of the future states should not appear
+ # here the state names are not checked as all of them appear at the top of
+ # the page like: state1 > state2 > state3
+ strings_not_displayed=[ request1.type.states[2].desc,
+ request1.type.states[3].desc,
+ request1.type.states[4].desc ]
+ # check history of each sample
+ for sample in request1.samples:
+ strings_displayed = [ 'Events for Sample "%s"' % sample.name,
+ 'Request submitted and sample state set to %s' % request1.type.states[0].name,
+ request1.type.states[0].name,
+ request1.type.states[1].name ]
+ self.view_sample_history( cntrller='requests_admin',
+ sample_id=self.security.encode_id( sample.id ),
+ strings_displayed=strings_displayed,
+ strings_not_displayed=strings_not_displayed )
# def test_040_request_lifecycle( self ):
# """Testing request life-cycle as it goes through all the states"""
# # logged in as regular_user1
@@ -478,6 +666,17 @@ class TestFormsAndRequests( TwillTestCas
self.logout()
self.login( email=admin_user.email )
##################
+ # Purge all libraries
+ ##################
+ for library in [ library1, library2 ]:
+ self.delete_library_item( 'library_admin',
+ self.security.encode_id( library.id ),
+ self.security.encode_id( library.id ),
+ library.name,
+ item_type='library' )
+ self.purge_library( self.security.encode_id( library.id ), library.name )
+
+ ##################
# Delete request_type permissions
##################
for request_type in [ request_type1 ]:
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -861,16 +861,26 @@ class TwillTestCase( unittest.TestCase )
self.home()
# Functions associated with browsers, cookies, HTML forms and page visits
-
+
def check_page_for_string( self, patt ):
"""Looks for 'patt' in the current browser page"""
page = self.last_page()
- for subpatt in patt.split():
- if page.find( patt ) == -1:
- fname = self.write_temp_file( page )
- errmsg = "no match to '%s'\npage content written to '%s'" % ( patt, fname )
- raise AssertionError( errmsg )
-
+ if page.find( patt ) == -1:
+ fname = self.write_temp_file( page )
+ errmsg = "no match to '%s'\npage content written to '%s'" % ( patt, fname )
+ raise AssertionError( errmsg )
+
+ def check_string_count_in_page( self, patt, min_count ):
+ """Checks the number of 'patt' occurrences in the current browser page"""
+ page = self.last_page()
+ patt_count = page.count( patt )
+ # The number of occurrences of patt in the page should be at least min_count
+ # so show error if patt_count is less than min_count
+ if patt_count < min_count:
+ fname = self.write_temp_file( page )
+ errmsg = "%i occurrences of '%s' found instead of %i.\npage content written to '%s' " % ( min_count, patt, patt_count, fname )
+ raise AssertionError( errmsg )
+
def check_string_not_in_page( self, patt ):
"""Checks to make sure 'patt' is NOT in the page."""
page = self.last_page()
@@ -878,6 +888,16 @@ class TwillTestCase( unittest.TestCase )
fname = self.write_temp_file( page )
errmsg = "string (%s) incorrectly displayed in page.\npage content written to '%s'" % ( patt, fname )
raise AssertionError( errmsg )
+
+ def check_page(self, strings_displayed, strings_displayed_count, strings_not_displayed):
+ """Checks a page for strings displayed, not displayed and number of occurrences of a string"""
+ for check_str in strings_displayed:
+ self.check_page_for_string(check_str)
+ for check_str, count in strings_displayed_count:
+ self.check_string_count_in_page(check_str, count)
+ for check_str in strings_not_displayed:
+ self.check_string_not_in_page(check_str)
+
def write_temp_file( self, content, suffix='.html' ):
fd, fname = tempfile.mkstemp( suffix=suffix, prefix='twilltestcase-' )
@@ -1435,7 +1455,7 @@ class TwillTestCase( unittest.TestCase )
self.check_page_for_string( check_str )
self.home()
- # Requests stuff
+ # Sample tracking stuff
def check_request_grid( self, cntrller, state, deleted=False, strings_displayed=[] ):
self.visit_url( '%s/%s/browse_requests?sort=create_time&f-state=%s&f-deleted=%s' % \
( self.url, cntrller, state.replace( ' ', '+' ), str( deleted ) ) )
@@ -1510,24 +1530,20 @@ class TwillTestCase( unittest.TestCase )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
self.home()
- def view_request( self, cntrller, request_id, strings_displayed=[], strings_not_displayed=[] ):
+ def view_request( self, cntrller, request_id, strings_displayed=[], strings_displayed_count=[], strings_not_displayed=[] ):
self.visit_url( "%s/%s/browse_requests?operation=view_request&id=%s" % ( self.url, cntrller, request_id ) )
- for check_str in strings_displayed:
- self.check_page_for_string( check_str )
- for check_str in strings_not_displayed:
- self.check_string_not_in_page( check_str )
- def view_request_history( self, cntrller, request_id, strings_displayed=[], strings_not_displayed=[] ):
+ self.check_page( strings_displayed, strings_displayed_count, strings_not_displayed )
+ def view_request_history( self, cntrller, request_id, strings_displayed=[], strings_displayed_count=[], strings_not_displayed=[] ):
self.visit_url( "%s/requests_common/view_request_history?cntrller=%s&id=%s" % ( self.url, cntrller, request_id ) )
- for check_str in strings_displayed:
- self.check_page_for_string( check_str )
- for check_str in strings_not_displayed:
- self.check_string_not_in_page( check_str )
+ self.check_page( strings_displayed, strings_displayed_count, strings_not_displayed )
+ def view_sample_history( self, cntrller, sample_id, strings_displayed=[], strings_displayed_count=[], strings_not_displayed=[] ):
+ self.visit_url( "%s/requests_common/sample_events?cntrller=%s&sample_id=%s" % ( self.url, cntrller, sample_id ) )
+ self.check_page( strings_displayed, strings_displayed_count, strings_not_displayed )
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 )
if new_name:
tc.fv( "1", "name", new_name )
if new_desc:
@@ -1537,15 +1553,29 @@ class TwillTestCase( unittest.TestCase )
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=[] ):
+ def edit_request_email_settings( self, cntrller, request_id, check_request_owner=True, additional_emails='',
+ check_sample_states=[], 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 )
+ tc.fv( "2", "email_address", check_request_owner )
+ tc.fv( "2", "additional_email_addresses", additional_emails )
+ for state_name, state_id, is_checked in check_sample_states:
+ tc.fv( "2", "sample_state_%i" % state_id, is_checked )
+ tc.submit( "edit_email_settings_button" )
+ for check_str in strings_displayed_after_submit:
+ self.check_page_for_string( check_str )
+ def add_samples( self, cntrller, request_id, sample_value_tuples, strings_displayed=[], strings_displayed_after_submit=[] ):
url = "%s/requests_common/add_sample?cntrller=%s&request_id=%s&add_sample_button=Add+sample" % ( self.url, cntrller, request_id )
self.visit_url( url )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
- for sample_index, sample_info in enumerate( sample_value_tuples ):
- sample_name = sample_info[0]
- sample_field_values = sample_info[1]
+ for sample_index, ( sample_name, target_library_info, sample_field_values ) in enumerate( sample_value_tuples ):
tc.fv( "1", "sample_%i_name" % sample_index, sample_name )
+ lib_widget_index = sample_index + 1
+ tc.fv( "1", "sample_%i_library_id" % lib_widget_index, target_library_info[ 'library' ] )
+ self.refresh_form( "sample_%i_library_id" % lib_widget_index, target_library_info[ 'library' ] )
+ tc.fv( "1", "sample_%i_folder_id" % lib_widget_index, target_library_info[ 'folder' ] )
for field_index, field_value in enumerate( sample_field_values ):
tc.fv( "1", "sample_%i_field_%i" % ( sample_index, field_index ), field_value )
# Do not click on Add sample button when all the sample have been added
@@ -1556,6 +1586,32 @@ class TwillTestCase( unittest.TestCase )
tc.submit( "save_samples_button" )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
+ def edit_samples( self, cntrller, request_id, sample_value_tuples, strings_displayed=[], strings_displayed_after_submit=[] ):
+ url = "%s/requests_common/edit_samples?cntrller=%s&id=%s&editing_samples=True" % ( self.url, cntrller, request_id )
+ self.visit_url( url )
+ for check_str in strings_displayed:
+ self.check_page_for_string( check_str )
+ for sample_index, ( sample_name, target_library_info, sample_field_values ) in enumerate( sample_value_tuples ):
+ tc.fv( "1", "sample_%i_name" % sample_index, sample_name )
+ lib_widget_index = sample_index + 1
+ tc.fv( "1", "sample_%i_library_id" % lib_widget_index, target_library_info[ 'library' ] )
+ self.refresh_form( "sample_%i_library_id" % lib_widget_index, target_library_info[ 'library' ] )
+ tc.fv( "1", "sample_%i_folder_id" % lib_widget_index, target_library_info[ 'folder' ] )
+ for field_index, field_value in enumerate( sample_field_values ):
+ tc.fv( "1", "sample_%i_field_%i" % ( sample_index, field_index ), field_value )
+ tc.submit( "save_samples_button" )
+ for check_str in strings_displayed_after_submit:
+ self.check_page_for_string( check_str )
+ def add_bar_codes( self, cntrller, request_id, bar_codes, strings_displayed=[], strings_displayed_after_submit=[] ):
+ url = "%s/requests_common/edit_samples?cntrller=%s&id=%s&editing_samples=True" % ( self.url, cntrller, request_id )
+ self.visit_url( url )
+ for check_str in strings_displayed:
+ self.check_page_for_string( check_str )
+ for sample_index, bar_code in enumerate( bar_codes ):
+ tc.fv( "1", "sample_%i_bar_code" % sample_index, bar_code )
+ tc.submit( "save_samples_button" )
+ 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/requests_common/submit_request?cntrller=%s&id=%s" % ( self.url, cntrller, request_id ) )
for check_str in strings_displayed_after_submit:
@@ -1568,20 +1624,6 @@ class TwillTestCase( unittest.TestCase )
tc.submit( "reject_button" )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
- 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/edit_samples?cntrller=requests_admin&id=%s&editing_samples=True" % ( 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( ' ', '+' )
- field_name = "sample_%i_bar_code" % index
- url += "&%s=%s" % ( field_name, field_value )
- url += "&%s=%s" % ( sample_field_name, sample_field_value )
- url += "&save_samples_button=Save"
- 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_names, sample_ids, 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
--- a/templates/requests/common/edit_samples.mako
+++ b/templates/requests/common/edit_samples.mako
@@ -155,6 +155,8 @@
%elif editing_samples:
<p/><div class="form-row">
+ ## hidden element to make twill work.
+ <input type="hidden" name="hidden_input" value=""/><input type="submit" name="save_samples_button" value="Save"/><input type="submit" name="cancel_changes_button" value="Cancel"/><div class="toolParamHelp" style="clear: both;">
1
0
galaxy-dist commit b124f54952de: Small fixes and refactor to workflows: remove unused ensure_popup_helper(), and fix extra comma that was returning error in IE.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Kanwei Li <kanwei(a)gmail.com>
# Date 1289591135 18000
# Node ID b124f54952dee106f4ca87ea89a7f828c41562c3
# Parent b7f712cecaa9f527f1161f923ebe798eba526cb6
Small fixes and refactor to workflows: remove unused ensure_popup_helper(), and fix extra comma that was returning error in IE.
--- a/templates/workflow/editor.mako
+++ b/templates/workflow/editor.mako
@@ -13,7 +13,6 @@
ensure_dd_helper();
make_left_panel( $("#left"), $("#center"), $("#left-border" ) );
make_right_panel( $("#right"), $("#center"), $("#right-border" ) );
- ensure_popup_helper();
## handle_minwidth_hint = rp.handle_minwidth_hint;
</script></%def>
@@ -82,8 +81,7 @@
"${initial_text}": function() {
// Show/hide menu and update vars, user preferences.
var menu = $('#tool-search');
- if (menu.is(":visible"))
- {
+ if (menu.is(":visible")) {
// Hide menu.
pref_value = "False";
menu_option_text = "Search Tools";
@@ -91,9 +89,7 @@
// Reset search.
reset_tool_search(true);
- }
- else
- {
+ } else {
// Show menu.
pref_value = "True";
menu_option_text = "Hide Search";
@@ -158,18 +154,17 @@
var next = this_label.next();
var no_visible_tools = true;
// Look through tools following label and, if none are visible, hide label.
- while (next.length != 0 && next.hasClass("toolTitle"))
- {
- if (next.is(":visible"))
- {
+ while (next.length !== 0 && next.hasClass("toolTitle")) {
+ if (next.is(":visible")) {
no_visible_tools = false;
break;
+ } else {
+ next = next.next();
}
- else
- next = next.next();
}
- if (no_visible_tools)
+ if (no_visible_tools) {
this_label.hide();
+ }
});
} else {
$("#search-no-results").show();
@@ -211,9 +206,9 @@
scroll_to_nodes();
canvas_manager.draw_overview();
// Determine if any parameters were 'upgraded' and provide message
- upgrade_message = ""
- $.each( data['upgrade_messages'], function( k, v ) {
- upgrade_message += ( "<li>Step " + ( parseInt(k) + 1 ) + ": " + workflow.nodes[k].name + "<ul>");
+ upgrade_message = "";
+ $.each( data.upgrade_messages, function( k, v ) {
+ upgrade_message += ( "<li>Step " + ( parseInt(k, 10) + 1 ) + ": " + workflow.nodes[k].name + "<ul>");
$.each( v, function( i, vv ) {
upgrade_message += "<li>" + vv +"</li>";
});
@@ -256,7 +251,7 @@
"Layout": layout_editor,
"Save" : save_current_workflow,
##"Load a Workflow" : load_workflow,
- "Close": close_editor,
+ "Close": close_editor
});
function edit_workflow_outputs(){
@@ -297,21 +292,21 @@
workflow.has_changes = true;
});
$('#workflow-output-area').show();
- };
+ }
function layout_editor() {
workflow.layout();
workflow.fit_canvas_to_nodes();
scroll_to_nodes();
canvas_manager.draw_overview();
- };
+ }
function edit_workflow_attributes() {
workflow.clear_active_node();
$('.right-content').hide();
$('#edit-attributes').show();
- };
+ }
$.jStore.engineReady(function() {
// On load, set the size to the pref stored in local storage if it exists
@@ -354,7 +349,11 @@
// Lets the overview be toggled visible and invisible, adjusting the arrows accordingly
$("#close-viewport").click( function() {
- $("#overview-border").css("right") == "0px" ? hide_overview() : show_overview();
+ if ( $("#overview-border").css("right") === "0px" ) {
+ hide_overview();
+ } else {
+ show_overview();
+ }
});
// Unload handler
@@ -405,7 +404,7 @@
function scroll_to_nodes() {
var cv = $("#canvas-viewport");
- var cc = $("#canvas-container")
+ var cc = $("#canvas-container");
var top, left;
if ( cc.width() < cv.width() ) {
left = ( cv.width() - cc.width() ) / 2;
@@ -436,14 +435,14 @@
node.init_field_data( data );
},
error: function( x, e ) {
- var m = "error loading field data"
- if ( x.status == 0 ) {
- m += ", server unavailable"
+ var m = "error loading field data";
+ if ( x.status === 0 ) {
+ m += ", server unavailable";
}
node.error( m );
}
});
- };
+ }
function add_node_for_module( type, title ) {
node = prebuild_node( type, title );
@@ -466,7 +465,7 @@
node.error( m );
}
});
- };
+ }
<%
from galaxy.jobs.actions.post import ActionBox
@@ -500,12 +499,12 @@
}
function new_pja(action_type, target, node){
- if (node.post_job_actions == undefined){
+ if (node.post_job_actions === undefined){
//New tool node, set up dict.
node.post_job_actions = {};
}
- if (node.post_job_actions[action_type+target] == undefined){
- var new_pja = new Object();
+ if (node.post_job_actions[action_type+target] === undefined){
+ var new_pja = {};
new_pja.action_type = action_type;
new_pja.output_name = target;
node.post_job_actions[action_type+target] = null;
@@ -513,7 +512,7 @@
display_pja(new_pja, node);
workflow.active_form_has_changes = true;
return true;
- }else{
+ } else {
return false;
}
}
@@ -576,7 +575,7 @@
var value = $(this).attr( 'value' );
options[ $(this).text() ] = function() {
$(form).append( "<input type='hidden' name='"+name+"' value='"+value+"' />" ).submit();
- }
+ };
});
b.insertAfter( this );
$(this).remove();
@@ -613,9 +612,9 @@
"Don't Save": do_close
} );
} else {
- window.document.location = "${next_url}"
+ window.document.location = "${next_url}";
}
- }
+ };
var save_current_workflow = function ( eventObj, success_callback ) {
show_modal( "Saving workflow", "progress" );
@@ -634,14 +633,14 @@
type: "POST",
data: {
id: "${trans.security.encode_id( stored.id )}",
- workflow_data: function() { return JSON.stringify( workflow.to_simple() ) },
+ workflow_data: function() { return JSON.stringify( workflow.to_simple() ); },
"_": "true"
},
dataType: 'json',
success: function( data ) {
var body = $("<div></div>").text( data.message );
if ( data.errors ) {
- body.addClass( "warningmark" )
+ body.addClass( "warningmark" );
var errlist = $( "<ul/>" );
$.each( data.errors, function( i, v ) {
$("<li></li>").text( v ).appendTo( errlist );
@@ -663,7 +662,7 @@
}
}
});
- }
+ };
// We bind to ajaxStop because of auto-saving, since the form submission ajax
// call needs to be completed so that the new data is saved
@@ -677,7 +676,7 @@
} else {
savefn(success_callback);
}
- }
+ };
</script></%def>
1
0
galaxy-dist commit fc1f67023d86: Fixes for building target library and folder SelectFields for transferring sample datasets. All folders are now present in the target folder SelectField. Move security-related methods from the model's User class to the security agent where they belong.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Greg Von Kuster <greg(a)bx.psu.edu>
# Date 1289574787 18000
# Node ID fc1f67023d86a69ea7229501b093d61748630f4c
# Parent dc4af741ffc455c4737aff67c7d7de48c8628392
Fixes for building target library and folder SelectFields for transferring sample datasets. All folders are now present in the target folder SelectField. Move security-related methods from the model's User class to the security agent where they belong.
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -52,40 +52,6 @@ class User( object ):
if role not in roles:
roles.append( role )
return roles
- def accessible_libraries( self, trans, actions ):
- # TODO: eliminate this method - instead use
- # trans.app.security_agent.get_accessible_libraries().
- # 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 )
- roles = self.all_roles()
- actions_to_check = actions
- # The libraries dictionary looks like: { library : '1,2' }, library : '3' }
- # Its keys are the libraries that should be displayed for the current user and whose values are a
- # string of comma-separated folder ids, of the associated folders the should NOT be displayed.
- # The folders that should not be displayed may not be a complete list, but it is ultimately passed
- # to the calling method to keep from re-checking the same folders when the library / folder
- # select lists are rendered.
- libraries = {}
- for library in all_libraries:
- can_show, hidden_folder_ids = trans.app.security_agent.show_library_item( self, roles, library, actions_to_check )
- if can_show:
- libraries[ library ] = hidden_folder_ids
- return libraries
- 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 ):
"""
--- a/lib/galaxy/web/controllers/requests.py
+++ b/lib/galaxy/web/controllers/requests.py
@@ -77,7 +77,8 @@ class Requests( BaseController ):
kwd[ 'message' ] = message
# Allow the user to create a new request only if they have permission to access a
# (sequencer configuration) request type.
- if len( trans.user.accessible_request_types( trans ) ):
+ accessible_request_types = trans.app.security_agent.get_accessible_request_types( trans, trans.user )
+ if accessible_request_types:
self.request_grid.global_actions = [ grids.GridAction( "Create new request", dict( controller='requests_common',
action='create_request',
cntrller='requests' ) ) ]
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -414,7 +414,7 @@ class RequestsCommon( BaseController, Us
cntrller=cntrller,
id=request_id,
editing_samples=editing_samples ) )
- libraries = self.__get_accessible_libraries( trans, request.user )
+ libraries = trans.app.security_agent.get_accessible_libraries( trans, request.user )
# Build a list of sample widgets (based on the attributes of each sample) for display.
displayable_sample_widgets = self.__get_sample_widgets( trans, request, request.samples, **kwd )
encoded_selected_sample_ids = self.__get_encoded_selected_sample_ids( trans, request, **kwd )
@@ -815,7 +815,7 @@ class RequestsCommon( BaseController, Us
else:
sample_index = len( displayable_sample_widgets )
if params.get( 'add_sample_button', False ):
- libraries = self.__get_accessible_libraries( trans, request.user )
+ libraries = trans.app.security_agent.get_accessible_libraries( trans, request.user )
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 ) )
@@ -1214,20 +1214,14 @@ class RequestsCommon( BaseController, Us
else:
folder = None
return library, folder
+ def __get_active_folders( self, library ):
+ """Return all of the active folders for the received library"""
+ root_folder = library.root_folder
+ active_folders = [ root_folder ]
+ for folder in root_folder.active_folders:
+ active_folders.append( folder )
+ return active_folders
# ===== Methods for handling form definition widgets =====
- def __get_accessible_libraries( self, trans, user ):
- # Return a dictionary whose keys are libraries that user can
- # access and whose values are empty string ''. This is because
- # methods expect the dictionary instead of a simple list because
- # this method replaces the deprecated model.User.accessible_libraries()
- # method. TODO: fix methods that call this method to expect the list
- # returne dby trans.app.securoty_agent.get_accessible_libraries() and
- # then eliminate this method.
- accessible_libraries = trans.app.security_agent.get_accessible_libraries( trans, user )
- accessible_libraries_dict = odict()
- for library in accessible_libraries:
- accessible_libraries_dict[ library ] = ''
- return accessible_libraries_dict
def __get_request_widgets( self, trans, id ):
"""Get the widgets for the request"""
request = trans.sa_session.query( trans.model.Request ).get( id )
@@ -1275,7 +1269,7 @@ class RequestsCommon( BaseController, Us
# Build the list of widgets which will be used to render each sample row on the request page
if not request:
return sample_widgets
- libraries = self.__get_accessible_libraries( trans, request.user )
+ libraries = trans.app.security_agent.get_accessible_libraries( trans, request.user )
# Build the list if sample widgets, populating the values from kwd.
for index, sample in enumerate( samples ):
id_index = index + 1
@@ -1361,7 +1355,7 @@ class RequestsCommon( BaseController, Us
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 )
+ accessible_request_types = trans.app.security_agent.get_accessible_request_types( trans, trans.user )
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 ) \
@@ -1388,11 +1382,9 @@ class RequestsCommon( BaseController, Us
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.
+ # existing library then display all the folders of the selected library in the folder_select_field. Library folders do
+ # not have ACCESS permissions associated with them (only LIBRARY_ADD, LIBRARY_MODIFY, LIBRARY_MANAGE), so all folders will
+ # be present in the folder_select_field for each library selected.
params = util.Params( kwd )
library_select_field_name= "sample_%i_library_id" % sample_index
folder_select_field_name = "sample_%i_folder_id" % sample_index
@@ -1401,15 +1393,11 @@ class RequestsCommon( BaseController, Us
if not folder_id:
folder_id = params.get( folder_select_field_name, None )
selected_library = None
- selected_hidden_folder_ids = []
- showable_folders = []
if library_id not in [ None, 'none' ]:
- # 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():
+ for library in libraries:
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
elif sample and sample.library and library_id == 'none':
# The user previously selected a library but is now resetting the selection to 'none'
@@ -1417,21 +1405,17 @@ class RequestsCommon( BaseController, Us
elif sample and sample.library:
library_id = trans.security.encode_id( sample.library.id )
selected_library = sample.library
- # sample_%i_library_id SelectField with refresh on change enabled
+ # Build the sample_%i_library_id SelectField with refresh on change enabled
library_select_field = build_select_field( trans,
- libraries.keys(),
+ libraries,
'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
+ # Get all 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 )
+ folders = self.__get_active_folders( selected_library )
if folder_id:
selected_folder_id = folder_id
elif sample and sample.folder:
@@ -1440,11 +1424,12 @@ class RequestsCommon( BaseController, Us
selected_folder_id = trans.security.encode_id( selected_library.root_folder.id )
else:
selected_folder_id = 'none'
+ folders = []
# 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,
+ folders,
'name',
folder_select_field_name,
initial_value='none',
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -84,7 +84,7 @@
[ '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 or trans.user.accessible_request_types( trans ) ) ) )
+ tab( "lab", "Lab", None, menu_options=menu_options, visible=( trans.user and ( trans.user.requests or trans.app.security_agent.get_accessible_request_types( trans, trans.user ) ) ) )
%>
## Visualization menu.
--- a/lib/galaxy/security/__init__.py
+++ b/lib/galaxy/security/__init__.py
@@ -60,6 +60,8 @@ class RBACAgent:
raise "Unimplemented Method"
def get_private_user_role( self, user ):
raise "Unimplemented Method"
+ def get_accessible_request_types( self, trans, user ):
+ raise "Unimplemented Method"
def user_set_default_permissions( self, user, permissions={}, history=False, dataset=False ):
raise "Unimplemented Method"
def history_set_default_permissions( self, history, permissions=None, dataset=False, bypass_manage_permission=False ):
@@ -76,6 +78,8 @@ class RBACAgent:
raise "Unimplemented Method"
def get_accessible_libraries( self, trans, user ):
raise "Unimplemented Method"
+ def get_permitted_libraries( self, trans, user, actions ):
+ raise "Unimplemented Method"
def folder_is_public( self, library ):
raise "Unimplemented Method"
def make_folder_public( self, folder, count=0 ):
@@ -247,7 +251,7 @@ class GalaxyRBACAgent( RBACAgent ):
def can_access_library( self, roles, library ):
return self.library_is_public( library ) or self.allow_action( roles, self.permitted_actions.LIBRARY_ACCESS, library )
def get_accessible_libraries( self, trans, user ):
- """Return all data libraries that user can access"""
+ """Return all data libraries that the received user can access"""
accessible_libraries = []
current_user_role_ids = [ role.id for role in user.all_roles() ]
library_access_action = self.permitted_actions.LIBRARY_ACCESS.action
@@ -499,6 +503,20 @@ class GalaxyRBACAgent( RBACAgent ):
else:
permissions[ action ] = [ item_permission.role ]
return permissions
+ def get_accessible_request_types( self, trans, user ):
+ """Return all ReqquestTypes that the received user has permission to access."""
+ 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 the received user
+ role_ids = [ r.id for r in user.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
def copy_dataset_permissions( self, src, dst ):
if not isinstance( src, self.model.Dataset ):
src = src.dataset
@@ -731,6 +749,34 @@ class GalaxyRBACAgent( RBACAgent ):
else:
raise 'Invalid class (%s) specified for target_library_item (%s)' % \
( target_library_item.__class__, target_library_item.__class__.__name__ )
+ def get_permitted_libraries( self, trans, user, actions ):
+ """
+ This method is historical (it is not currently used), but may be useful again at some
+ point. It returns a dictionary whose keys are library objects and whose values are a
+ comma-separated string of folder ids. This method works with the show_library_item()
+ method below, and it returns libraries for which the received user has permission to
+ perform the received actions. Here is an example call to this method to return all
+ libraries for which the received user has LIBRARY_ADD permission:
+ libraries = trans.app.security_agent.get_permitted_libraries( trans, user,
+ [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
+ """
+ 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 )
+ roles = user.all_roles()
+ actions_to_check = actions
+ # The libraries dictionary looks like: { library : '1,2' }, library : '3' }
+ # Its keys are the libraries that should be displayed for the current user and whose values are a
+ # string of comma-separated folder ids, of the associated folders the should NOT be displayed.
+ # The folders that should not be displayed may not be a complete list, but it is ultimately passed
+ # to the calling method to keep from re-checking the same folders when the library / folder
+ # select lists are rendered.
+ libraries = {}
+ for library in all_libraries:
+ can_show, hidden_folder_ids = self.show_library_item( self, roles, library, actions_to_check )
+ if can_show:
+ libraries[ library ] = hidden_folder_ids
+ return libraries
def show_library_item( self, user, roles, library_item, actions_to_check, hidden_folder_ids='' ):
"""
This method must be sent an instance of Library() or LibraryFolder(). Recursive execution produces a
1
0
galaxy-dist commit dc4af741ffc4: Fixed missing substitution of get_id_tag for a galaxy_job_id in drmaa runner.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1289573664 18000
# Node ID dc4af741ffc455c4737aff67c7d7de48c8628392
# Parent dbe6ab1eaa7ca7f9754c22bb2f6edd3fc26e3432
Fixed missing substitution of get_id_tag for a galaxy_job_id in drmaa runner.
--- a/lib/galaxy/jobs/runners/drmaa.py
+++ b/lib/galaxy/jobs/runners/drmaa.py
@@ -175,7 +175,7 @@ class DRMAAJobRunner( BaseJobRunner ):
log.debug("(%s) command is: %s" % ( galaxy_id_tag, command_line ) )
# runJob will raise if there's a submit problem
job_id = self.ds.runJob(jt)
- log.info("(%s) queued as %s" % ( galaxy_job_id, job_id ) )
+ log.info("(%s) queued as %s" % ( galaxy_id_tag, job_id ) )
# store runner information for tracking if Galaxy restarts
job_wrapper.set_runner( runner_url, job_id )
1
0
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Dannon Baker <dannonbaker(a)me.com>
# Date 1289568520 18000
# Node ID ba34d996fff5669eca98ff98c6035a68ac93bd34
# Parent 2389c323a1e6a50de6d48d5431d16d830ccef021
# Parent a6cd0223a53b4f9498a0dde69dcef53387188e5f
Merge.
--- a/templates/requests/common/events.mako
+++ /dev/null
@@ -1,53 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-
-<%
- is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
- can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
- can_reject_request = is_admin and request.is_submitted
- can_add_samples = request.is_unsubmitted
-%>
-
-<br/><br/>
-<ul class="manage-table-actions">
- <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request Actions</a></li>
- <div popupmenu="request-${request.id}-popup">
- <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
- %if can_edit_request:
- <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit this request</a>
- %endif
- %if can_add_samples:
- <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit this request</a>
- %endif
- %if can_reject_request:
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject this request</a>
- %endif
- </div>
-</ul>
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-<h2>History of Sequencing Request "${request.name}"</h2>
-
-<div class="toolForm">
- <table class="grid">
- <thead>
- <tr>
- <th>State</th>
- <th>Last Update</th>
- <th>Comments</th>
- </tr>
- </thead>
- <tbody>
- %for state, updated, comments in events_list:
- <tr class="libraryRow libraryOrFolderRow" id="libraryRow">
- <td><b><a>${state}</a></b></td>
- <td><a>${updated}</a></td>
- <td><a>${comments}</a></td>
- </tr>
- %endfor
- </tbody>
- </table>
-</div>
--- a/tools/ncbi_blast_plus/blast_filter_fasta.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env python
-"""Filter a FASTA file using tabular output, e.g. from BLAST.
-
-Takes five command line options, tabular BLAST filename, ID column number
-(using one based counting), input FASTA filename, and two output FASTA
-filenames (for records with and without any BLAST hits).
-
-In the default NCBI BLAST+ tabular output, the query sequence ID is in column
-one, and the ID of the match from the database is in column two.
-"""
-import sys
-from galaxy_utils.sequence.fasta import fastaReader, fastaWriter
-
-#Parse Command Line
-blast_file, blast_col, in_file, out_positive_file, out_negative_file = sys.argv[1:]
-blast_col = int(blast_col)-1
-assert blast_col >= 0
-
-#Read tabular BLAST file and record all queries with hit(s)
-ids = set()
-blast_handle = open(blast_file, "rU")
-for line in blast_handle:
- ids.add(line.split("\t")[blast_col])
-blast_handle.close()
-
-#Write filtered FASTA file based on IDs from BLAST file
-reader = fastaReader(open(in_file, "rU"))
-positive_writer = fastaWriter(open(out_positive_file, "w"))
-negative_writer = fastaWriter(open(out_negative_file, "w"))
-for record in reader:
- #The [1:] is because the fastaReader leaves the > on the identifer.
- if record.identifier and record.identifier.split()[0][1:] in ids:
- positive_writer.write(record)
- else:
- negative_writer.write(record)
-positive_writer.close()
-negative_writer.close()
-reader.close()
--- a/tools/ncbi_blast_plus/blast_filter_fasta.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<tool id="blast_filter_fasta" name="Filter FASTA using BLAST output" version="0.0.1">
- <description>Divide a FASTA file based on BLAST hits</description>
- <command interpreter="python">
- blast_filter_fasta.py $blast_file $blast_col $in_file $out_positive_file $out_negative_file
- </command>
- <inputs>
- <param name="in_file" type="data" format="fasta" label="FASTA file to filter"/>
- <param name="blast_file" type="data" format="tabular" label="Tabular BLAST output"/>
- <param name="blast_col" type="select" label="Column containing FASTA identifiers">
- <option value="1">Column 1 - BLAST query ID</option>
- <option value="2">Column 2 - BLAST match ID</option>
- </param>
- </inputs>
- <outputs>
- <data name="out_positive_file" format="fasta" label="Sequences with BLAST hits" />
- <data name="out_negative_file" format="fasta" label="Sequences without BLAST hits" />
- </outputs>
- <requirements>
- </requirements>
- <tests>
- </tests>
- <help>
-
-**What it does**
-
-Typical use would be to take a multi-sequence FASTA and the tabular output of
-running BLAST on it, and divide the FASTA file in two: those sequence with a
-BLAST hit, and those without.
-
-In the default NCBI BLAST+ tabular output, the query sequence ID is in column
-one, and the ID of the match from the database is in column two.
-
-This allows you to filter the FASTA file for the subjects in the BLAST search,
-rather than filtering the FASTA file for the queries in the BLAST search.
-
- </help>
-</tool>
1
0
# 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 1289568820 18000
# Node ID dbe6ab1eaa7ca7f9754c22bb2f6edd3fc26e3432
# Parent b5b6b92a71e960691bb514af32ba2e21a731bd4e
# Parent ba34d996fff5669eca98ff98c6035a68ac93bd34
merge
1
0
galaxy-dist commit ecc1cefbccba: Enhance GFFReader to read and return complete features (genes/transcripts), which are composed of multiple intervals/blocks. This required hacking around a couple limitations of bx-python, but these can be easily fixed when bx-python is updated. Use enhanced functionality to correctly create converted datasets for visualizing GFF files.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User jeremy goecks <jeremy.goecks(a)emory.edu>
# Date 1289591537 18000
# Node ID ecc1cefbccba6135f824f9573f06c130fd6d08b4
# Parent b124f54952dee106f4ca87ea89a7f828c41562c3
Enhance GFFReader to read and return complete features (genes/transcripts), which are composed of multiple intervals/blocks. This required hacking around a couple limitations of bx-python, but these can be easily fixed when bx-python is updated. Use enhanced functionality to correctly create converted datasets for visualizing GFF files.
Also updated GOPS tools to use a simpler GFF reader wrapper to read in intervals and convert to BED coordinates.
--- a/tools/new_operations/gops_subtract.py
+++ b/tools/new_operations/gops_subtract.py
@@ -44,11 +44,11 @@ def main():
# Set readers to handle either GFF or default format.
if in1_gff_format:
- in1_reader_wrapper = GFFReaderWrapper
+ in1_reader_wrapper = GFFIntervalToBEDReaderWrapper
else:
in1_reader_wrapper = NiceReaderWrapper
if in2_gff_format:
- in2_reader_wrapper = GFFReaderWrapper
+ in2_reader_wrapper = GFFIntervalToBEDReaderWrapper
else:
in2_reader_wrapper = NiceReaderWrapper
--- a/tools/new_operations/gops_intersect.py
+++ b/tools/new_operations/gops_intersect.py
@@ -44,11 +44,11 @@ def main():
# Set readers to handle either GFF or default format.
if in1_gff_format:
- in1_reader_wrapper = GFFReaderWrapper
+ in1_reader_wrapper = GFFIntervalToBEDReaderWrapper
else:
in1_reader_wrapper = NiceReaderWrapper
if in2_gff_format:
- in2_reader_wrapper = GFFReaderWrapper
+ in2_reader_wrapper = GFFIntervalToBEDReaderWrapper
else:
in2_reader_wrapper = NiceReaderWrapper
@@ -66,10 +66,10 @@ def main():
fix_strand=True )
out_file = open( out_fname, "w" )
-
+
try:
for line in intersect( [g1,g2], pieces=pieces, mincols=mincols ):
- if type( line ) == GenomicInterval:
+ if isinstance( line, GenomicInterval ):
if in1_gff_format:
line = convert_bed_coords_to_gff( line )
out_file.write( "%s\n" % "\t".join( line.fields ) )
--- /dev/null
+++ b/lib/galaxy/datatypes/converters/gff_to_interval_index_converter.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+
+"""
+Convert from GFF file to interval index file.
+
+usage:
+ python gff_to_interval_index_converter.py [input] [output]
+"""
+
+from __future__ import division
+
+import sys, fileinput
+from galaxy import eggs
+import pkg_resources; pkg_resources.require( "bx-python" )
+from galaxy.tools.util.gff_util import *
+from bx.interval_index_file import Indexes
+
+def main():
+ # Arguments
+ input_fname, out_fname = sys.argv[1:]
+
+ # Do conversion.
+ chr_col, start_col, end_col, strand_col = ( 0, 3, 4, 6 )
+ index = Indexes()
+ offset = 0
+ reader_wrapper = GFFReaderWrapper( fileinput.FileInput( input_fname ),
+ chrom_col=chr_col,
+ start_col=start_col,
+ end_col=end_col,
+ strand_col=strand_col,
+ fix_strand=True )
+ for feature in list( reader_wrapper ):
+ # TODO: need to address comments:
+ # if comment:
+ # increment_offset.
+
+ # Add feature; index expects BED coordinates.
+ convert_gff_coords_to_bed( feature )
+ index.add( feature.chrom, feature.start, feature.end, offset )
+
+ # Increment offset by feature length; feature length is all
+ # intervals/lines that comprise feature.
+ feature_len = 0
+ for interval in feature.intervals:
+ # HACK: +1 for EOL char. Need bx-python to provide raw_line itself
+ # b/c TableReader strips EOL characters, thus changing the line
+ # length.
+ feature_len += len( interval.raw_line ) + 1
+ offset += feature_len
+
+ index.write( open(out_fname, "w") )
+
+if __name__ == "__main__":
+ main()
+
--- a/lib/galaxy/datatypes/converters/interval_to_summary_tree_converter.py
+++ b/lib/galaxy/datatypes/converters/interval_to_summary_tree_converter.py
@@ -14,7 +14,7 @@ import pkg_resources; pkg_resources.requ
from galaxy.visualization.tracks.summary import *
from bx.intervals.io import *
from bx.cookbook import doc_optparse
-from galaxy.tools.util.gff_util import GFFReaderWrapper
+from galaxy.tools.util.gff_util import *
def main():
# Read options, args.
@@ -40,9 +40,12 @@ def main():
strand_col=strand_col,
fix_strand=True )
st = SummaryTree(block_size=25, levels=6, draw_cutoff=150, detail_cutoff=30)
- for line in list( reader_wrapper ):
- if type( line ) is GenomicInterval:
- st.insert_range( line.chrom, long( line.start ), long( line.end ) )
+ for feature in list( reader_wrapper ):
+ if isinstance( feature, GenomicInterval ):
+ # Tree expects BED coordinates.
+ if type( feature ) is GFFFeature:
+ convert_gff_coords_to_bed( feature )
+ st.insert_range( feature.chrom, long( feature.start ), long( feature.end ) )
st.write(out_fname)
--- a/lib/galaxy/datatypes/converters/gff_to_interval_index_converter.xml
+++ b/lib/galaxy/datatypes/converters/gff_to_interval_index_converter.xml
@@ -1,6 +1,6 @@
-<tool id="CONVERTER_gff_to_interval_index_0" name="Convert BED to Interval Index" version="1.0.0" hidden="true">
+<tool id="CONVERTER_gff_to_interval_index_0" name="Convert GFF to Interval Index" version="1.0.0" hidden="true"><!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> -->
- <command interpreter="python">interval_to_interval_index_converter.py $input1 $output1 --gff</command>
+ <command interpreter="python">gff_to_interval_index_converter.py $input1 $output1</command><inputs><page><param format="gff" name="input1" type="data" label="Choose GFF file"/>
--- a/tools/new_operations/flanking_features.py
+++ b/tools/new_operations/flanking_features.py
@@ -153,11 +153,11 @@ def main():
# Set readers to handle either GFF or default format.
if in1_gff_format:
- in1_reader_wrapper = GFFReaderWrapper
+ in1_reader_wrapper = GFFIntervalToBEDReaderWrapper
else:
in1_reader_wrapper = NiceReaderWrapper
if in2_gff_format:
- in2_reader_wrapper = GFFReaderWrapper
+ in2_reader_wrapper = GFFIntervalToBEDReaderWrapper
else:
in2_reader_wrapper = NiceReaderWrapper
--- a/lib/galaxy/tools/util/gff_util.py
+++ b/lib/galaxy/tools/util/gff_util.py
@@ -2,25 +2,173 @@
Provides utilities for working with GFF files.
"""
-from bx.intervals.io import NiceReaderWrapper, GenomicInterval
+from bx.intervals.io import *
+
+class GFFInterval( GenomicInterval ):
+ """
+ A GFF interval, including attributes. If file is strictly a GFF file,
+ only attribute is 'group.'
+ """
+ def __init__( self, reader, fields, chrom_col, start_col, end_col, strand_col, default_strand, \
+ fix_strand=False, raw_line='' ):
+ GenomicInterval.__init__( self, reader, fields, chrom_col, start_col, end_col, strand_col, \
+ default_strand, fix_strand=fix_strand )
+ self.raw_line = raw_line
+ self.attributes = parse_gff_attributes( fields[8] )
+
+class GFFFeature( GenomicInterval ):
+ """
+ A GFF feature, which can include multiple intervals.
+ """
+ def __init__( self, reader, chrom_col, start_col, end_col, strand_col, default_strand, \
+ fix_strand=False, intervals=[] ):
+ GenomicInterval.__init__( self, reader, intervals[0].fields, chrom_col, start_col, end_col, \
+ strand_col, default_strand, fix_strand=fix_strand )
+ self.intervals = intervals
+ # Use intervals to set feature attributes.
+ for interval in self.intervals:
+ # Error checking.
+ if interval.chrom != self.chrom:
+ raise ValueError( "interval chrom does not match self chrom: %i != %i" % \
+ ( interval.chrom, self.chrom ) )
+ if interval.strand != self.strand:
+ raise ValueError( "interval strand does not match self strand: %s != %s" % \
+ ( interval.strand, self.strand ) )
+ # Set start, end of interval.
+ if interval.start < self.start:
+ self.start = interval.start
+ if interval.end > self.end:
+ self.end = interval.end
+
+class GFFIntervalToBEDReaderWrapper( NiceReaderWrapper ):
+ """
+ Reader wrapper that reads GFF intervals/lines and automatically converts
+ them to BED format.
+ """
+
+ def parse_row( self, line ):
+ # HACK: this should return a GFF interval, but bx-python operations
+ # require GenomicInterval objects and subclasses will not work.
+ interval = GenomicInterval( self, line.split( "\t" ), self.chrom_col, self.start_col, \
+ self.end_col, self.strand_col, self.default_strand, \
+ fix_strand=self.fix_strand )
+ interval = convert_gff_coords_to_bed( interval )
+ return interval
class GFFReaderWrapper( NiceReaderWrapper ):
"""
- Reader wrapper converts GFF format--starting and ending coordinates are 1-based, closed--to the
- 'traditional'/BED interval format--0 based, half-open. This is useful when using GFF files as inputs
- to tools that expect traditional interval format.
+ Reader wrapper for GFF files.
+
+ Wrapper has two major functions:
+ (1) group entries for GFF file (via group column), GFF3 (via id attribute ),
+ or GTF (via gene_id/transcript id);
+ (2) convert coordinates from GFF format--starting and ending coordinates
+ are 1-based, closed--to the 'traditional'/BED interval format--0 based,
+ half-open. This is useful when using GFF files as inputs to tools that
+ expect traditional interval format.
"""
+
+ def __init__( self, reader, **kwargs ):
+ """
+ Create wrapper. Defaults are group_entries=False and
+ convert_coords_to_bed=True to support backward compatibility.
+ """
+ NiceReaderWrapper.__init__( self, reader, **kwargs )
+ self.group_entries = kwargs.get( 'group_entries', False )
+ self.convert_coords_to_bed = kwargs.get( 'convert_coords_to_bed', True )
+ self.last_line = None
+ self.cur_offset = 0
+ self.seed_interval = None
+
def parse_row( self, line ):
- interval = GenomicInterval( self, line.split( "\t" ), self.chrom_col, self.start_col, self.end_col, \
- self.strand_col, self.default_strand, fix_strand=self.fix_strand )
- interval = convert_gff_coords_to_bed( interval )
+ interval = GFFInterval( self, line.split( "\t" ), self.chrom_col, self.start_col, \
+ self.end_col, self.strand_col, self.default_strand, \
+ fix_strand=self.fix_strand, raw_line=line )
+ if self.convert_coords_to_bed:
+ interval = convert_gff_coords_to_bed( interval )
return interval
+ def next( self ):
+ """ Returns next GFFFeature. """
+
+ #
+ # Helper function.
+ #
+
+ def handle_parse_error( parse_error ):
+ """ Actions to take when ParseError found. """
+ if self.outstream:
+ if self.print_delegate and hasattr(self.print_delegate,"__call__"):
+ self.print_delegate( self.outstream, e, self )
+ self.skipped += 1
+ # no reason to stuff an entire bad file into memmory
+ if self.skipped < 10:
+ self.skipped_lines.append( ( self.linenum, self.current_line, str( e ) ) )
+
+ #
+ # Get next GFFFeature
+ #
+
+ # If there is no seed interval, set one. Also, if there are no more
+ # intervals to read, this is where iterator dies.
+ if not self.seed_interval:
+ while not self.seed_interval:
+ try:
+ self.seed_interval = GenomicIntervalReader.next( self )
+ except ParseError, e:
+ handle_parse_error( e )
+
+ # Initialize feature name from seed.
+ feature_group = self.seed_interval.attributes.get( 'group', None ) # For GFF
+ feature_id = self.seed_interval.attributes.get( 'id', None ) # For GFF3
+ feature_gene_id = self.seed_interval.attributes.get( 'gene_id', None ) # For GTF
+ feature_transcript_id = self.seed_interval.attributes.get( 'transcript_id', None ) # For GTF
+
+ # Read all intervals associated with seed.
+ feature_intervals = []
+ feature_intervals.append( self.seed_interval )
+ while True:
+ try:
+ interval = GenomicIntervalReader.next( self )
+ except StopIteration, e:
+ # No more intervals to read, but last feature needs to be
+ # returned.
+ interval = None
+ break
+ except ParseError, e:
+ handle_parse_error( e )
+
+ # If interval not associated with feature, break.
+ group = interval.attributes.get( 'group', None )
+ if group and feature_group != group:
+ break
+ id = interval.attributes.get( 'id', None )
+ if id and feature_id != id:
+ break
+ gene_id = interval.attributes.get( 'gene_id', None )
+ transcript_id = interval.attributes.get( 'transcript_id', None )
+ if transcript_id and transcript_id != feature_transcript_id and gene_id and \
+ gene_id != feature_gene_id:
+ break
+
+ # Interval associated with feature.
+ feature_intervals.append( interval )
+
+ # Last interval read is the seed for the next interval.
+ self.seed_interval = interval
+
+ # Return GFF feature with all intervals.
+ return GFFFeature( self, self.chrom_col, self.start_col, self.end_col, self.strand_col, \
+ self.default_strand, fix_strand=self.fix_strand, \
+ intervals=feature_intervals )
+
+
def convert_bed_coords_to_gff( interval ):
"""
- Converts an interval object's coordinates from BED format to GFF format. Accepted object types include
- GenomicInterval and list (where the first element in the list is the interval's start, and the second
- element is the interval's end).
+ Converts an interval object's coordinates from BED format to GFF format.
+ Accepted object types include GenomicInterval and list (where the first
+ element in the list is the interval's start, and the second element is
+ the interval's end).
"""
if type( interval ) is GenomicInterval:
interval.start += 1
@@ -30,9 +178,10 @@ def convert_bed_coords_to_gff( interval
def convert_gff_coords_to_bed( interval ):
"""
- Converts an interval object's coordinates from GFF format to BED format. Accepted object types include
- GenomicInterval and list (where the first element in the list is the interval's start, and the second
- element is the interval's end).
+ Converts an interval object's coordinates from GFF format to BED format.
+ Accepted object types include GenomicInterval and list (where the first
+ element in the list is the interval's start, and the second element is
+ the interval's end).
"""
if type( interval ) is GenomicInterval:
interval.start -= 1
@@ -42,10 +191,15 @@ def convert_gff_coords_to_bed( interval
def parse_gff_attributes( attr_str ):
"""
- Parses a GFF/GTF attribute string and returns a dictionary of name-value pairs.
- The general format for a GFF3 attributes string is name1=value1;name2=value2
- The general format for a GTF attribute string is name1 "value1" ; name2 "value2"
- """
+ Parses a GFF/GTF attribute string and returns a dictionary of name-value
+ pairs. The general format for a GFF3 attributes string is
+ name1=value1;name2=value2
+ The general format for a GTF attribute string is
+ name1 "value1" ; name2 "value2"
+ The general format for a GFF attribute string is a single string that
+ denotes the interval's group; in this case, method returns a dictionary
+ with a single key-value pair, and key name is 'group'
+ """
attributes_list = attr_str.split(";")
attributes = {}
for name_value_pair in attributes_list:
@@ -53,6 +207,9 @@ def parse_gff_attributes( attr_str ):
pair = name_value_pair.strip().split(" ")
if len( pair ) == 1:
pair = name_value_pair.strip().split("=")
+ if len( pair ) == 1:
+ # Could not split for some reason -- raise exception?
+ continue
if pair == '':
continue
name = pair[0].strip()
@@ -61,4 +218,9 @@ def parse_gff_attributes( attr_str ):
# Need to strip double quote from values
value = pair[1].strip(" \"")
attributes[ name ] = value
+
+ if len( attributes ) == 0:
+ # Could not split attributes string, so entire string must be
+ # 'group' attribute. This is the case for strictly GFF files.
+ attributes['group'] = attr_str
return attributes
1
0
galaxy-dist commit 10d0ffe5b7e2: Include BLAST-XML to tabular in tool_conf.xml.sample
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 1286962559 -3600
# Node ID 10d0ffe5b7e22b2d122945ab5b7fa951a18c6e4a
# Parent caed55b53f5a811226e0bfa1c177e7b3e57a909a
Include BLAST-XML to tabular in tool_conf.xml.sample
--- a/tool_conf.xml.sample
+++ b/tool_conf.xml.sample
@@ -268,6 +268,7 @@
<tool file="ncbi_blast_plus/ncbi_blastx_wrapper.xml" /><tool file="ncbi_blast_plus/ncbi_tblastn_wrapper.xml" /><tool file="ncbi_blast_plus/ncbi_tblastx_wrapper.xml" />
+ <tool file="ncbi_blast_plus/blastxml_to_tabular.xml" /><tool file="ncbi_blast_plus/blast_filter_fasta.xml" /></section><section name="NGS: Mapping" id="solexa_tools">
1
0
galaxy-dist commit dfc848840870: Various sample tracking bug fixes: samples no longer require bar codes, change all references to 'barcode' to be 'bar_code' since that is what the model uses, and mixing the 2 is not maintainable, enhance sample popup menu options, fix broken logic that handles bulk sample library and folderr changes.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Greg Von Kuster <greg(a)bx.psu.edu>
# Date 1289503442 18000
# Node ID dfc848840870fa9936a69e62322446fcb940fb40
# Parent ca23ea683d26df004e666ede82950c712e3ac637
Various sample tracking bug fixes: samples no longer require bar codes, change all references to 'barcode' to be 'bar_code' since that is what the model uses, and mixing the 2 is not maintainable, enhance sample popup menu options, fix broken logic that handles bulk sample library and folderr changes.
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -725,7 +725,7 @@ class RequestsCommon( BaseController, Us
search_type = params.get( 'search_type', '' )
request_states = util.listify( params.get( 'request_states', '' ) )
samples = []
- if search_type == 'barcode':
+ if search_type == 'bar_code':
samples = trans.sa_session.query( trans.model.Sample ) \
.filter( and_( trans.model.Sample.table.c.deleted==False,
func.lower( trans.model.Sample.table.c.bar_code ).like( "%" + search_string.lower() + "%" ) ) ) \
@@ -763,7 +763,7 @@ class RequestsCommon( BaseController, Us
display='checkboxes' )
# Build the search_type SelectField
selected_value = kwd.get( 'search_type', 'sample name' )
- types = [ 'sample name', 'barcode', 'dataset' ]
+ types = [ 'sample name', 'bar_code', 'dataset' ]
search_type = build_select_field( trans, types, 'self', 'search_type', selected_value=selected_value, refresh_on_change=False )
# Build the search_box TextField
search_box = TextField( 'search_box', 50, kwd.get('search_box', '' ) )
@@ -846,7 +846,7 @@ class RequestsCommon( BaseController, Us
# Append the new sample to the current list of samples for the request
displayable_sample_widgets.append( dict( id=None,
name=name,
- barcode='',
+ bar_code='',
library=None,
library_id=library_id,
folder=None,
@@ -999,7 +999,7 @@ class RequestsCommon( BaseController, Us
**kwd )
displayable_sample_widgets.append( dict( id=None,
name=row[0],
- barcode='',
+ bar_code='',
library=None,
folder=None,
library_select_field=library_select_field,
@@ -1029,7 +1029,10 @@ class RequestsCommon( BaseController, Us
editing_samples=False )
def __save_samples( self, trans, cntrller, request, samples, **kwd ):
# Here we handle saving all new samples added by the user as well as saving
- # changes to any subset of the request's samples.
+ # changes to any subset of the request's samples. A sample will not have an
+ # associated SampleState until the request is submitted, at which time the
+ # sample is automatically associated with the first SampleState configured by
+ # the admin for the request's RequestType.
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
@@ -1065,7 +1068,7 @@ class RequestsCommon( BaseController, Us
# Send the encoded sample_ids to update_sample_state.
# TODO: make changes necessary to just send the samples...
encoded_selected_sample_ids = self.__get_encoded_selected_sample_ids( trans, request, **kwd )
- # Make sure all samples have a unique barcode if the state is changing
+ # Make sure all samples have a unique bar_code if the state is changing
for sample_index in range( len( samples ) ):
current_sample = samples[ sample_index ]
if current_sample is None:
@@ -1073,13 +1076,15 @@ class RequestsCommon( BaseController, Us
# on which to perform the action.
continue
request_sample = request.samples[ sample_index ]
- bc_message = self.__validate_barcode( trans, request_sample, current_sample[ 'barcode' ] )
- if bc_message:
- #status = 'error'
- message += bc_message
- kwd[ 'message' ] = message
- del kwd[ 'save_samples_button' ]
- handle_error( **kwd )
+ bar_code = current_sample[ 'bar_code' ]
+ if bar_code:
+ # If the sample has a new bar_code, make sure it is unique.
+ bc_message = self.__validate_bar_code( trans, request_sample, bar_code )
+ if bc_message:
+ message += bc_message
+ kwd[ 'message' ] = message
+ del kwd[ 'save_samples_button' ]
+ handle_error( **kwd )
self.update_sample_state( trans, cntrller, encoded_selected_sample_ids, new_state, comment=sample_event_comment )
return trans.response.send_redirect( web.url_for( controller='requests_common',
cntrller=cntrller,
@@ -1088,18 +1093,22 @@ class RequestsCommon( BaseController, Us
elif sample_operation == 'Select data library and folder':
# TODO: fix the code so that the sample_operation_select_field does not use
# sample_0_library_id as it's name. it should use something like sample_operation_library_id
- # and sample_operation-folder_id because the name sample_0_library_id should belong to the
+ # and sample_operation_folder_id because the name sample_0_library_id should belong to the
# first sample since all other form field values are named like this. The library and folder
# are skewed to be named +1 resulting in the forced use of id_index everywhere...
library_id = params.get( 'sample_0_library_id', 'none' )
folder_id = params.get( 'sample_0_folder_id', 'none' )
library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ for sample_index in range( len( samples ) ):
+ current_sample = samples[ sample_index ]
+ current_sample[ 'library' ] = library
+ current_sample[ 'folder' ] = folder
self.__update_samples( trans, cntrller, request, samples, **kwd )
# Samples will not have an associated SampleState until the request is submitted, at which
# time all samples of the request will be set to the first SampleState configured for the
# request's RequestType defined by the admin.
if request.is_submitted:
- # See if all the samples' barcodes are in the same state, and if so send email if configured to.
+ # See if all the samples' bar_codes are in the same state, and if so send email if configured to.
common_state = request.samples_have_common_state
if common_state and common_state.id == request.type.states[1].id:
comment = "All samples of this request are in the (%s) sample state. " % common_state.name
@@ -1136,10 +1145,12 @@ class RequestsCommon( BaseController, Us
status=status,
message=message ) )
def __update_samples( self, trans, cntrller, request, sample_widgets, **kwd ):
- # Determine if the values in kwd require updating the request's samples. The list of
- # sample_widgets must have the same number of objects as request.samples, but some of
- # the objects can be None. Those that are not None correspond to samples selected by
- # the user for performing an action on multiple samples simultaneously.
+ # The list of sample_widgets must have the same number of objects as request.samples,
+ # but some of the objects can be None. Those that are not None correspond to samples
+ # selected by the user for performing an action on multiple samples simultaneously.
+ # The items in the sample_widgets list have already been populated with any changed
+ # param values (changed implies the value in kwd is different from the attribute value
+ # in the database) in kwd before this method is reached.
def handle_error( **kwd ):
kwd[ 'status' ] = 'error'
return trans.response.send_redirect( web.url_for( controller='requests_common',
@@ -1147,12 +1158,6 @@ class RequestsCommon( BaseController, Us
cntrller=cntrller,
**kwd ) )
params = util.Params( kwd )
- sample_operation = params.get( 'sample_operation', 'none' )
- if sample_operation != 'none':
- # These values will be in kwd if the user checked 1 or more checkboxes for performing this action
- # on a set of samples.
- library_id = params.get( 'sample_0_library_id', 'none' )
- folder_id = params.get( 'sample_0_folder_id', 'none' )
for index, sample_widget in enumerate( sample_widgets ):
if sample_widget is not None:
# sample_widget will be None if the user checked sample check boxes and selected an action
@@ -1161,21 +1166,16 @@ class RequestsCommon( BaseController, Us
# Get the sample's form values to see if they have changed.
form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
if sample.name != sample_widget[ 'name' ] or \
- sample.bar_code != sample_widget[ 'barcode' ] or \
+ sample.bar_code != sample_widget[ 'bar_code' ] or \
sample.library != sample_widget[ 'library' ] or \
sample.folder != sample_widget[ 'folder' ] or \
form_values.content != sample_widget[ 'field_values' ]:
# Information about this sample has been changed.
sample.name = sample_widget[ 'name' ]
- barcode = sample_widget[ 'barcode' ]
- # The bar_code field requires special handling because after a request is submitted, the
- # state of a sample cannot be changed without a bar_code associated with the sample. Bar
- # codes can only be added to a sample after the request is submitted. Also, a samples will
- # not have an associated SampleState until the request is submitted, at which time the sample
- # is automatically associated with the first SamplesState configured by the admin for the
- # request's RequestType.
- if barcode:
- bc_message = self.__validate_barcode( trans, sample, bar_code )
+ bar_code = sample_widget[ 'bar_code' ]
+ # If the sample has a new bar_code, make sure it is unique.
+ if bar_code:
+ bc_message = self.__validate_bar_code( trans, sample, bar_code )
if bc_message:
kwd[ 'message' ] = bc_message
del kwd[ 'save_samples_button' ]
@@ -1191,7 +1191,7 @@ class RequestsCommon( BaseController, Us
'Bar code associated with the sample' )
trans.sa_session.add( event )
trans.sa_session.flush()
- sample.bar_code = barcode
+ sample.bar_code = bar_code
sample.library = sample_widget[ 'library' ]
sample.folder = sample_widget[ 'folder' ]
form_values.content = sample_widget[ 'field_values' ]
@@ -1281,7 +1281,7 @@ class RequestsCommon( BaseController, Us
# Update the sample attributes from kwd
sample_id = None
name = util.restore_text( params.get( 'sample_%i_name' % index, sample.name ) )
- bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, sample.bar_code ) )
+ bar_code = util.restore_text( params.get( 'sample_%i_bar_code' % index, sample.bar_code ) )
library_id = util.restore_text( params.get( 'sample_%i_library_id' % id_index, '' ) )
if not library_id and sample.library:
library_id = trans.security.encode_id( sample.library.id )
@@ -1303,7 +1303,7 @@ class RequestsCommon( BaseController, Us
**kwd )
sample_widgets.append( dict( id=sample_id,
name=name,
- barcode=bar_code,
+ bar_code=bar_code,
library=library,
folder=folder,
field_values=field_values,
@@ -1317,7 +1317,7 @@ class RequestsCommon( BaseController, Us
if not name:
break
id_index = index + 1
- bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
+ bar_code = util.restore_text( params.get( 'sample_%i_bar_code' % index, '' ) )
library_id = util.restore_text( params.get( 'sample_%i_library_id' % id_index, '' ) )
folder_id = util.restore_text( params.get( 'sample_%i_folder_id' % id_index, '' ) )
library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
@@ -1334,7 +1334,7 @@ class RequestsCommon( BaseController, Us
**kwd )
sample_widgets.append( dict( id=None,
name=name,
- barcode=bar_code,
+ bar_code=bar_code,
library=library,
folder=folder,
field_values=field_values,
@@ -1492,31 +1492,24 @@ class RequestsCommon( BaseController, Us
action='edit_samples',
cntrller=cntrller,
**kwd ) )
- def __validate_barcode( self, trans, sample, barcode ):
+ def __validate_bar_code( self, trans, sample, bar_code ):
"""
- Makes sure that the barcode about to be assigned to a sample is globally unique.
- That is, barcodes must be unique across requests in Galaxy sample tracking.
+ Make sure that the bar_code about to be assigned to a sample is globally unique.
+ That is, bar_codes must be unique across requests in Galaxy sample tracking.
+ Bar codes are not required, but if used, they can only be added to a sample after
+ the request is submitted.
"""
message = ''
unique = True
for index in range( len( sample.request.samples ) ):
- # Check for empty bar code
- if not barcode.strip():
- if sample.state.id == sample.request.type.states[0].id:
- # The user has not yet filled in the barcode value, but the sample is
- # 'new', so all is well.
- break
- else:
- message = "Fill in the barcode for sample (%s) before changing it's state." % sample.name
- break
# TODO: Add a unique constraint to sample.bar_code table column
# Make sure bar code is unique
- for sample_with_barcode in trans.sa_session.query( trans.model.Sample ) \
- .filter( trans.model.Sample.table.c.bar_code == barcode ):
- if sample_with_barcode and sample_with_barcode.id != sample.id:
+ for sample_with_bar_code in trans.sa_session.query( trans.model.Sample ) \
+ .filter( trans.model.Sample.table.c.bar_code == bar_code ):
+ if sample_with_bar_code and sample_with_bar_code.id != sample.id:
message = '''The bar code (%s) associated with the sample (%s) belongs to another sample.
Bar codes must be unique across all samples, so use a different bar code
- for this sample.''' % ( barcode, sample.name )
+ for this sample.''' % ( bar_code, sample.name )
unique = False
break
if not unique:
--- a/templates/requests/common/common.mako
+++ b/templates/requests/common/common.mako
@@ -122,7 +122,7 @@
is_rejected = request.is_rejected
is_submitted = sample.request.is_submitted
is_unsubmitted = sample.request.is_unsubmitted
- can_delete_samples = not is_complete
+ can_delete_samples = editing_samples and request.samples and ( ( is_admin and not is_complete ) or is_unsubmitted )
display_checkboxes = editing_samples and ( is_complete or is_rejected or is_submitted )
display_bar_code = request.samples and ( is_complete or is_rejected or is_submitted )
display_datasets = request.samples and ( is_complete or is_submitted )
@@ -153,10 +153,10 @@
%if display_bar_code:
<td valign="top">
%if is_admin:
- <input type="text" name="sample_${sample_widget_index}_barcode" value="${sample_widget['barcode']}" size="10"/>
+ <input type="text" name="sample_${sample_widget_index}_bar_code" value="${sample_widget['bar_code']}" size="10"/>
%else:
- ${sample_widget['barcode']}
- <input type="hidden" name="sample_${sample_widget_index}_barcode" value="${sample_widget['barcode']}"/>
+ ${sample_widget['bar_code']}
+ <input type="hidden" name="sample_${sample_widget_index}_bar_code" value="${sample_widget['bar_code']}"/>
%endif
</td>
%endif
@@ -234,7 +234,7 @@
is_submitted = request.is_submitted
is_unsubmitted = request.is_unsubmitted
can_add_samples = request.is_unsubmitted
- can_delete_samples = editing_samples and request.samples and not is_complete
+ can_delete_samples = editing_samples and request.samples and ( ( is_admin and not is_complete ) or is_unsubmitted )
can_edit_samples = request.samples and ( is_admin or not is_complete )
can_select_datasets = is_admin and displayable_sample_widgets and ( is_submitted or is_complete )
can_transfer_datasets = is_admin and request.samples and not request.is_rejected
@@ -280,14 +280,14 @@
<tbody><% trans.sa_session.refresh( request ) %>
## displayable_sample_widgets is a dictionary whose keys are:
- ## id, name, barcode, library, folder, field_values, library_select_field, folder_select_field
+ ## id, name, bar_code, library, folder, field_values, library_select_field, folder_select_field
## A displayable_sample_widget will have an id == None if the widget's associated sample has not
## yet been saved (i.e., the use clicked the "Add sample" button but has not yet clicked the
## "Save" button.
%for sample_widget_index, sample_widget in enumerate( displayable_sample_widgets ):
<%
sample_widget_name = sample_widget[ 'name' ]
- sample_widget_barcode = sample_widget[ 'barcode' ]
+ sample_widget_bar_code = sample_widget[ 'bar_code' ]
sample_widget_library = sample_widget[ 'library' ]
if sample_widget_library:
if cntrller == 'requests':
@@ -309,7 +309,12 @@
<td>
%if sample.state and ( can_select_datasets or can_transfer_datasets ):
## A sample will have a state only after the request has been submitted.
- <% encoded_id = trans.security.encode_id( sample.id ) %>
+ <%
+ encoded_id = trans.security.encode_id( sample.id )
+ transferred_dataset_files = sample.transferred_dataset_files
+ if not transferred_dataset_files:
+ transferred_dataset_files = []
+ %><div style="float: left; margin-left: 2px;" class="menubutton split popup" id="sample-${sample.id}-popup">
${sample.name}
</div>
@@ -317,8 +322,10 @@
%if can_select_datasets:
<li><a class="action-button" href="${h.url_for( controller='requests_admin', action='select_datasets_to_transfer', request_id=trans.security.encode_id( request.id ), sample_id=trans.security.encode_id( sample.id ) )}">Select datasets to transfer</a></li>
%endif
- %if sample.untransferred_dataset_files:
- <li><a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_datasets', sample_id=trans.security.encode_id( sample.id ) )}">Transfer datasets</a></li>
+ %if sample.datasets and len( sample.datasets ) > len( transferred_dataset_files ):
+ <li><a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_datasets', sample_id=trans.security.encode_id( sample.id ) )}">Manage selected datasets</a></li>
+ %elif sample.datasets and len(sample.datasets ) == len( transferred_dataset_files ):
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_sample_datasets', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ), transfer_status=trans.model.SampleDataset.transfer_status.COMPLETE )}">View transferred datasets</a></li>
%endif
</div>
%else:
@@ -326,7 +333,7 @@
%endif
</td>
%if display_bar_code:
- <td>${sample_widget_barcode}</td>
+ <td>${sample_widget_bar_code}</td>
%endif
%if is_unsubmitted:
<td>Unsubmitted</td>
@@ -361,9 +368,8 @@
<td>
%if is_admin:
<%
- if sample.transferred_dataset_files:
- transferred_dataset_files = sample.transferred_dataset_files
- else:
+ transferred_dataset_files = sample.transferred_dataset_files
+ if not transferred_dataset_files:
transferred_dataset_files = []
%>
%if not sample.datasets:
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -1575,7 +1575,7 @@ class TwillTestCase( unittest.TestCase )
for index, field_value in enumerate( bar_codes ):
sample_field_name = "sample_%i_name" % index
sample_field_value = samples[ index ].name.replace( ' ', '+' )
- field_name = "sample_%i_barcode" % index
+ field_name = "sample_%i_bar_code" % index
url += "&%s=%s" % ( field_name, field_value )
url += "&%s=%s" % ( sample_field_name, sample_field_value )
url += "&save_samples_button=Save"
1
0