# HG changeset patch -- Bitbucket.org # Project galaxy-dist # URL http://bitbucket.org/galaxy/galaxy-dist/overview # User Greg Von Kuster <greg@bx.psu.edu> # Date 1288723785 14400 # Node ID 6497a8cfd12e477e9c92dd183c4abb81d469ba51 # Parent b7ac22fab1588a565a07acc4972564c2b9198489 More miscellaneous bug fixes in Sample Tracking, all due to the recent UI changes. Changes the name of the test_forms_and_requests.py functional test script to be test_sample_tracking.py to more clearly reeflect what's being tested. --- a/templates/requests/common/edit_samples.mako +++ b/templates/requests/common/edit_samples.mako @@ -12,58 +12,6 @@ <%def name="javascripts()"> ${parent.javascripts()} ${common_javascripts()} - ${local_javascripts()} -</%def> - -<%def name="local_javascripts()"> - <script type="text/javascript"> - // Looks for changes in sample states using an async request. Keeps - // calling itself (via setTimeout) until all samples are in a terminal - // state. - var updater = function ( sample_states ) { - // Check if there are any items left to track - var empty = true; - for ( i in sample_states ) { - empty = false; - break; - } - if ( ! empty ) { - setTimeout( function() { updater_callback( sample_states ) }, 1000 ); - } - }; - - var updater_callback = function ( sample_states ) { - // Build request data - var ids = [] - var states = [] - $.each( sample_states, function ( id, state ) { - ids.push( id ); - states.push( state ); - }); - // Make ajax call - $.ajax( { - type: "POST", - url: "${h.url_for( controller='requests_common', action='sample_state_updates' )}", - dataType: "json", - data: { ids: ids.join( "," ), states: states.join( "," ) }, - success : function ( data ) { - $.each( data, function( id, val ) { - // Replace HTML - var cell1 = $("#sampleState-" + id); - cell1.html( val.html_state ); - var cell2 = $("#sampleDatasets-" + id); - cell2.html( val.html_datasets ); - sample_states[ parseInt( id ) ] = val.state; - }); - updater( sample_states ); - }, - error: function() { - // Just retry, like the old method, should try to be smarter - updater( sample_states ); - } - }); - }; - </script></%def><% @@ -73,7 +21,8 @@ is_complete = request.is_complete is_unsubmitted = request.is_unsubmitted can_add_samples = is_unsubmitted - can_edit_or_delete_samples = request.samples and not is_complete + can_delete_samples = request.samples and not is_complete + can_edit_samples = request.samples and ( is_admin or not is_complete ) can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted can_reject_or_transfer = is_admin and request.is_submitted can_submit = request.samples and is_unsubmitted @@ -82,7 +31,7 @@ <br/><br/><ul class="manage-table-actions"> - %if not editing_samples and can_edit_or_delete_samples: + %if not editing_samples and can_edit_samples: <li><a class="action-button" href="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples='True' )}">Edit samples</a></li> %endif %if editing_samples and can_add_samples: @@ -131,13 +80,13 @@ grid_header = '<h3>Add Samples to Request "%s"</h3>' % request.name %> ${render_samples_grid( cntrller, request, current_samples, action='edit_samples', editing_samples=editing_samples, encoded_selected_sample_ids=encoded_selected_sample_ids, render_buttons=False, grid_header=grid_header )} - %if editing_samples and len( sample_operation_select_field.options ) > 1 and not ( is_unsubmitted or is_complete ): + %if editing_samples and len( sample_operation_select_field.options ) > 1 and not is_unsubmitted: <div class="form-row" style="background-color:#FAFAFA;"> For selected samples: ${sample_operation_select_field.get_html()} </div><% sample_operation_selected_value = sample_operation_select_field.get_selected( return_value=True ) %> - %if sample_operation_selected_value != 'none' and encoded_selected_sample_ids: + %if ( is_admin or not is_complete ) and sample_operation_selected_value != 'none' and encoded_selected_sample_ids: <div class="form-row" style="background-color:#FAFAFA;"> %if sample_operation_selected_value == trans.model.Sample.bulk_operations.CHANGE_STATE: ## sample_operation_selected_value == 'Change state' @@ -150,7 +99,7 @@ Optional </div></div> - %elif sample_operation_selected_value == trans.app.model.Sample.bulk_operations.SELECT_LIBRARY: + %elif not is_complete and sample_operation_selected_value == trans.app.model.Sample.bulk_operations.SELECT_LIBRARY: <% libraries_selected_value = libraries_select_field.get_selected( return_value=True ) %><div class="form-row"><label>Select data library:</label> @@ -210,12 +159,12 @@ Click the <b>Save</b> button when you have finished editing the samples </div> %endif - ##%if request.samples and request.is_submitted: - ## <script type="text/javascript"> - ## // Updater - ## updater( {${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }}); - ## </script> - ##%endif + %if request.samples and request.is_submitted: + <script type="text/javascript"> + // Updater + updater( {${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }}); + </script> + %endif </form></div> %if is_unsubmitted and not editing_samples: --- a/test/functional/test_forms_and_requests.py +++ /dev/null @@ -1,436 +0,0 @@ -import galaxy.model -from galaxy.model.orm import * -from base.twilltestcase import * -from base.test_db_util import * - -sample_states = [ ( 'New', 'Sample entered into the system' ), - ( 'Received', 'Sample tube received' ), - ( 'Done', 'Sequence run complete' ) ] -address_dict = dict( short_desc="Office", - name="James Bond", - institution="MI6" , - address="MI6 Headquarters", - city="London", - state="London", - postal_code="007", - country="United Kingdom", - phone="007-007-0007" ) - -class TestFormsAndRequests( TwillTestCase ): - def test_000_initiate_users( self ): - """Ensuring all required user accounts exist""" - self.logout() - self.login( email='test1@bx.psu.edu', username='regular-user1' ) - global regular_user1 - regular_user1 = get_user( 'test1@bx.psu.edu' ) - assert regular_user1 is not None, 'Problem retrieving user with email "test1@bx.psu.edu" from the database' - global regular_user1_private_role - regular_user1_private_role = get_private_role( regular_user1 ) - self.logout() - self.login( email='test2@bx.psu.edu', username='regular-user2' ) - global regular_user2 - regular_user2 = get_user( 'test2@bx.psu.edu' ) - assert regular_user2 is not None, 'Problem retrieving user with email "test2@bx.psu.edu" from the database' - global regular_user2_private_role - regular_user2_private_role = get_private_role( regular_user2 ) - self.logout() - self.login( email='test3@bx.psu.edu', username='regular-user3' ) - global regular_user3 - regular_user3 = get_user( 'test3@bx.psu.edu' ) - assert regular_user3 is not None, 'Problem retrieving user with email "test3@bx.psu.edu" from the database' - global regular_user3_private_role - regular_user3_private_role = get_private_role( regular_user3 ) - self.logout() - self.login( email='test@bx.psu.edu', username='admin-user' ) - global admin_user - admin_user = get_user( 'test@bx.psu.edu' ) - assert admin_user is not None, 'Problem retrieving user with email "test@bx.psu.edu" from the database' - global admin_user_private_role - admin_user_private_role = get_private_role( admin_user ) - def test_005_create_required_groups_and_roles( self ): - """Testing creating all required groups and roles for this script""" - # Logged in as admin_user - # Create role_one - name = 'Role One' - description = "This is Role One's description" - user_ids = [ str( admin_user.id ), str( regular_user1.id ), str( regular_user3.id ) ] - self.create_role( name=name, - description=description, - in_user_ids=user_ids, - in_group_ids=[], - create_group_for_role='no', - private_role=admin_user.email ) - # Get the role object for later tests - global role_one - role_one = get_role_by_name( name ) - # Create group_one - name = 'Group One' - self.create_group( name=name, in_user_ids=[ str( regular_user1.id ) ], in_role_ids=[ str( role_one.id ) ] ) - # Get the group object for later tests - global group_one - group_one = get_group_by_name( name ) - assert group_one is not None, 'Problem retrieving group named "Group One" from the database' - # NOTE: To get this to work with twill, all select lists on the ~/admin/role page must contain at least - # 1 option value or twill throws an exception, which is: ParseError: OPTION outside of SELECT - # Due to this bug in twill, we create the role, we bypass the page and visit the URL in the - # associate_users_and_groups_with_role() method. - # - #create role_two - name = 'Role Two' - description = 'This is Role Two' - user_ids = [ str( admin_user.id ) ] - group_ids = [ str( group_one.id ) ] - private_role = admin_user.email - self.create_role( name=name, - description=description, - in_user_ids=user_ids, - in_group_ids=group_ids, - private_role=private_role ) - # Get the role object for later tests - global role_two - role_two = get_role_by_name( name ) - assert role_two is not None, 'Problem retrieving role named "Role Two" from the database' - def test_010_create_request_form( self ): - """Testing creating a request form definition, editing the name and description and adding fields""" - # Logged in as admin_user - # Create a form definition - tmp_name = "Temp form" - tmp_desc = "Temp form description" - form_type = galaxy.model.FormDefinition.types.REQUEST - self.create_form( name=tmp_name, - desc=tmp_desc, - form_type=form_type, - num_fields=0, - strings_displayed=[ 'Create a new form definition' ], - strings_displayed_after_submit=[ tmp_name, tmp_desc, form_type ] ) - tmp_form = get_form( tmp_name ) - # Edit the name and description of the form definition, and add 3 fields. - new_name = "Request Form" - new_desc = "Request Form description" - global test_field_name1 - test_field_name1 = 'Test field name one' - global test_field_name2 - test_field_name2 = 'Test field name two' - global test_field_name3 - test_field_name3 = 'Test field name three' - field_dicts = [ dict( name=test_field_name1, - desc='Test field description one', - type='SelectField', - required='optional', - selectlist=[ 'option1', 'option2' ] ), - dict( name=test_field_name2, - desc='Test field description two', - type='AddressField', - required='optional' ), - dict( name=test_field_name3, - desc='Test field description three', - type='TextField', - required='required' ) ] - self.edit_form( id=self.security.encode_id( tmp_form.current.id ), - new_form_name=new_name, - new_form_desc=new_desc, - field_dicts=field_dicts, - field_index=len( tmp_form.fields ), - strings_displayed=[ 'Edit form definition "%s"' % tmp_name ], - strings_displayed_after_submit=[ "The form '%s' has been updated with the changes." % new_name ] ) - # Get the form_definition object for later tests - global form_one - form_one = get_form( new_name ) - assert form_one is not None, 'Problem retrieving form named "%s" from the database' % new_name - assert len( form_one.fields ) == len( tmp_form.fields ) + len( field_dicts ) - def test_015_create_sample_form( self ): - """Testing creating sample form definition""" - name = "Sample Form" - desc = "This is Form Two's description" - form_type = galaxy.model.FormDefinition.types.SAMPLE - form_layout_name = 'Layout Grid One' - self.create_form( name=name, - desc=desc, - form_type=form_type, - form_layout_name=form_layout_name, - strings_displayed=[ 'Create a new form definition' ], - strings_displayed_after_submit=[ "The form '%s' has been updated with the changes." % name ] ) - global form_two - form_two = get_form( name ) - assert form_two is not None, "Error retrieving form %s from db" % name - def test_020_create_request_type( self ): - """Testing creating a request_type""" - request_form = get_form( form_one.name ) - sample_form = get_form( form_two.name ) - name = 'Test Requestype' - self.create_request_type( name, - "test sequencer configuration", - self.security.encode_id( request_form.id ), - self.security.encode_id( sample_form.id ), - sample_states, - strings_displayed=[ 'Create a new sequencer configuration' ], - strings_displayed_after_submit=[ "Sequencer configuration (%s) has been created" % name ] ) - global request_type1 - request_type1 = get_request_type_by_name( name ) - assert request_type1 is not None, 'Problem retrieving sequencer configuration named "%s" from the database' % name - # Set permissions - permissions_in = [ k for k, v in galaxy.model.RequestType.permitted_actions.items() ] - permissions_out = [] - # Role one members are: admin_user, regular_user1, regular_user3. Each of these users will be permitted for - # REQUEST_TYPE_ACCESS on this request_type - self.request_type_permissions( self.security.encode_id( request_type1.id ), - request_type1.name, - str( role_one.id ), - permissions_in, - permissions_out ) - # Make sure the request_type1 is not accessible by regular_user2 since regular_user2 does not have Role1. - self.logout() - self.login( email=regular_user2.email ) - self.visit_url( '%s/requests_common/create_request?cntrller=requests&request_type=True' % self.url ) - try: - self.check_page_for_string( 'There are no sequencer configurations created for a new request.' ) - raise AssertionError, 'The request_type %s is accessible by %s when it should be restricted' % ( request_type1.name, regular_user2.email ) - except: - pass - self.logout() - self.login( email=admin_user.email ) - def test_025_create_request( self ): - """Testing creating a sequence run request""" - # logged in as admin_user - # Create a user_address - self.logout() - self.login( email=regular_user1.email ) - self.add_user_address( regular_user1.id, address_dict ) - global user_address1 - user_address1 = get_user_address( regular_user1, address_dict[ 'short_desc' ] ) - # Set field values - the tuples in the field_values list include the field_value, and True if refresh_on_change - # is required for that field. - field_value_tuples = [ ( 'option1', False ), ( str( user_address1.id ), True ), ( 'field three value', False ) ] - # Create the request - name = 'Request One' - desc = 'Request One Description' - self.create_request( cntrller='requests', - request_type_id=self.security.encode_id( request_type1.id ), - name=name, - desc=desc, - field_value_tuples=field_value_tuples, - strings_displayed=[ 'Create a new sequencing request', - test_field_name1, - test_field_name2, - test_field_name3 ], - strings_displayed_after_submit=[ name, desc ] ) - global request_one - request_one = get_request_by_name( name ) - # Make sure the request's state is now set to NEW - assert request_one.state is not request_one.states.NEW, "The state of the request '%s' should be set to '%s'" \ - % ( request_one.name, request_one.states.NEW ) - # Sample fields - the tuple represents a sample name and a list of sample form field values - sample_value_tuples = [ ( 'Sample One', [ 'S1 Field 0 Value' ] ), - ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ] - strings_displayed_after_submit = [ 'Unsubmitted' ] - for sample_name, field_values in sample_value_tuples: - strings_displayed_after_submit.append( sample_name ) - # Add samples to the request - self.add_samples( cntrller='requests', - request_id=self.security.encode_id( request_one.id ), - request_name=request_one.name, - sample_value_tuples=sample_value_tuples, - strings_displayed=[ 'There are no samples.' ], - strings_displayed_after_submit=strings_displayed_after_submit ) - def test_030_edit_basic_request_info( self ): - """Testing editing the basic information of a sequence run request""" - # logged in as regular_user1 - fields = [ 'option2', str( user_address1.id ), 'field three value (edited)' ] - new_name=request_one.name + ' (Renamed)' - new_desc=request_one.desc + ' (Re-described)' - self.edit_basic_request_info( request_id=self.security.encode_id( request_one.id ), - cntrller='requests', - name=request_one.name, - new_name=new_name, - new_desc=new_desc, - new_fields=fields, - strings_displayed=[ 'Edit sequencing request "%s"' % request_one.name ], - strings_displayed_after_submit=[ new_name, new_desc ] ) - refresh( request_one ) - # check if the request is showing in the 'new' filter - self.check_request_grid( cntrller='requests', - state=request_one.states.NEW, - strings_displayed=[ request_one.name ] ) - 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( request_one.id ), - request_name=request_one.name, - strings_displayed_after_submit=[ 'The request has been submitted.' ] ) - refresh( request_one ) - # Make sure the request is showing in the 'submitted' filter - self.check_request_grid( cntrller='requests', - state=request_one.states.SUBMITTED, - strings_displayed=[ request_one.name ] ) - # Make sure the request's state is now set to 'submitted' - assert request_one.state is not request_one.states.SUBMITTED, "The state of the request '%s' should be set to '%s'" \ - % ( request_one.name, request_one.states.SUBMITTED ) - def test_040_request_lifecycle( self ): - """Testing request life-cycle as it goes through all the states""" - # logged in as regular_user1 - self.logout() - self.login( email=admin_user.email ) - self.check_request_grid( cntrller='requests_admin', - state=request_one.states.SUBMITTED, - strings_displayed=[ request_one.name ] ) - self.visit_url( "%s/requests_common/view_request?cntrller=requests&id=%s" % ( self.url, self.security.encode_id( request_one.id ) ) ) - # TODO: add some string for checking on the page above... - # Set bar codes for the samples - bar_codes = [ '1234567890', '0987654321' ] - strings_displayed_after_submit=[ 'Changes made to the samples have been saved.' ] - for bar_code in bar_codes: - strings_displayed_after_submit.append( bar_code ) - self.add_bar_codes( request_id=self.security.encode_id( request_one.id ), - request_name=request_one.name, - bar_codes=bar_codes, - samples=request_one.samples, - strings_displayed_after_submit=strings_displayed_after_submit ) - # Change the states of all the samples of this request to ultimately be COMPLETE - self.change_sample_state( request_id=self.security.encode_id( request_one.id ), - request_name=request_one.name, - sample_names=[ sample.name for sample in request_one.samples ], - sample_ids=[ sample.id for sample in request_one.samples ], - new_sample_state_id=request_type1.states[1].id, - new_state_name=request_type1.states[1].name ) - self.change_sample_state( request_id=self.security.encode_id( request_one.id ), - request_name=request_one.name, - sample_names=[ sample.name for sample in request_one.samples ], - sample_ids=[ sample.id for sample in request_one.samples ], - new_sample_state_id=request_type1.states[2].id, - new_state_name=request_type1.states[2].name ) - refresh( request_one ) - self.logout() - self.login( email=regular_user1.email ) - # check if the request's state is now set to 'complete' - self.check_request_grid( cntrller='requests', - state='Complete', - strings_displayed=[ request_one.name ] ) - assert request_one.state is not request_one.states.COMPLETE, "The state of the request '%s' should be set to '%s'" \ - % ( request_one.name, request_one.states.COMPLETE ) - - def test_045_admin_create_request_on_behalf_of_regular_user( self ): - """Testing creating and submitting a request as an admin on behalf of a regular user""" - # Logged in as regular_user1 - self.logout() - self.login( email=admin_user.email ) - # Create the request - name = "RequestTwo" - desc = 'Request Two Description' - # Set field values - the tuples in the field_values list include the field_value, and True if refresh_on_change - # is required for that field. - field_value_tuples = [ ( 'option2', False ), ( str( user_address1.id ), True ), ( 'field_2_value', False ) ] - self.create_request( cntrller='requests_admin', - request_type_id=self.security.encode_id( request_type1.id ), - other_users_id=self.security.encode_id( regular_user1.id ), - name=name, - desc=desc, - field_value_tuples=field_value_tuples, - strings_displayed=[ 'Create a new sequencing request', - test_field_name1, - test_field_name2, - test_field_name3 ], - strings_displayed_after_submit=[ "The request has been created." ] ) - global request_two - request_two = get_request_by_name( name ) - # Make sure the request is showing in the 'new' filter - self.check_request_grid( cntrller='requests_admin', - state=request_two.states.NEW, - strings_displayed=[ request_two.name ] ) - # Make sure the request's state is now set to 'new' - assert request_two.state is not request_two.states.NEW, "The state of the request '%s' should be set to '%s'" \ - % ( request_two.name, request_two.states.NEW ) - # Sample fields - the tuple represents a sample name and a list of sample form field values - sample_value_tuples = [ ( 'Sample One', [ 'S1 Field 0 Value' ] ), - ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ] - strings_displayed_after_submit = [ 'Unsubmitted' ] - for sample_name, field_values in sample_value_tuples: - strings_displayed_after_submit.append( sample_name ) - # Add samples to the request - self.add_samples( cntrller='requests_admin', - request_id=self.security.encode_id( request_two.id ), - request_name=request_two.name, - sample_value_tuples=sample_value_tuples, - strings_displayed=[ 'There are no samples.' ], - strings_displayed_after_submit=strings_displayed_after_submit ) - # Submit the request - self.submit_request( cntrller='requests_admin', - request_id=self.security.encode_id( request_two.id ), - request_name=request_two.name, - strings_displayed_after_submit=[ 'The request has been submitted.' ] ) - refresh( request_two ) - # Make sure the request is showing in the 'submitted' filter - self.check_request_grid( cntrller='requests_admin', - state=request_two.states.SUBMITTED, - strings_displayed=[ request_two.name ] ) - # Make sure the request's state is now set to 'submitted' - assert request_two.state is not request_two.states.SUBMITTED, "The state of the request '%s' should be set to '%s'" \ - % ( request_two.name, request_two.states.SUBMITTED ) - # Make sure both requests are showing in the 'All' filter - self.check_request_grid( cntrller='requests_admin', - state='All', - strings_displayed=[ request_one.name, request_two.name ] ) - def test_050_reject_request( self ): - """Testing rejecting a request""" - # Logged in as admin_user - self.reject_request( request_id=self.security.encode_id( request_two.id ), - request_name=request_two.name, - comment="Rejection test comment", - strings_displayed=[ 'Reject Sequencing Request "%s"' % request_two.name ], - strings_displayed_after_submit=[ 'Request (%s) has been rejected.' % request_two.name ] ) - refresh( request_two ) - # Make sure the request is showing in the 'rejected' filter - self.check_request_grid( cntrller='requests_admin', - state=request_two.states.REJECTED, - strings_displayed=[ request_two.name ] ) - # Make sure the request's state is now set to REJECTED - assert request_two.state is not request_two.states.REJECTED, "The state of the request '%s' should be set to '%s'" \ - % ( request_two.name, request_two.states.REJECTED ) - def test_055_reset_data_for_later_test_runs( self ): - """Reseting data to enable later test runs to pass""" - """ - # Logged in as admin_user - ################## - # Delete request_type permissions - ################## - for request_type in [ request_type1 ]: - delete_request_type_permissions( request_type.id ) - ################## - # Mark all request_types deleted - ################## - for request_type in [ request_type1 ]: - mark_obj_deleted( request_type ) - ################## - # Mark all requests deleted - ################## - for request in [ request_one, request_two ]: - mark_obj_deleted( request ) - ################## - # Mark all forms deleted - ################## - for form in [ form_one, form_two ]: - self.mark_form_deleted( self.security.encode_id( form.current.id ) ) - ################## - # Mark all user_addresses deleted - ################## - for user_address in [ user_address1 ]: - mark_obj_deleted( user_address ) - ################## - # Delete all non-private roles - ################## - for role in [ role_one, role_two ]: - self.mark_role_deleted( self.security.encode_id( role.id ), role.name ) - self.purge_role( self.security.encode_id( role.id ), role.name ) - # Manually delete the role from the database - refresh( role ) - delete( role ) - ################## - # Delete all groups - ################## - for group in [ group_one ]: - self.mark_group_deleted( self.security.encode_id( group.id ), group.name ) - self.purge_group( self.security.encode_id( group.id ), group.name ) - # Manually delete the group from the database - refresh( group ) - delete( group ) - """ --- a/lib/galaxy/web/controllers/requests_common.py +++ b/lib/galaxy/web/controllers/requests_common.py @@ -1047,10 +1047,15 @@ class RequestsCommon( BaseController, Us action='update_request_state', request_id=trans.security.encode_id( request.id ) ) ) 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 + # 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 ) - self.__update_samples( trans, request, samples, **kwd ) + 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. @@ -1092,7 +1097,7 @@ class RequestsCommon( BaseController, Us editing_samples=editing_samples, status=status, message=message ) ) - def __update_samples( self, trans, request, sample_widgets, **kwd ): + 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 --- /dev/null +++ b/test/functional/test_sample_tracking.py @@ -0,0 +1,434 @@ +import galaxy.model +from galaxy.model.orm import * +from base.twilltestcase import * +from base.test_db_util import * + +sample_states = [ ( 'New', 'Sample entered into the system' ), + ( 'Received', 'Sample tube received' ), + ( 'Done', 'Sequence run complete' ) ] +address_dict = dict( short_desc="Office", + name="James Bond", + institution="MI6" , + address="MI6 Headquarters", + city="London", + state="London", + postal_code="007", + country="United Kingdom", + phone="007-007-0007" ) + +class TestFormsAndRequests( TwillTestCase ): + def test_000_initiate_users( self ): + """Ensuring all required user accounts exist""" + self.logout() + self.login( email='test1@bx.psu.edu', username='regular-user1' ) + global regular_user1 + regular_user1 = get_user( 'test1@bx.psu.edu' ) + assert regular_user1 is not None, 'Problem retrieving user with email "test1@bx.psu.edu" from the database' + global regular_user1_private_role + regular_user1_private_role = get_private_role( regular_user1 ) + self.logout() + self.login( email='test2@bx.psu.edu', username='regular-user2' ) + global regular_user2 + regular_user2 = get_user( 'test2@bx.psu.edu' ) + assert regular_user2 is not None, 'Problem retrieving user with email "test2@bx.psu.edu" from the database' + global regular_user2_private_role + regular_user2_private_role = get_private_role( regular_user2 ) + self.logout() + self.login( email='test3@bx.psu.edu', username='regular-user3' ) + global regular_user3 + regular_user3 = get_user( 'test3@bx.psu.edu' ) + assert regular_user3 is not None, 'Problem retrieving user with email "test3@bx.psu.edu" from the database' + global regular_user3_private_role + regular_user3_private_role = get_private_role( regular_user3 ) + self.logout() + self.login( email='test@bx.psu.edu', username='admin-user' ) + global admin_user + admin_user = get_user( 'test@bx.psu.edu' ) + assert admin_user is not None, 'Problem retrieving user with email "test@bx.psu.edu" from the database' + global admin_user_private_role + admin_user_private_role = get_private_role( admin_user ) + def test_005_create_required_groups_and_roles( self ): + """Testing creating all required groups and roles for this script""" + # Logged in as admin_user + # Create role_one + name = 'Role One' + description = "This is Role One's description" + user_ids = [ str( admin_user.id ), str( regular_user1.id ), str( regular_user3.id ) ] + self.create_role( name=name, + description=description, + in_user_ids=user_ids, + in_group_ids=[], + create_group_for_role='no', + private_role=admin_user.email ) + # Get the role object for later tests + global role_one + role_one = get_role_by_name( name ) + # Create group_one + name = 'Group One' + self.create_group( name=name, in_user_ids=[ str( regular_user1.id ) ], in_role_ids=[ str( role_one.id ) ] ) + # Get the group object for later tests + global group_one + group_one = get_group_by_name( name ) + assert group_one is not None, 'Problem retrieving group named "Group One" from the database' + # NOTE: To get this to work with twill, all select lists on the ~/admin/role page must contain at least + # 1 option value or twill throws an exception, which is: ParseError: OPTION outside of SELECT + # Due to this bug in twill, we create the role, we bypass the page and visit the URL in the + # associate_users_and_groups_with_role() method. + # + #create role_two + name = 'Role Two' + description = 'This is Role Two' + user_ids = [ str( admin_user.id ) ] + group_ids = [ str( group_one.id ) ] + private_role = admin_user.email + self.create_role( name=name, + description=description, + in_user_ids=user_ids, + in_group_ids=group_ids, + private_role=private_role ) + # Get the role object for later tests + global role_two + role_two = get_role_by_name( name ) + assert role_two is not None, 'Problem retrieving role named "Role Two" from the database' + def test_010_create_request_form( self ): + """Testing creating a request form definition, editing the name and description and adding fields""" + # Logged in as admin_user + # Create a form definition + tmp_name = "Temp form" + tmp_desc = "Temp form description" + form_type = galaxy.model.FormDefinition.types.REQUEST + self.create_form( name=tmp_name, + desc=tmp_desc, + form_type=form_type, + num_fields=0, + strings_displayed=[ 'Create a new form definition' ], + strings_displayed_after_submit=[ tmp_name, tmp_desc, form_type ] ) + tmp_form = get_form( tmp_name ) + # Edit the name and description of the form definition, and add 3 fields. + new_name = "Request Form" + new_desc = "Request Form description" + global test_field_name1 + test_field_name1 = 'Test field name one' + global test_field_name2 + test_field_name2 = 'Test field name two' + global test_field_name3 + test_field_name3 = 'Test field name three' + field_dicts = [ dict( name=test_field_name1, + desc='Test field description one', + type='SelectField', + required='optional', + selectlist=[ 'option1', 'option2' ] ), + dict( name=test_field_name2, + desc='Test field description two', + type='AddressField', + required='optional' ), + dict( name=test_field_name3, + desc='Test field description three', + type='TextField', + required='required' ) ] + self.edit_form( id=self.security.encode_id( tmp_form.current.id ), + new_form_name=new_name, + new_form_desc=new_desc, + field_dicts=field_dicts, + field_index=len( tmp_form.fields ), + strings_displayed=[ 'Edit form definition "%s"' % tmp_name ], + strings_displayed_after_submit=[ "The form '%s' has been updated with the changes." % new_name ] ) + # Get the form_definition object for later tests + global form_one + form_one = get_form( new_name ) + assert form_one is not None, 'Problem retrieving form named "%s" from the database' % new_name + assert len( form_one.fields ) == len( tmp_form.fields ) + len( field_dicts ) + def test_015_create_sample_form( self ): + """Testing creating sample form definition""" + name = "Sample Form" + desc = "This is Form Two's description" + form_type = galaxy.model.FormDefinition.types.SAMPLE + form_layout_name = 'Layout Grid One' + self.create_form( name=name, + desc=desc, + form_type=form_type, + form_layout_name=form_layout_name, + strings_displayed=[ 'Create a new form definition' ], + strings_displayed_after_submit=[ "The form '%s' has been updated with the changes." % name ] ) + global form_two + form_two = get_form( name ) + assert form_two is not None, "Error retrieving form %s from db" % name + def test_020_create_request_type( self ): + """Testing creating a request_type""" + request_form = get_form( form_one.name ) + sample_form = get_form( form_two.name ) + name = 'Test Requestype' + self.create_request_type( name, + "test sequencer configuration", + self.security.encode_id( request_form.id ), + self.security.encode_id( sample_form.id ), + sample_states, + strings_displayed=[ 'Create a new sequencer configuration' ], + strings_displayed_after_submit=[ "Sequencer configuration (%s) has been created" % name ] ) + global request_type1 + request_type1 = get_request_type_by_name( name ) + assert request_type1 is not None, 'Problem retrieving sequencer configuration named "%s" from the database' % name + # Set permissions + permissions_in = [ k for k, v in galaxy.model.RequestType.permitted_actions.items() ] + permissions_out = [] + # Role one members are: admin_user, regular_user1, regular_user3. Each of these users will be permitted for + # REQUEST_TYPE_ACCESS on this request_type + self.request_type_permissions( self.security.encode_id( request_type1.id ), + request_type1.name, + str( role_one.id ), + permissions_in, + permissions_out ) + # Make sure the request_type1 is not accessible by regular_user2 since regular_user2 does not have Role1. + self.logout() + self.login( email=regular_user2.email ) + self.visit_url( '%s/requests_common/create_request?cntrller=requests&request_type=True' % self.url ) + try: + self.check_page_for_string( 'There are no sequencer configurations created for a new request.' ) + raise AssertionError, 'The request_type %s is accessible by %s when it should be restricted' % ( request_type1.name, regular_user2.email ) + except: + pass + self.logout() + self.login( email=admin_user.email ) + def test_025_create_request( self ): + """Testing creating a sequence run request""" + # logged in as admin_user + # Create a user_address + self.logout() + self.login( email=regular_user1.email ) + self.add_user_address( regular_user1.id, address_dict ) + global user_address1 + user_address1 = get_user_address( regular_user1, address_dict[ 'short_desc' ] ) + # Set field values - the tuples in the field_values list include the field_value, and True if refresh_on_change + # is required for that field. + field_value_tuples = [ ( 'option1', False ), ( str( user_address1.id ), True ), ( 'field three value', False ) ] + # Create the request + name = 'Request One' + desc = 'Request One Description' + self.create_request( cntrller='requests', + request_type_id=self.security.encode_id( request_type1.id ), + name=name, + desc=desc, + field_value_tuples=field_value_tuples, + strings_displayed=[ 'Create a new sequencing request', + test_field_name1, + test_field_name2, + test_field_name3 ], + strings_displayed_after_submit=[ name, desc ] ) + global request_one + request_one = get_request_by_name( name ) + # Make sure the request's state is now set to NEW + assert request_one.state is not request_one.states.NEW, "The state of the request '%s' should be set to '%s'" \ + % ( request_one.name, request_one.states.NEW ) + # Sample fields - the tuple represents a sample name and a list of sample form field values + sample_value_tuples = [ ( 'Sample One', [ 'S1 Field 0 Value' ] ), + ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ] + strings_displayed_after_submit = [ 'Unsubmitted' ] + for sample_name, field_values in sample_value_tuples: + strings_displayed_after_submit.append( sample_name ) + # Add samples to the request + self.add_samples( cntrller='requests', + request_id=self.security.encode_id( request_one.id ), + request_name=request_one.name, + sample_value_tuples=sample_value_tuples, + strings_displayed=[ 'There are no samples.' ], + strings_displayed_after_submit=strings_displayed_after_submit ) + def test_030_edit_basic_request_info( self ): + """Testing editing the basic information of a sequence run request""" + # logged in as regular_user1 + fields = [ 'option2', str( user_address1.id ), 'field three value (edited)' ] + new_name=request_one.name + ' (Renamed)' + new_desc=request_one.desc + ' (Re-described)' + self.edit_basic_request_info( request_id=self.security.encode_id( request_one.id ), + cntrller='requests', + name=request_one.name, + new_name=new_name, + new_desc=new_desc, + new_fields=fields, + strings_displayed=[ 'Edit sequencing request "%s"' % request_one.name ], + strings_displayed_after_submit=[ new_name, new_desc ] ) + refresh( request_one ) + # check if the request is showing in the 'new' filter + self.check_request_grid( cntrller='requests', + state=request_one.states.NEW, + strings_displayed=[ request_one.name ] ) + 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( request_one.id ), + request_name=request_one.name, + strings_displayed_after_submit=[ 'The request has been submitted.' ] ) + refresh( request_one ) + # Make sure the request is showing in the 'submitted' filter + self.check_request_grid( cntrller='requests', + state=request_one.states.SUBMITTED, + strings_displayed=[ request_one.name ] ) + # Make sure the request's state is now set to 'submitted' + assert request_one.state is not request_one.states.SUBMITTED, "The state of the request '%s' should be set to '%s'" \ + % ( request_one.name, request_one.states.SUBMITTED ) + def test_040_request_lifecycle( self ): + """Testing request life-cycle as it goes through all the states""" + # logged in as regular_user1 + self.logout() + self.login( email=admin_user.email ) + self.check_request_grid( cntrller='requests_admin', + state=request_one.states.SUBMITTED, + strings_displayed=[ request_one.name ] ) + self.visit_url( "%s/requests_common/view_request?cntrller=requests&id=%s" % ( self.url, self.security.encode_id( request_one.id ) ) ) + # TODO: add some string for checking on the page above... + # Set bar codes for the samples + bar_codes = [ '1234567890', '0987654321' ] + strings_displayed_after_submit=[ 'Changes made to the samples have been saved.' ] + for bar_code in bar_codes: + strings_displayed_after_submit.append( bar_code ) + self.add_bar_codes( request_id=self.security.encode_id( request_one.id ), + request_name=request_one.name, + bar_codes=bar_codes, + samples=request_one.samples, + strings_displayed_after_submit=strings_displayed_after_submit ) + # Change the states of all the samples of this request to ultimately be COMPLETE + self.change_sample_state( request_id=self.security.encode_id( request_one.id ), + request_name=request_one.name, + sample_names=[ sample.name for sample in request_one.samples ], + sample_ids=[ sample.id for sample in request_one.samples ], + new_sample_state_id=request_type1.states[1].id, + new_state_name=request_type1.states[1].name ) + self.change_sample_state( request_id=self.security.encode_id( request_one.id ), + request_name=request_one.name, + sample_names=[ sample.name for sample in request_one.samples ], + sample_ids=[ sample.id for sample in request_one.samples ], + new_sample_state_id=request_type1.states[2].id, + new_state_name=request_type1.states[2].name ) + refresh( request_one ) + self.logout() + self.login( email=regular_user1.email ) + # check if the request's state is now set to 'complete' + self.check_request_grid( cntrller='requests', + state='Complete', + strings_displayed=[ request_one.name ] ) + assert request_one.state is not request_one.states.COMPLETE, "The state of the request '%s' should be set to '%s'" \ + % ( request_one.name, request_one.states.COMPLETE ) + + def test_045_admin_create_request_on_behalf_of_regular_user( self ): + """Testing creating and submitting a request as an admin on behalf of a regular user""" + # Logged in as regular_user1 + self.logout() + self.login( email=admin_user.email ) + # Create the request + name = "RequestTwo" + desc = 'Request Two Description' + # Set field values - the tuples in the field_values list include the field_value, and True if refresh_on_change + # is required for that field. + field_value_tuples = [ ( 'option2', False ), ( str( user_address1.id ), True ), ( 'field_2_value', False ) ] + self.create_request( cntrller='requests_admin', + request_type_id=self.security.encode_id( request_type1.id ), + other_users_id=self.security.encode_id( regular_user1.id ), + name=name, + desc=desc, + field_value_tuples=field_value_tuples, + strings_displayed=[ 'Create a new sequencing request', + test_field_name1, + test_field_name2, + test_field_name3 ], + strings_displayed_after_submit=[ "The request has been created." ] ) + global request_two + request_two = get_request_by_name( name ) + # Make sure the request is showing in the 'new' filter + self.check_request_grid( cntrller='requests_admin', + state=request_two.states.NEW, + strings_displayed=[ request_two.name ] ) + # Make sure the request's state is now set to 'new' + assert request_two.state is not request_two.states.NEW, "The state of the request '%s' should be set to '%s'" \ + % ( request_two.name, request_two.states.NEW ) + # Sample fields - the tuple represents a sample name and a list of sample form field values + sample_value_tuples = [ ( 'Sample One', [ 'S1 Field 0 Value' ] ), + ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ] + strings_displayed_after_submit = [ 'Unsubmitted' ] + for sample_name, field_values in sample_value_tuples: + strings_displayed_after_submit.append( sample_name ) + # Add samples to the request + self.add_samples( cntrller='requests_admin', + request_id=self.security.encode_id( request_two.id ), + request_name=request_two.name, + sample_value_tuples=sample_value_tuples, + strings_displayed=[ 'There are no samples.' ], + strings_displayed_after_submit=strings_displayed_after_submit ) + # Submit the request + self.submit_request( cntrller='requests_admin', + request_id=self.security.encode_id( request_two.id ), + request_name=request_two.name, + strings_displayed_after_submit=[ 'The request has been submitted.' ] ) + refresh( request_two ) + # Make sure the request is showing in the 'submitted' filter + self.check_request_grid( cntrller='requests_admin', + state=request_two.states.SUBMITTED, + strings_displayed=[ request_two.name ] ) + # Make sure the request's state is now set to 'submitted' + assert request_two.state is not request_two.states.SUBMITTED, "The state of the request '%s' should be set to '%s'" \ + % ( request_two.name, request_two.states.SUBMITTED ) + # Make sure both requests are showing in the 'All' filter + self.check_request_grid( cntrller='requests_admin', + state='All', + strings_displayed=[ request_one.name, request_two.name ] ) + def test_050_reject_request( self ): + """Testing rejecting a request""" + # Logged in as admin_user + self.reject_request( request_id=self.security.encode_id( request_two.id ), + request_name=request_two.name, + comment="Rejection test comment", + strings_displayed=[ 'Reject Sequencing Request "%s"' % request_two.name ], + strings_displayed_after_submit=[ 'Request (%s) has been rejected.' % request_two.name ] ) + refresh( request_two ) + # Make sure the request is showing in the 'rejected' filter + self.check_request_grid( cntrller='requests_admin', + state=request_two.states.REJECTED, + strings_displayed=[ request_two.name ] ) + # Make sure the request's state is now set to REJECTED + assert request_two.state is not request_two.states.REJECTED, "The state of the request '%s' should be set to '%s'" \ + % ( request_two.name, request_two.states.REJECTED ) + def test_055_reset_data_for_later_test_runs( self ): + """Reseting data to enable later test runs to pass""" + # Logged in as admin_user + ################## + # Delete request_type permissions + ################## + for request_type in [ request_type1 ]: + delete_request_type_permissions( request_type.id ) + ################## + # Mark all request_types deleted + ################## + for request_type in [ request_type1 ]: + mark_obj_deleted( request_type ) + ################## + # Mark all requests deleted + ################## + for request in [ request_one, request_two ]: + mark_obj_deleted( request ) + ################## + # Mark all forms deleted + ################## + for form in [ form_one, form_two ]: + self.mark_form_deleted( self.security.encode_id( form.current.id ) ) + ################## + # Mark all user_addresses deleted + ################## + for user_address in [ user_address1 ]: + mark_obj_deleted( user_address ) + ################## + # Delete all non-private roles + ################## + for role in [ role_one, role_two ]: + self.mark_role_deleted( self.security.encode_id( role.id ), role.name ) + self.purge_role( self.security.encode_id( role.id ), role.name ) + # Manually delete the role from the database + refresh( role ) + delete( role ) + ################## + # Delete all groups + ################## + for group in [ group_one ]: + self.mark_group_deleted( self.security.encode_id( group.id ), group.name ) + self.purge_group( self.security.encode_id( group.id ), group.name ) + # Manually delete the group from the database + refresh( group ) + delete( group ) --- a/templates/requests/common/common.mako +++ b/templates/requests/common/common.mako @@ -65,12 +65,60 @@ } } } + + // Looks for changes in sample states using an async request. Keeps + // calling itself (via setTimeout) until all samples are in a terminal + // state. + var updater = function ( sample_states ) { + // Check if there are any items left to track + var empty = true; + for ( i in sample_states ) { + empty = false; + break; + } + if ( ! empty ) { + setTimeout( function() { updater_callback( sample_states ) }, 1000 ); + } + }; + + var updater_callback = function ( sample_states ) { + // Build request data + var ids = [] + var states = [] + $.each( sample_states, function ( id, state ) { + ids.push( id ); + states.push( state ); + }); + // Make ajax call + $.ajax( { + type: "POST", + url: "${h.url_for( controller='requests_common', action='sample_state_updates' )}", + dataType: "json", + data: { ids: ids.join( "," ), states: states.join( "," ) }, + success : function ( data ) { + $.each( data, function( id, val ) { + // Replace HTML + var cell1 = $("#sampleState-" + id); + cell1.html( val.html_state ); + var cell2 = $("#sampleDatasets-" + id); + cell2.html( val.html_datasets ); + sample_states[ parseInt( id ) ] = val.state; + }); + updater( sample_states ); + }, + error: function() { + // Just retry, like the old method, should try to be smarter + updater( sample_states ); + } + }); + }; </script></%def><%def name="render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )"><% if sample: + trans.sa_session.refresh( sample.request ) is_complete = sample.request.is_complete is_submitted = sample.request.is_submitted is_unsubmitted = sample.request.is_unsubmitted @@ -80,12 +128,12 @@ is_unsubmitted = False %><% - if is_admin and is_submitted and editing_samples and trans.security.encode_id( sample.id ) in encoded_selected_sample_ids: + if is_submitted and editing_samples and trans.security.encode_id( sample.id ) in encoded_selected_sample_ids: checked_str = "checked" else: checked_str = "" %> - %if is_admin and is_submitted and editing_samples: + %if is_submitted and editing_samples: <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true" ${checked_str}/><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"/></td> %endif <td valign="top"> @@ -95,7 +143,11 @@ </div></td> %if sample and is_submitted or is_complete: - <td valign="top"><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td> + %if is_admin: + <td valign="top"><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td> + %else: + ${current_sample['barcode']} + %endif %endif %if sample: %if is_unsubmitted: @@ -118,7 +170,7 @@ <td valign="top"><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td><td valign="top"><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td> %endif - %if sample and ( is_admin or is_unsubmitted ): + %if sample and ( is_admin or is_unsubmitted ) and not is_complete: ## Delete button <td valign="top"><a class="action-button" href="${h.url_for( controller='requests_common', action='delete_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), sample_id=current_sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a></td> %endif @@ -127,20 +179,22 @@ <%def name="render_samples_grid( cntrller, request, current_samples, action, editing_samples=False, encoded_selected_sample_ids=[], render_buttons=False, grid_header='<h3>Samples</h3>' )"> ## Displays the "Samples" grid <% + trans.sa_session.refresh( request ) is_admin = cntrller == 'requests_admin' and trans.user_is_admin() is_complete = request.is_complete is_submitted = request.is_submitted is_unsubmitted = request.is_unsubmitted can_add_samples = request.is_unsubmitted - can_edit_or_delete_samples = request.samples and not is_complete + can_delete_samples = request.samples and not is_complete + can_edit_samples = request.samples and ( is_admin or not is_complete ) %> ${grid_header} - %if render_buttons and ( can_add_samples or can_edit_or_delete_samples ): + %if render_buttons and ( can_add_samples or can_edit_samples ): <ul class="manage-table-actions"> %if can_add_samples: <li><a class="action-button" href="${h.url_for( controller='requests_common', action='add_sample', cntrller=cntrller, request_id=trans.security.encode_id( request.id ), add_sample_button='Add sample' )}">Add sample</a></li> %endif - %if can_edit_or_delete_samples: + %if can_edit_samples: <li><a class="action-button" href="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples='True' )}">Edit samples</a></li> %endif </ul> @@ -148,7 +202,7 @@ <table class="grid"><thead><tr> - %if is_admin and is_submitted and editing_samples: + %if is_submitted and editing_samples: <th><input type="checkbox" id="checkAll" name=select_all_samples_checkbox value="true" onclick='checkAllFields(1);'/><input type="hidden" name=select_all_samples_checkbox value="true"/></th> %endif <th>Name</th> @@ -191,7 +245,7 @@ except: sample = None %> - %if editing_samples: + %if not is_complete and editing_samples: <tr>${render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr> %elif sample: <tr> @@ -220,6 +274,7 @@ %endif </tr> %else: + ## The Add sample button was clicked for this sample_widget <tr>${render_editable_sample_row( is_admin, None, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr> %endif %endfor @@ -231,9 +286,7 @@ <tr><td>${sample_name}</td> %for field_index, field in fields_dict.items(): - <% - field_type = field[ 'type' ] - %> + <% field_type = field[ 'type' ] %><td> %if display_only: %if sample_values[field_index]: --- a/templates/requests/common/view_request.mako +++ b/templates/requests/common/view_request.mako @@ -22,7 +22,8 @@ is_unsubmitted = request.is_unsubmitted can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted can_add_samples = is_unsubmitted - can_edit_or_delete_samples = request.samples and not is_complete + can_delete_samples = request.samples and not is_complete + can_edit_samples = request.samples and ( is_admin or not is_complete ) can_submit = request.samples and is_unsubmitted %> @@ -131,14 +132,23 @@ ${states} <div style="clear: both"></div></div> + %if request.samples and request.is_submitted: + <script type="text/javascript"> + // Updater + updater( {${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }}); + </script> + %endif </div></div></div></div><p/> %if current_samples: - <% grid_header = '<h3>Samples</h3>' %> - ${render_samples_grid( cntrller, request, current_samples=current_samples, action='view_request', editing_samples=False, encoded_selected_sample_ids=[], render_buttons=can_edit_or_delete_samples, grid_header=grid_header )} + <% + grid_header = '<h3>Samples</h3>' + render_buttons = can_edit_samples + %> + ${render_samples_grid( cntrller, request, current_samples=current_samples, action='view_request', editing_samples=False, encoded_selected_sample_ids=[], render_buttons=render_buttons, grid_header=grid_header )} %else: There are no samples. %if can_add_samples: