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

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 1288645775 14400
# Node ID 695c89309bcb2f5d31bfb8b9d8b0124ff3abf90f
# Parent cf7ec71c561323940ce95c8148d2008508920092
Cleanup of debug info.
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -476,7 +476,6 @@ function Workflow( canvas_container ) {
});
},
rectify_workflow_outputs : function() {
- console.log("RECTIFICATION!");
// Find out if we're using workflow_outputs or not.
var using_workflow_outputs = false;
$.each( this.nodes, function ( k, node ) {
@@ -493,7 +492,6 @@ function Workflow( canvas_container ) {
if (node.type == 'tool'){
var node_changed = false;
if (node.post_job_actions == null){
- console.log("CREATED FOR NEW NODE");
node.post_job_actions = {};
}
var pjas_to_rem = [];
1
0

galaxy-dist commit c4d8ffb3109e: Increment js version to get around cached editors.
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 1288647270 14400
# Node ID c4d8ffb3109e8cfb8143dc87c88dee3337bb7569
# Parent 9285b39ae2f3dfd5017808b4d9b657094fec802b
Increment js version to get around cached editors.
--- a/lib/galaxy/web/framework/helpers/__init__.py
+++ b/lib/galaxy/web/framework/helpers/__init__.py
@@ -44,7 +44,7 @@ def js( *args ):
TODO: This has a hardcoded "?v=X" to defeat caching. This should be done
in a better way.
"""
- return "\n".join( [ javascript_include_tag( "/static/scripts/" + name + ".js?v=7" ) for name in args ] )
+ return "\n".join( [ javascript_include_tag( "/static/scripts/" + name + ".js?v=8" ) for name in args ] )
# Hashes
1
0

galaxy-dist commit b7ac22fab158: First pass of bug fixes, anbd additional UI cleanup for Sample Tracking.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Greg Von Kuster <greg(a)bx.psu.edu>
# Date 1288713844 14400
# Node ID b7ac22fab1588a565a07acc4972564c2b9198489
# Parent 667043341e81261a7fe587daa475110486b15292
First pass of bug fixes, anbd additional UI cleanup for Sample Tracking.
--- a/templates/requests/common/edit_samples.mako
+++ b/templates/requests/common/edit_samples.mako
@@ -73,9 +73,10 @@
is_complete = request.is_complete
is_unsubmitted = request.is_unsubmitted
can_add_samples = is_unsubmitted
- can_edit_or_delete_samples = is_unsubmitted and request.samples
+ can_edit_or_delete_samples = request.samples and 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
%><br/><br/>
@@ -87,7 +88,7 @@
%if editing_samples and 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 is_unsubmitted:
+ %if can_submit:
<li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
%endif
<li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
@@ -209,12 +210,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/lib/galaxy/web/controllers/requests.py
+++ b/lib/galaxy/web/controllers/requests.py
@@ -36,6 +36,12 @@ class Requests( BaseController ):
action='edit_basic_request_info',
cntrller='requests',
**kwd ) )
+ if operation == "edit_samples":
+ kwd[ 'editing_samples' ] = True
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller='requests',
+ **kwd ) )
if operation == "view_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='view_request',
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -46,17 +46,6 @@ class RequestsGrid( grids.Grid ):
.filter( model.RequestEvent.table.c.id.in_( select( columns=[ func.max( model.RequestEvent.table.c.id ) ],
from_obj=model.RequestEvent.table,
group_by=model.RequestEvent.table.c.request_id ) ) )
- def get_accepted_filters( self ):
- """ Returns a list of accepted filters for this column. """
- # TODO: is this method necessary?
- accepted_filter_labels_and_vals = [ model.Request.states.get( state ) for state in model.Request.states ]
- accepted_filter_labels_and_vals.append( "All" )
- accepted_filters = []
- for val in accepted_filter_labels_and_vals:
- label = val.lower()
- args = { self.key: val }
- accepted_filters.append( grids.GridColumnFilter( label, args ) )
- return accepted_filters
# Grid definition
title = "Sequencing Requests"
@@ -64,7 +53,6 @@ class RequestsGrid( grids.Grid ):
model_class = model.Request
default_sort_key = "-update_time"
num_rows_per_page = 50
- preserve_state = True
use_paging = True
default_filter = dict( state="All", deleted="False" )
columns = [
@@ -379,14 +367,24 @@ class RequestsCommon( BaseController, Us
sample_event_comment = ""
event = trans.model.RequestEvent( request, request.states.SUBMITTED, sample_event_comment )
trans.sa_session.add( event )
- # change the state of each of the samples of thus request
- new_state = request.type.states[0]
+ # Change the state of each of the samples of this request
+ # request.type.states is the list of SampleState objects configured
+ # by the admin for this RequestType.
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ # 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 configured by the admin.
+ initial_sample_state_after_request_submitted = request.type.states[0]
for sample in request.samples:
- event = trans.model.SampleEvent( sample, new_state, 'Samples created.' )
+ event_comment = 'Request submitted and sample state set to %s.' % request.type.states[0].name
+ event = trans.model.SampleEvent( sample,
+ initial_sample_state_after_request_submitted,
+ event_comment )
trans.sa_session.add( event )
trans.sa_session.add( request )
trans.sa_session.flush()
- request.send_email_notification( trans, new_state )
+ request.send_email_notification( trans, initial_sample_state_after_request_submitted )
message = 'The request has been submitted.'
return trans.response.send_redirect( web.url_for( controller=cntrller,
action='browse_requests',
@@ -519,8 +517,6 @@ class RequestsCommon( BaseController, Us
try:
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( id ) )
except:
- message += "Invalid request ID (%s). " % str( id )
- status = 'error'
ok_for_now = False
if ok_for_now:
request.deleted = True
@@ -549,8 +545,6 @@ class RequestsCommon( BaseController, Us
try:
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( id ) )
except:
- message += "Invalid request ID (%s). " % str( id )
- status = 'error'
ok_for_now = False
if ok_for_now:
request.deleted = False
@@ -903,6 +897,7 @@ class RequestsCommon( BaseController, Us
action='edit_samples',
cntrller=cntrller,
id=trans.security.encode_id( sample.request.id ),
+ editing_samples=True,
status=status,
message=message ) )
if is_admin:
@@ -1024,7 +1019,7 @@ class RequestsCommon( BaseController, Us
if sample_state_id in [ None, 'none' ]:
message = "Select a new state from the <b>Change current state</b> list before clicking the <b>Save</b> button."
kwd[ 'message' ] = message
- del kwd[ 'save_changes_button' ]
+ del kwd[ 'save_samples_button' ]
handle_error( **kwd )
sample_event_comment = util.restore_text( params.get( 'sample_event_comment', '' ) )
new_state = trans.sa_session.query( trans.model.SampleState ).get( trans.security.decode_id( sample_state_id ) )
@@ -1056,32 +1051,38 @@ class RequestsCommon( BaseController, Us
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 )
- # See if all the samples' barcodes 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:
- event = trans.model.RequestEvent( request,
- request.states.SUBMITTED,
- "All samples are in %s state." % common_state.name )
- trans.sa_session.add( event )
- trans.sa_session.flush()
- request.send_email_notification( trans, request.type.states[1] )
+ # 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.
+ common_state = request.samples_have_common_state
+ if common_state and common_state.id == request.type.states[1].id:
+ event = trans.model.RequestEvent( request,
+ request.states.SUBMITTED,
+ "All samples are in %s state." % common_state.name )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ request.send_email_notification( trans, request.type.states[1] )
message = 'Changes made to the samples have been saved. '
else:
- # Saving a newly created sample.
+ # Saving a newly created sample. The sample 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 configured
+ # by the admin ( i.e., the sample's SampleState would be set to request.type.states[0] ).
for index in range( len( samples ) - len( request.samples ) ):
sample_index = len( request.samples )
current_sample = samples[ sample_index ]
form_values = trans.model.FormValues( request.type.sample_form, current_sample[ 'field_values' ] )
trans.sa_session.add( form_values )
trans.sa_session.flush()
- s = trans.model.Sample( current_sample[ 'name' ],
- '',
- request,
- form_values,
- current_sample[ 'barcode' ],
- current_sample[ 'library' ],
- current_sample[ 'folder' ] )
+ s = trans.model.Sample( name=current_sample[ 'name' ],
+ desc='',
+ request=request,
+ form_values=form_values,
+ bar_code='',
+ library=current_sample[ 'library' ],
+ folder=current_sample[ 'folder' ] )
trans.sa_session.add( s )
trans.sa_session.flush()
return trans.response.send_redirect( web.url_for( controller='requests_common',
@@ -1113,49 +1114,55 @@ class RequestsCommon( BaseController, Us
if obj is not None:
# obj will be None if the user checked sample check boxes and selected an action
# to perform on multiple samples, but did not select certain samples.
- sample_updated = False
- # If this sample has values in kwd, then kwd will include a
- # key whose value is this sample's ( possibly changed ) name. An
- # example of this key is 'sample_0_name'.
- for k, v in kwd.items():
- name_key = 'sample_%i_name' % index
- if k == name_key:
- sample_updated = True
- break
- if sample_updated:
- id_index = index + 1
- if sample_operation == 'none':
- # We are handling changes to a single sample.
- library_id = params.get( 'sample_%i_library_id' % id_index, 'none' )
- folder_id = params.get( 'sample_%i_folder_id' % id_index, 'none' )
- # Update the corresponding sample's values as well as the sample_widget.
- sample = request.samples[ index ]
- sample.name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
- # The bar_code field requires special handling because after a request is submitted, the
- # state of a sample cannot be changed without a bar_code assocaited with the sample.
- bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
- if not bar_code and not sample.bar_code:
- # If this is a 'new' (still in its first state) sample, create an event
- if sample.state.id == request.states[0].id:
- event = trans.model.SampleEvent( sample,
- request.type.states[1],
- 'Sample added to the system' )
+ sample = request.samples[ index ]
+ # See if any values in kwd are different from the values already associated with this sample.
+ id_index = index + 1
+ if sample_operation == 'none':
+ # We are handling changes to a single sample.
+ library_id = params.get( 'sample_%i_library_id' % id_index, 'none' )
+ folder_id = params.get( 'sample_%i_folder_id' % id_index, 'none' )
+ # Update the corresponding sample's values as well as the sample_widget.
+ name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
+ # The bar_code field requires special handling because after a request is submitted, the
+ # state of a sample cannot be changed without a bar_code associated with the sample. Bar
+ # codes can only be added to a sample after the request is submitted. Also, a samples will
+ # not have an associated SampleState until the request is submitted, at which time the sample
+ # is automatically associated with the first SamplesState configured by the admin for the
+ # request's RequestType.
+ bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
+ if bar_code:
+ bc_message = self.__validate_barcode( trans, sample, bar_code )
+ if bc_message:
+ kwd[ 'message' ] = bc_message
+ del kwd[ 'save_samples_button' ]
+ handle_error( **kwd )
+ if not sample.bar_code:
+ # If the sample's associated SampleState is still the initial state
+ # configured by the admin for the request's RequestType, this must be
+ # the first time a bar code was added to the sample, so change it's state
+ # to the next associated SampleState.
+ if sample.state.id == request.type.states[0].id:
+ event = trans.app.model.SampleEvent(sample,
+ request.type.states[1],
+ 'Bar code associated with the sample' )
trans.sa_session.add( event )
trans.sa_session.flush()
- elif bar_code:
- bc_message = self.__validate_barcode( trans, sample, bar_code )
- if bc_message:
- kwd[ 'message' ] = bc_message
- del kwd[ 'save_samples_button' ]
- handle_error( **kwd )
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ field_values = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
+ form_values.content = field_values
+ if sample.name != name or \
+ sample.bar_code != bar_code or \
+ sample.library != library or \
+ sample.folder != folder or \
+ form_values.content != field_values:
+ # Information about this sample has been changed.
+ sample.name = name
sample.bar_code = bar_code
- library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
sample.library = library
sample.folder = folder
- field_values = []
- for field_index in range( len( request.type.sample_form.fields ) ):
- field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
- form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
form_values.content = field_values
trans.sa_session.add_all( ( sample, form_values ) )
trans.sa_session.flush()
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -9,8 +9,6 @@ import logging, os, pexpect, ConfigParse
log = logging.getLogger( __name__ )
-
-
class AdminRequestsGrid( RequestsGrid ):
class UserColumn( grids.TextColumn ):
def get_value( self, trans, grid, request ):
@@ -51,6 +49,7 @@ class RequestTypeGrid( grids.Grid ):
return request_type.sample_form.name
# Grid definition
+ webapp = "galaxy"
title = "Sequencer Configurations"
template = "admin/requests/grid.mako"
model_class = model.RequestType
@@ -103,6 +102,7 @@ class DataTransferGrid( grids.Grid ):
def get_value( self, trans, grid, sample_dataset ):
return sample_dataset.status
# Grid definition
+ webapp = "galaxy"
title = "Sample Datasets"
template = "admin/requests/grid.mako"
model_class = model.SampleDataset
@@ -129,9 +129,19 @@ class DataTransferGrid( grids.Grid ):
visible=False,
filterable="standard" ) )
operations = [
- grids.GridOperation( "Start Transfer", allow_multiple=True, condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ) ),
- grids.GridOperation( "Rename", allow_multiple=True, allow_popup=False, condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ) ),
- grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ) ),
+ grids.GridOperation( "Transfer",
+ allow_multiple=True,
+ condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ),
+ url_args=dict( webapp="galaxy" ) ),
+ grids.GridOperation( "Rename",
+ allow_multiple=True,
+ allow_popup=False,
+ condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ),
+ url_args=dict( webapp="galaxy" ) ),
+ grids.GridOperation( "Delete",
+ allow_multiple=True,
+ condition=( lambda item: item.status in [ model.SampleDataset.transfer_status.NOT_STARTED ] ),
+ url_args=dict( webapp="galaxy" ) )
]
def apply_query_filter( self, trans, query, **kwd ):
sample_id = kwd.get( 'sample_id', None )
@@ -158,6 +168,12 @@ class RequestsAdmin( BaseController, Use
action='edit_basic_request_info',
cntrller='requests_admin',
**kwd ) )
+ if operation == "edit_samples":
+ kwd[ 'editing_samples' ] = True
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller='requests_admin',
+ **kwd ) )
if operation == "view_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='view_request',
@@ -273,7 +289,7 @@ class RequestsAdmin( BaseController, Use
break
if no_datasets_transferred:
status = 'error'
- message = 'A dataset can be renamed only if it is in the "Not Started" state.'
+ message = 'A dataset can be renamed only if it has been transferred.'
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
sample_id=trans.security.encode_id( selected_sample_datasets[0].sample.id ),
@@ -282,7 +298,7 @@ class RequestsAdmin( BaseController, Use
return trans.fill_template( '/admin/requests/rename_datasets.mako',
sample=selected_sample_datasets[0].sample,
id_list=id_list )
- elif operation == "start transfer":
+ elif operation == "transfer":
self.__start_datatx( trans, selected_sample_datasets[0].sample, selected_sample_datasets )
# Render the grid view
sample_id = params.get( 'sample_id', None )
@@ -292,7 +308,7 @@ class RequestsAdmin( BaseController, Use
return invalid_id_redirect( trans, 'requests_admin', sample_id )
request_id = trans.security.encode_id( sample.request.id )
library_id = trans.security.encode_id( sample.library.id )
- self.datatx_grid.global_actions = [ grids.GridAction( "Refresh",
+ self.datatx_grid.global_actions = [ grids.GridAction( "Refresh page",
dict( controller='requests_admin',
action='manage_datasets',
sample_id=sample_id ) ),
@@ -302,11 +318,11 @@ class RequestsAdmin( BaseController, Use
request_id=request_id,
folder_path=sample.request.type.datatx_info[ 'data_dir' ],
sample_id=sample_id ) ),
- #grids.GridAction( 'Data library "%s"' % sample.library.name,
- # dict( controller='library_common',
- # action='browse_library',
- # cntrller='library_admin',
- # id=library_id ) ),
+ grids.GridAction( "Browse target data library",
+ dict( controller='library_common',
+ action='browse_library',
+ cntrller='library_admin',
+ id=library_id ) ),
grids.GridAction( "Browse this request",
dict( controller='requests_common',
action='view_request',
--- a/templates/requests/common/view_request.mako
+++ b/templates/requests/common/view_request.mako
@@ -18,15 +18,18 @@
from galaxy.web.framework.helpers import time_ago
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ is_complete = request.is_complete
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_submit = request.samples and is_unsubmitted
%><br/><br/><ul class="manage-table-actions">
- %if is_unsubmitted:
+ %if can_submit:
<li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
%endif
<li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
@@ -66,13 +69,12 @@
</div><div class="form-row"><label>User:</label>
- %if is_admin:
- ${request.user.email}
- %elif request.user.username:
- ${request.user.username}
- %else:
- Unknown
- %endif
+ ${request.user.email}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Sequencer configuration:</label>
+ ${request.type.name}
<div style="clear: both"></div></div><div class="form-row">
@@ -129,11 +131,6 @@
${states}
<div style="clear: both"></div></div>
- <div class="form-row">
- <label>Sequencer configuration:</label>
- ${request.type.name}
- <div style="clear: both"></div>
- </div></div></div></div>
@@ -141,7 +138,7 @@
<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_request, grid_header=grid_header )}
+ ${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 )}
%else:
There are no samples.
%if can_add_samples:
--- a/templates/grid_base.mako
+++ b/templates/grid_base.mako
@@ -752,11 +752,18 @@
%if grid.global_actions:
<ul class="manage-table-actions">
- %for action in grid.global_actions:
- <li>
- <a class="action-button" href="${h.url_for( **action.url_args )}">${action.label}</a>
- </li>
- %endfor
+ %if len( grid.global_actions ) < 4:
+ %for action in grid.global_actions:
+ <li><a class="action-button" href="${h.url_for( **action.url_args )}">${action.label}</a></li>
+ %endfor
+ %else:
+ <li><a class="action-button" id="action-8675309-popup" class="menubutton">Actions</a></li>
+ <div popupmenu="action-8675309-popup">
+ %for action in grid.global_actions:
+ <a class="action-button" href="${h.url_for( **action.url_args )}">${action.label}</a>
+ %endfor
+ </div>
+ %endif
</ul>
%endif
--- a/templates/requests/common/common.mako
+++ b/templates/requests/common/common.mako
@@ -88,26 +88,26 @@
%if is_admin and 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>
+ <td valign="top"><input type="text" name="sample_${current_sample_index}_name" value="${current_sample['name']}" size="10"/><div class="toolParamHelp" style="clear: both;"><i>${' (required)' }</i></div></td>
%if sample and is_submitted or is_complete:
- <td><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td>
+ <td valign="top"><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td>
%endif
%if sample:
%if is_unsubmitted:
<td>Unsubmitted</td>
%else:
- <td><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
+ <td valign="top"><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
%endif
%else:
<td></td>
%endif
- <td>${current_sample['library_select_field'].get_html()}</td>
- <td>${current_sample['folder_select_field'].get_html()}</td>
+ <td valign="top">${current_sample['library_select_field'].get_html()}</td>
+ <td valign="top">${current_sample['folder_select_field'].get_html()}</td>
%if is_submitted or is_complete:
<%
if sample:
@@ -115,12 +115,12 @@
else:
label = 'add'
%>
- <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
- <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
+ <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 ):
## Delete button
- <td><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>
+ <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
</%def>
@@ -132,7 +132,7 @@
is_submitted = request.is_submitted
is_unsubmitted = request.is_unsubmitted
can_add_samples = request.is_unsubmitted
- can_edit_or_delete_samples = request.samples and ( is_admin or request.is_unsubmitted )
+ can_edit_or_delete_samples = request.samples and not is_complete
%>
${grid_header}
%if render_buttons and ( can_add_samples or can_edit_or_delete_samples ):
1
0

galaxy-dist commit 667043341e81: Significant cleanup for the Sample Tracking UI. Things are more streamlined and Galaxy UI standards are now followed.
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 1288649810 14400
# Node ID 667043341e81261a7fe587daa475110486b15292
# Parent c4d8ffb3109e8cfb8143dc87c88dee3337bb7569
Significant cleanup for the Sample Tracking UI. Things are more streamlined and Galaxy UI standards are now followed.
--- a/templates/requests/common/sample_events.mako
+++ b/templates/requests/common/sample_events.mako
@@ -5,10 +5,7 @@
<h2>Events for Sample "${sample.name}"</h2><ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">
- <span>Browse this request</span></a>
- </li>
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a></li></ul><h3>Sequencing Request "${sample.request.name}"</h3>
--- /dev/null
+++ b/templates/requests/common/edit_samples.mako
@@ -0,0 +1,240 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+<%namespace file="/requests/common/common.mako" import="common_javascripts" />
+<%namespace file="/requests/common/common.mako" import="render_samples_grid" />
+<%namespace file="/requests/common/common.mako" import="render_request_type_sample_form_grids" />
+
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "library" )}
+</%def>
+
+<%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>
+
+<%
+ from galaxy.web.framework.helpers import time_ago
+
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ is_complete = request.is_complete
+ is_unsubmitted = request.is_unsubmitted
+ can_add_samples = is_unsubmitted
+ can_edit_or_delete_samples = is_unsubmitted and request.samples
+ can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
+ can_reject_or_transfer = is_admin and request.is_submitted
+%>
+
+<br/><br/>
+
+<ul class="manage-table-actions">
+ %if not editing_samples and can_edit_or_delete_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:
+ <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 is_unsubmitted:
+ <li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
+ %endif
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
+ <div popupmenu="request-${request.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
+ %if can_edit_request:
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">View history</a>
+ %if can_reject_or_transfer:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
+ %endif
+ </div>
+</ul>
+
+%if request.samples_without_library_destinations:
+ <br/>
+ <font color="red"><b><i>Select a target data library and folder for all samples before starting the sequence run</i></b></font>
+ <br/>
+%endif
+
+%if request.is_rejected:
+ <br/>
+ <font color="red"><b><i>Reason for rejection: </i></b></font><b>${request.last_comment}</b>
+ <br/>
+%endif
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolFormBody">
+ <form id="edit_samples" name="edit_samples" action="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples=editing_samples )}" method="post">
+ %if current_samples:
+ <%
+ if editing_samples:
+ grid_header = '<h3>Edit Current Samples of Request "%s"</h3>' % request.name
+ else:
+ 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 ):
+ <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:
+ <div class="form-row" style="background-color:#FAFAFA;">
+ %if sample_operation_selected_value == trans.model.Sample.bulk_operations.CHANGE_STATE:
+ ## sample_operation_selected_value == 'Change state'
+ <div class="form-row">
+ <label>Change current state</label>
+ ${sample_state_id_select_field.get_html()}
+ <label>Comments</label>
+ <input type="text" name="sample_event_comment" value=""/>
+ <div class="toolParamHelp" style="clear: both;">
+ Optional
+ </div>
+ </div>
+ %elif 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>
+ ${libraries_select_field.get_html()}
+ </div>
+ %if libraries_selected_value != 'none':
+ <div class="form-row">
+ <label>Select folder:</label>
+ ${folders_select_field.get_html()}
+ </div>
+ %endif
+ %endif
+ </div>
+ %endif
+ %endif
+ ## Render the other grids
+ <% trans.sa_session.refresh( request.type.sample_form ) %>
+ %for grid_index, grid_name in enumerate( request.type.sample_form.layout ):
+ ${render_request_type_sample_form_grids( grid_index, grid_name, request.type.sample_form.grid_fields( grid_index ), editing_samples=editing_samples )}
+ %endfor
+ %else:
+ <label>There are no samples.</label>
+ %endif
+ %if not editing_samples and is_unsubmitted:
+ ## The user is adding a new sample
+ %if current_samples:
+ <p/>
+ <div class="form-row">
+ <label> Copy <input type="text" name="num_sample_to_copy" value="1" size="3"/> samples from sample ${sample_copy.get_html()}</label>
+ <div class="toolParamHelp" style="clear: both;">
+ Select the sample from which the new sample should be copied or leave selection as <b>None</b> to add a new "generic" sample.
+ </div>
+ </div>
+ %endif
+ <p/>
+ <div class="form-row">
+ %if ( request.samples or current_samples ) and ( editing_samples or len( current_samples ) > len( request.samples ) ):
+ <input type="submit" name="add_sample_button" value="Add sample"/>
+ <input type="submit" name="save_samples_button" value="Save"/>
+ <input type="submit" name="cancel_changes_button" value="Cancel"/>
+ <div class="toolParamHelp" style="clear: both;">
+ Click the <b>Add sample</b> button for each new sample and click the <b>Save</b> button when you have finished adding samples.
+ </div>
+ %else:
+ <input type="submit" name="add_sample_button" value="Add sample"/>
+ <div class="toolParamHelp" style="clear: both;">
+ Click the <b>Add sample</b> button for each new sample.
+ </div>
+ %endif
+ </div>
+ %elif editing_samples:
+ <p/>
+ <div class="form-row">
+ <input type="submit" name="save_samples_button" value="Save"/>
+ <input type="submit" name="cancel_changes_button" value="Cancel"/>
+ <div class="toolParamHelp" style="clear: both;">
+ 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
+ </form>
+</div>
+%if is_unsubmitted and not editing_samples:
+ <p/>
+ ##<div class="toolForm">
+ ##<div class="toolFormTitle">Import samples from csv file</div>
+ <h4><img src="/static/images/fugue/toggle-expand.png" alt="Hide" onclick="showContent(this);" style="cursor:pointer;"/> Import samples from csv file</h4>
+ <div style="display:none;">
+ <div class="toolFormBody">
+ <form id="import" name="import" action="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( request.id ), editing_samples=editing_samples )}" enctype="multipart/form-data" method="post" >
+ <div class="form-row">
+ <input type="file" name="file_data" />
+ <input type="submit" name="import_samples_button" value="Import samples"/>
+ <div class="toolParamHelp" style="clear: both;">
+ The csv file must be in the following format:<br/>
+ SampleName,DataLibrary,DataLibraryFolder,FieldValue1,FieldValue2...
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ ##</div>
+%endif
--- a/test/functional/test_forms_and_requests.py
+++ b/test/functional/test_forms_and_requests.py
@@ -225,15 +225,12 @@ class TestFormsAndRequests( TwillTestCas
strings_displayed_after_submit = [ 'Unsubmitted' ]
for sample_name, field_values in sample_value_tuples:
strings_displayed_after_submit.append( sample_name )
- for field_value in field_values:
- strings_displayed_after_submit.append( field_value )
# 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=[ 'Sequencing Request "%s"' % request_one.name,
- 'There are no samples.' ],
+ 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"""
@@ -277,11 +274,11 @@ class TestFormsAndRequests( TwillTestCas
self.check_request_grid( cntrller='requests_admin',
state=request_one.states.SUBMITTED,
strings_displayed=[ request_one.name ] )
- self.visit_url( "%s/requests_common/manage_request?cntrller=requests&id=%s" % ( self.url, self.security.encode_id( request_one.id ) ) )
- self.check_page_for_string( 'Sequencing Request "%s"' % request_one.name )
+ 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 are saved.' ]
+ 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 ),
@@ -333,7 +330,7 @@ class TestFormsAndRequests( TwillTestCas
test_field_name1,
test_field_name2,
test_field_name3 ],
- strings_displayed_after_submit=[ "The request has been created" ] )
+ 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
@@ -349,15 +346,12 @@ class TestFormsAndRequests( TwillTestCas
strings_displayed_after_submit = [ 'Unsubmitted' ]
for sample_name, field_values in sample_value_tuples:
strings_displayed_after_submit.append( sample_name )
- for field_value in field_values:
- strings_displayed_after_submit.append( field_value )
# 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=[ 'Sequencing Request "%s"' % request_two.name,
- 'There are no samples.' ],
+ strings_displayed=[ 'There are no samples.' ],
strings_displayed_after_submit=strings_displayed_after_submit )
# Submit the request
self.submit_request( cntrller='requests_admin',
@@ -394,6 +388,7 @@ class TestFormsAndRequests( TwillTestCas
% ( request_two.name, request_two.states.REJECTED )
def test_055_reset_data_for_later_test_runs( self ):
"""Reseting data to enable later test runs to pass"""
+ """
# Logged in as admin_user
##################
# Delete request_type permissions
@@ -438,3 +433,4 @@ class TestFormsAndRequests( TwillTestCas
# Manually delete the group from the database
refresh( group )
delete( group )
+ """
--- a/templates/admin/requests/rename_datasets.mako
+++ b/templates/admin/requests/rename_datasets.mako
@@ -10,7 +10,7 @@
<a class="action-button" href="${h.url_for( controller='requests_admin', action='manage_datasets', sample_id=trans.security.encode_id( sample.id ) )}">Browse datasets</a></li><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller='requests_admin', id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller='requests_admin', id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a></li></ul>
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -158,9 +158,9 @@ class RequestsAdmin( BaseController, Use
action='edit_basic_request_info',
cntrller='requests_admin',
**kwd ) )
- if operation == "manage_request":
+ if operation == "view_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='view_request',
cntrller='requests_admin',
**kwd ) )
if operation == "request_events":
@@ -186,14 +186,14 @@ class RequestsAdmin( BaseController, Use
return self.request_grid( trans, **kwd )
@web.expose
@web.require_admin
- def reject( self, trans, **kwd ):
+ def reject_request( self, trans, **kwd ):
params = util.Params( kwd )
request_id = params.get( 'id', '' )
status = params.get( 'status', 'done' )
message = params.get( 'message', 'done' )
if params.get( 'cancel_reject_button', False ):
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='view_request',
cntrller='requests_admin',
id=request_id ) )
try:
@@ -292,7 +292,6 @@ class RequestsAdmin( BaseController, Use
return invalid_id_redirect( trans, 'requests_admin', sample_id )
request_id = trans.security.encode_id( sample.request.id )
library_id = trans.security.encode_id( sample.library.id )
- self.datatx_grid.title = 'Datasets of sample "%s"' % sample.name
self.datatx_grid.global_actions = [ grids.GridAction( "Refresh",
dict( controller='requests_admin',
action='manage_datasets',
@@ -303,14 +302,14 @@ class RequestsAdmin( BaseController, Use
request_id=request_id,
folder_path=sample.request.type.datatx_info[ 'data_dir' ],
sample_id=sample_id ) ),
- grids.GridAction( 'Data library "%s"' % sample.library.name,
- dict( controller='library_common',
- action='browse_library',
- cntrller='library_admin',
- id=library_id ) ),
+ #grids.GridAction( 'Data library "%s"' % sample.library.name,
+ # dict( controller='library_common',
+ # action='browse_library',
+ # cntrller='library_admin',
+ # id=library_id ) ),
grids.GridAction( "Browse this request",
dict( controller='requests_common',
- action='manage_request',
+ action='view_request',
cntrller='requests_admin',
id=request_id ) ) ]
return self.datatx_grid( trans, **kwd )
--- a/templates/requests/common/edit_basic_request_info.mako
+++ b/templates/requests/common/edit_basic_request_info.mako
@@ -1,14 +1,25 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
+<%
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ can_add_samples = request.is_unsubmitted
+%>
+
<br/><br/><ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse all requests</a>
- </li>
+ <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_add_samples:
+ <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">View history</a>
+ %if is_admin and request.is_submitted:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
+ %endif
+ </div></ul>
%if message:
--- /dev/null
+++ b/templates/requests/common/view_request.mako
@@ -0,0 +1,157 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+<%namespace file="/requests/common/common.mako" import="common_javascripts" />
+<%namespace file="/requests/common/common.mako" import="render_samples_grid" />
+<%namespace file="/requests/common/common.mako" import="render_request_type_sample_form_grids" />
+
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "library" )}
+</%def>
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${common_javascripts()}
+</%def>
+
+<%
+ from galaxy.web.framework.helpers import time_ago
+
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ is_unsubmitted = request.is_unsubmitted
+ can_edit_request = ( is_admin and not request.is_complete ) or request.is_unsubmitted
+ can_add_samples = is_unsubmitted
+%>
+
+<br/><br/>
+
+<ul class="manage-table-actions">
+ %if is_unsubmitted:
+ <li><a class="action-button" confirm="More samples cannot be added to this request after it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit request</a></li>
+ %endif
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request actions</a></li>
+ <div popupmenu="request-${request.id}-popup">
+ %if can_edit_request:
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">View history</a>
+ %if is_admin and request.is_submitted:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
+ %endif
+ </div>
+</ul>
+
+%if request.is_rejected:
+ <font color="red"><b><i>Reason for rejection: </i></b></font><b>${request.last_comment}</b>
+ <br/><br/>
+%endif
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolForm">
+ <div class="toolFormTitle">Sequencing request "${request.name}"</div>
+ <div class="toolFormBody">
+ <div class="form-row">
+ <label>Current state:</label>
+ ${request.state}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Description:</label>
+ ${request.desc}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>User:</label>
+ %if is_admin:
+ ${request.user.email}
+ %elif request.user.username:
+ ${request.user.username}
+ %else:
+ Unknown
+ %endif
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> More</h4>
+ <div style="display:none;">
+ %for index, rd in enumerate( request_widgets ):
+ <%
+ field_label = rd[ 'label' ]
+ field_value = rd[ 'value' ]
+ %>
+ <div class="form-row">
+ <label>${field_label}:</label>
+ %if field_label == 'State':
+ <a href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">${field_value}</a>
+ %else:
+ ${field_value}
+ %endif
+ </div>
+ <div style="clear: both"></div>
+ %endfor
+ <div class="form-row">
+ <label>Date created:</label>
+ ${request.create_time}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Last updated:</label>
+ ${time_ago( request.update_time )}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Email recipients:</label>
+ <%
+ if request.notification:
+ emails = ', '.join( request.notification[ 'email' ] )
+ else:
+ emails = ''
+ %>
+ ${emails}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Send email when state changes to:</label>
+ <%
+ if request.notification:
+ states = []
+ for ss in request.type.states:
+ if ss.id in request.notification[ 'sample_states' ]:
+ states.append( ss.name )
+ states = ', '.join( states )
+ else:
+ states = ''
+ %>
+ ${states}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Sequencer configuration:</label>
+ ${request.type.name}
+ <div style="clear: both"></div>
+ </div>
+ </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_request, grid_header=grid_header )}
+%else:
+ There are no samples.
+ %if can_add_samples:
+ <ul class="manage-table-actions">
+ <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>
+ </ul>
+ %endif
+%endif
+## Render the other grids
+<% trans.sa_session.refresh( request.type.sample_form ) %>
+%for grid_index, grid_name in enumerate( request.type.sample_form.layout ):
+ ${render_request_type_sample_form_grids( grid_index, grid_name, request.type.sample_form.grid_fields( grid_index ), editing_samples=False )}
+%endfor
--- a/lib/galaxy/web/controllers/library.py
+++ b/lib/galaxy/web/controllers/library.py
@@ -24,13 +24,11 @@ class LibraryListGrid( grids.Grid ):
columns = [
NameColumn( "Name",
key="name",
- model_class=model.Library,
link=( lambda library: dict( operation="browse", id=library.id ) ),
attach_popup=False,
filterable="advanced" ),
DescriptionColumn( "Description",
key="description",
- model_class=model.Library,
attach_popup=False,
filterable="advanced" ),
]
--- a/templates/admin/forms/show_form_read_only.mako
+++ b/templates/admin/forms/show_form_read_only.mako
@@ -81,10 +81,10 @@
<form name="library" action="${h.url_for( controller='forms', action='manage' )}" method="post" >
%if form_definition.type == trans.app.model.FormDefinition.types.SAMPLE:
%if not len(form_definition.layout):
- ${render_grid( 0, '', form_definition.fields_of_grid( None ) )}
+ ${render_grid( 0, '', form_definition.grid_fields( None ) )}
%else:
%for grid_index, grid_name in enumerate(form_definition.layout):
- ${render_grid( grid_index, grid_name, form_definition.fields_of_grid( grid_index ) )}
+ ${render_grid( grid_index, grid_name, form_definition.grid_fields( grid_index ) )}
%endfor
%endif
%else:
--- a/templates/library/common/browse_library.mako
+++ b/templates/library/common/browse_library.mako
@@ -346,7 +346,6 @@
><td style="padding-left: ${folder_pad}px;"><input type="checkbox" class="folderCheckbox"/>
-
%if folder.deleted:
<span class="libraryItem-error">
%endif
@@ -354,7 +353,6 @@
<div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id}-popup"><a href="javascript:void(0);">${folder.name}</a></div>
-
%if folder.deleted:
</span>
%endif
--- a/templates/admin/requests/reject.mako
+++ b/templates/admin/requests/reject.mako
@@ -8,16 +8,16 @@
<h2>Reject Sequencing Request "${request.name}"</h2><ul class="manage-table-actions"><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">Events</a>
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">View history</a></li><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id(request.id) )}">Browse this request</a>
+ <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></li></ul><div class="toolForm"><div class="toolFormTitle">Reject request</div>
- <form name="event" action="${h.url_for( controller='requests_admin', action='reject', id=trans.security.encode_id( request.id ) )}" method="post" >
+ <form name="event" action="${h.url_for( controller='requests_admin', action='reject_request', id=trans.security.encode_id( request.id ) )}" method="post" ><div class="form-row">
Rejecting this request will move the request state to <b>Rejected</b>.
</div>
--- a/templates/admin/requests/get_data.mako
+++ b/templates/admin/requests/get_data.mako
@@ -75,7 +75,7 @@
<a class="action-button" href="${h.url_for( controller='requests_admin', action='view_request_type', id=trans.security.encode_id( request.type.id ) )}">Sequencer configuration "${request.type.name}"</a></li><li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
+ <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></li></ul>
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1488,25 +1488,23 @@ class MetadataFile( object ):
return os.path.abspath( os.path.join( path, "metadata_%d.dat" % self.id ) )
class FormDefinition( object ):
- types = Bunch( REQUEST = 'Sequencing Request Form',
- SAMPLE = 'Sequencing Sample Form',
- LIBRARY_INFO_TEMPLATE = 'Library information template',
- USER_INFO = 'User Information' )
- def __init__(self, name=None, desc=None, fields=[],
- form_definition_current=None, form_type=None, layout=None):
+ types = Bunch( REQUEST = 'Sequencing Request Form',
+ SAMPLE = 'Sequencing Sample Form',
+ LIBRARY_INFO_TEMPLATE = 'Library information template',
+ USER_INFO = 'User Information' )
+ def __init__( self, name=None, desc=None, fields=[], form_definition_current=None, form_type=None, layout=None ):
self.name = name
self.desc = desc
self.fields = fields
self.form_definition_current = form_definition_current
self.type = form_type
self.layout = layout
- def fields_of_grid(self, grid_index):
- '''
- This method returns the list of fields belonging to the given grid.
- '''
+ def grid_fields( self, grid_index ):
+ # Returns a dictionary whose keys are integers corresponding to field positions
+ # on the grid and whose values are the field.
gridfields = {}
- for i, f in enumerate(self.fields):
- if str(f['layout']) == str(grid_index):
+ for i, f in enumerate( self.fields ):
+ if str( f[ 'layout' ] ) == str( grid_index ):
gridfields[i] = f
return gridfields
def get_widgets( self, user, contents=[], **kwd ):
--- a/lib/galaxy/web/controllers/requests.py
+++ b/lib/galaxy/web/controllers/requests.py
@@ -10,11 +10,10 @@ log = logging.getLogger( __name__ )
class UserRequestsGrid( RequestsGrid ):
operations = [ operation for operation in RequestsGrid.operations ]
- operations.append( grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted and item.is_unsubmitted ) ) )
- operations.append( grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted and item.is_new ) ) )
+ operations.append( grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: item.is_unsubmitted and not item.deleted ) ) )
+ operations.append( grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.is_new and not item.deleted ) ) )
operations.append( grids.GridOperation( "Undelete", allow_multiple=True, condition=( lambda item: item.deleted ) ) )
def apply_query_filter( self, trans, query, **kwd ):
- # gvk ( 9/28/10 ) TODO: is this method needed?
return query.filter_by( user=trans.user )
class Requests( BaseController ):
@@ -37,9 +36,9 @@ class Requests( BaseController ):
action='edit_basic_request_info',
cntrller='requests',
**kwd ) )
- if operation == "manage_request":
+ if operation == "view_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='view_request',
cntrller='requests',
**kwd ) )
if operation == "delete":
@@ -70,12 +69,12 @@ class Requests( BaseController ):
message = "%d requests (highlighted in red) were rejected. Click on the request name for details." % rejected
kwd[ 'status' ] = status
kwd[ 'message' ] = message
- # show the create request button to the user, only when the user has permissions
- # to at least one request_type (sequencer configuration)
+ # 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 ) ):
self.request_grid.global_actions = [ grids.GridAction( "Create new request", dict( controller='requests_common',
- action='create_request',
- cntrller='requests' ) ) ]
+ action='create_request',
+ cntrller='requests' ) ) ]
else:
self.request_grid.global_actions = []
# Render the list view
--- a/templates/requests/common/find_samples.mako
+++ b/templates/requests/common/find_samples.mako
@@ -76,7 +76,7 @@
<i>User: ${sample.request.user.email}</i>
%endif
<div class="toolParamHelp" style="clear: both;">
- <a href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Sequencing request: ${sample.request.name} | Type: ${sample.request.type.name} | State: ${sample.request.state}</a>
+ <a href="${h.url_for( controller='requests_common', action='edit_samples', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Sequencing request: ${sample.request.name} | Type: ${sample.request.type.name} | State: ${sample.request.state}</a></div></div><br/>
--- a/lib/galaxy/web/controllers/forms.py
+++ b/lib/galaxy/web/controllers/forms.py
@@ -86,7 +86,7 @@ class Forms( BaseController ):
status='error',
message="Invalid form ID") )
if operation == "view":
- return self.__view( trans, **kwd )
+ return self.view_form_definition( trans, **kwd )
elif operation == "delete":
return self.__delete( trans, **kwd )
elif operation == "undelete":
@@ -94,10 +94,12 @@ class Forms( BaseController ):
elif operation == "edit":
return self.edit( trans, **kwd )
return self.forms_grid( trans, **kwd )
- def __view(self, trans, **kwd):
+ @web.expose
+ def view_form_definition( self, trans, **kwd ):
+ form_definition_current_id = kwd.get( 'id', None )
try:
- fdc = trans.sa_session.query( trans.app.model.FormDefinitionCurrent )\
- .get( trans.security.decode_id(kwd['id']) )
+ fdc = trans.sa_session.query( trans.app.model.FormDefinitionCurrent ) \
+ .get( trans.security.decode_id( form_definition_current_id ) )
except:
return trans.response.send_redirect( web.url_for( controller='forms',
action='manage',
--- /dev/null
+++ b/templates/requests/common/common.mako
@@ -0,0 +1,332 @@
+<%namespace file="/requests/common/sample_state.mako" import="render_sample_state" />
+
+<%def name="javascripts()">
+ ${self.common_javascripts()}
+</%def>
+
+<%def name="common_javascripts()">
+ <script type="text/javascript">
+ function showContent(vThis)
+ {
+ // http://www.javascriptjunkie.com
+ // alert(vSibling.className + " " + vDef_Key);
+ vParent = vThis.parentNode;
+ vSibling = vParent.nextSibling;
+ while (vSibling.nodeType==3) {
+ // Fix for Mozilla/FireFox Empty Space becomes a TextNode or Something
+ vSibling = vSibling.nextSibling;
+ };
+ if(vSibling.style.display == "none")
+ {
+ vThis.src="/static/images/fugue/toggle.png";
+ vThis.alt = "Hide";
+ vSibling.style.display = "block";
+ } else {
+ vSibling.style.display = "none";
+ vThis.src="/static/images/fugue/toggle-expand.png";
+ vThis.alt = "Show";
+ }
+ return;
+ }
+ $(document).ready(function(){
+ //hide the all of the element with class msg_body
+ $(".msg_body").hide();
+ //toggle the component with class msg_body
+ $(".msg_head").click(function(){
+ $(this).next(".msg_body").slideToggle(0);
+ });
+ });
+
+ function checkAllFields()
+ {
+ var chkAll = document.getElementById('checkAll');
+ var checks = document.getElementsByTagName('input');
+ var boxLength = checks.length;
+ var allChecked = false;
+ var totalChecked = 0;
+ if ( chkAll.checked == true )
+ {
+ for ( i=0; i < boxLength; i++ )
+ {
+ if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
+ {
+ checks[i].checked = true;
+ }
+ }
+ }
+ else
+ {
+ for ( i=0; i < boxLength; i++ )
+ {
+ if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
+ {
+ checks[i].checked = false
+ }
+ }
+ }
+ }
+ </script>
+</%def>
+
+<%def name="render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )">
+ <%
+ if sample:
+ is_complete = sample.request.is_complete
+ is_submitted = sample.request.is_submitted
+ is_unsubmitted = sample.request.is_unsubmitted
+ else:
+ is_complete = False
+ is_submitted = False
+ is_unsubmitted = False
+ %>
+ <%
+ if is_admin and 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:
+ <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>
+ <input type="text" name="sample_${current_sample_index}_name" value="${current_sample['name']}" size="10"/>
+ <div class="toolParamHelp" style="clear: both;">
+ <i>${' (required)' }</i>
+ </div>
+ </td>
+ %if sample and is_submitted or is_complete:
+ <td><input type="text" name="sample_${current_sample_index}_barcode" value="${current_sample['barcode']}" size="10"/></td>
+ %endif
+ %if sample:
+ %if is_unsubmitted:
+ <td>Unsubmitted</td>
+ %else:
+ <td><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
+ %endif
+ %else:
+ <td></td>
+ %endif
+ <td>${current_sample['library_select_field'].get_html()}</td>
+ <td>${current_sample['folder_select_field'].get_html()}</td>
+ %if is_submitted or is_complete:
+ <%
+ if sample:
+ label = str( len( sample.datasets ) )
+ else:
+ label = 'add'
+ %>
+ <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
+ <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
+ %endif
+ %if sample and ( is_admin or is_unsubmitted ):
+ ## Delete button
+ <td><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
+</%def>
+
+<%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
+ <%
+ 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 ( is_admin or request.is_unsubmitted )
+ %>
+ ${grid_header}
+ %if render_buttons and ( can_add_samples or can_edit_or_delete_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:
+ <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>
+ %endif
+ <table class="grid">
+ <thead>
+ <tr>
+ %if is_admin and 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>
+ %if is_submitted or is_complete:
+ <th>Barcode</th>
+ %endif
+ <th>State</th>
+ <th>Data Library</th>
+ <th>Folder</th>
+ %if is_submitted or is_complete:
+ <th>Datasets Selected</th>
+ <th>Datasets Transferred</th>
+ %endif
+ <th>
+ %if editing_samples:
+ Delete
+ %endif
+ </th>
+ </tr>
+ <thead>
+ <tbody>
+ <% trans.sa_session.refresh( request ) %>
+ ## current_samples is a dictionary whose keys are:
+ ## name, barcode, library, folder, field_values, library_select_field, folder_select_field
+ %for current_sample_index, current_sample in enumerate( current_samples ):
+ <%
+ current_sample_name = current_sample[ 'name' ]
+ current_sample_barcode = current_sample[ 'barcode' ]
+ current_sample_library = current_sample[ 'library' ]
+ if current_sample_library:
+ if cntrller == 'requests':
+ library_cntrller = 'library'
+ elif is_admin:
+ library_cntrller = 'library_admin'
+ else:
+ library_cntrller = None
+ current_sample_folder = current_sample[ 'folder' ]
+ try:
+ sample = request.samples[ current_sample_index ]
+ except:
+ sample = None
+ %>
+ %if editing_samples:
+ <tr>${render_editable_sample_row( is_admin, sample, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
+ %elif sample:
+ <tr>
+ <td>${current_sample_name}</td>
+ %if is_submitted or is_complete:
+ <td>${current_sample_barcode}</td>
+ %endif
+ %if is_unsubmitted:
+ <td>Unsubmitted</td>
+ %else:
+ <td><a id="sampleState-${sample.id}" href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${render_sample_state( sample )}</a></td>
+ %endif
+ %if current_sample_library and library_cntrller is not None:
+ <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller=library_cntrller, id=trans.security.encode_id( current_sample_library.id ) )}">${current_sample_library.name}</a></td>
+ %else:
+ <td></td>
+ %endif
+ %if current_sample_folder:
+ <td>${current_sample_folder.name}</td>
+ %else:
+ <td></td>
+ %endif
+ %if is_submitted or is_complete:
+ <td><a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.datasets )}</a></td>
+ <td><a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${len( sample.transferred_dataset_files )}</a></td>
+ %endif
+ </tr>
+ %else:
+ <tr>${render_editable_sample_row( is_admin, None, current_sample_index, current_sample, encoded_selected_sample_ids )}</tr>
+ %endif
+ %endfor
+ </tbody>
+ </table>
+</%def>
+
+<%def name="render_sample_form( index, sample_name, sample_values, fields_dict, display_only )">
+ <tr>
+ <td>${sample_name}</td>
+ %for field_index, field in fields_dict.items():
+ <%
+ field_type = field[ 'type' ]
+ %>
+ <td>
+ %if display_only:
+ %if sample_values[field_index]:
+ %if field_type == 'WorkflowField':
+ %if str(sample_values[field_index]) != 'none':
+ <% workflow = trans.sa_session.query( trans.app.model.StoredWorkflow ).get( int( sample_values[ field_index ] ) ) %>
+ <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id( workflow.id ) )}">${workflow.name}</a>
+ %endif
+ %else:
+ ${sample_values[ field_index ]}
+ %endif
+ %else:
+ <i>None</i>
+ %endif
+ %else:
+ %if field_type == 'TextField':
+ <input type="text" name="sample_${index}_field_${field_index}" value="${sample_values[field_index]}" size="7"/>
+ %elif field_type == 'SelectField':
+ <select name="sample_${index}_field_${field_index}" last_selected_value="2">
+ %for option_index, option in enumerate(field['selectlist']):
+ %if option == sample_values[field_index]:
+ <option value="${option}" selected>${option}</option>
+ %else:
+ <option value="${option}">${option}</option>
+ %endif
+ %endfor
+ </select>
+ %elif field_type == 'WorkflowField':
+ <select name="sample_${index}_field_${field_index}">
+ %if str(sample_values[field_index]) == 'none':
+ <option value="none" selected>Select one</option>
+ %else:
+ <option value="none">Select one</option>
+ %endif
+ %for option_index, option in enumerate(request.user.stored_workflows):
+ %if not option.deleted:
+ %if str(option.id) == str(sample_values[field_index]):
+ <option value="${option.id}" selected>${option.name}</option>
+ %else:
+ <option value="${option.id}">${option.name}</option>
+ %endif
+ %endif
+ %endfor
+ </select>
+ %elif field_type == 'CheckboxField':
+ <input type="checkbox" name="sample_${index}_field_${field_index}" value="Yes"/>
+ %endif
+ <div class="toolParamHelp" style="clear: both;">
+ <i>${'('+field['required']+')' }</i>
+ </div>
+ %endif
+ </td>
+ %endfor
+ </tr>
+</%def>
+
+<%def name="render_request_type_sample_form_grids( grid_index, grid_name, fields_dict, editing_samples )">
+ <%
+ if not grid_name:
+ grid_name = "Sample form layout " + grid_index
+ %>
+ <h4><img src="/static/images/fugue/toggle-expand.png" alt="Hide" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
+ <div style="display:none;">
+ <table class="grid">
+ <thead>
+ <tr>
+ <th>Name</th>
+ %for index, field in fields_dict.items():
+ <th>
+ ${field['label']}
+ ## TODO: help comments in the grid header are UGLY!
+ ## If they are needed display them more appropriately,
+ ## if they are not, delete this commented code.
+ ##<div class="toolParamHelp" style="clear: both;">
+ ## <i>${field['helptext']}</i>
+ ##</div>
+ </th>
+ %endfor
+ <th></th>
+ </tr>
+ <thead>
+ <tbody>
+ <% trans.sa_session.refresh( request ) %>
+ %for sample_index, sample in enumerate( current_samples ):
+ <%
+ if editing_samples or sample_index >= len( request.samples ):
+ display_only = False
+ else:
+ display_only = True
+ %>
+ ${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict, display_only )}
+ %endfor
+ </tbody>
+ </table>
+ </div>
+</%def>
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -1489,17 +1489,17 @@ class TwillTestCase( unittest.TestCase )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
def add_samples( self, cntrller, request_id, request_name, sample_value_tuples, strings_displayed=[], strings_displayed_after_submit=[] ):
- self.visit_url( "%s/requests_common/manage_request?cntrller=%s&id=%s" % ( self.url, cntrller, request_id ) )
+ self.visit_url( "%s/requests_common/edit_samples?cntrller=%s&id=%s&editing_samples=False" % ( self.url, cntrller, request_id ) )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
# Simulate clicking the add-sample_button on the form. (gvk: 9/21/10 - TODO : There must be a bug in the mako template
# because twill cannot find any forms on the page, but I cannot find it although I've spent time cleaning up the
# template code and looking for any problems.
- url = "%s/requests_common/manage_request?cntrller=%s&id=%s" % ( self.url, cntrller, request_id )
+ url = "%s/requests_common/edit_samples?cntrller=%s&id=%s&editing_samples=False" % ( self.url, cntrller, request_id )
# This should work, but although twill does not thorw any exceptions, the button click never occurs
- # There are multiple forms on this page, and we'll only be using the form named manage_request.
+ # There are multiple forms on this page, and we'll only be using the form named edit_samples.
# for sample_index, sample_value_tuple in enumerate( sample_value_tuples ):
- # # Add the following form value to the already populated hidden field so that the manage_request
+ # # Add the following form value to the already populated hidden field so that the edit_samples
# # form is the current form
# tc.fv( "1", "id", request_id )
# tc.submit( 'add_sample_button' )
@@ -1530,7 +1530,7 @@ class TwillTestCase( unittest.TestCase )
for check_str in strings_displayed_after_submit:
self.check_page_for_string( check_str )
def reject_request( self, request_id, request_name, comment, strings_displayed=[], strings_displayed_after_submit=[] ):
- self.visit_url( "%s/requests_admin/reject?id=%s" % ( self.url, request_id ) )
+ self.visit_url( "%s/requests_admin/reject_request?id=%s" % ( self.url, request_id ) )
for check_str in strings_displayed:
self.check_page_for_string( check_str )
tc.fv( "1", "comment", comment )
@@ -1540,7 +1540,7 @@ class TwillTestCase( unittest.TestCase )
def add_bar_codes( self, request_id, request_name, bar_codes, samples, strings_displayed_after_submit=[] ):
# We have to simulate the form submission here since twill barfs on the page
# gvk - 9/22/10 - TODO: make sure the mako template produces valid html
- url = "%s/requests_common/manage_request?cntrller=requests_admin&id=%s&managing_samples=True" % ( self.url, request_id )
+ 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( ' ', '+' )
@@ -1555,15 +1555,15 @@ class TwillTestCase( unittest.TestCase )
strings_displayed=[], strings_displayed_after_submit=[] ):
# We have to simulate the form submission here since twill barfs on the page
# gvk - 9/22/10 - TODO: make sure the mako template produces valid html
- url = "%s/requests_common/manage_request?cntrller=requests_admin&id=%s" % ( self.url, request_id )
+ url = "%s/requests_common/edit_samples?cntrller=requests_admin&id=%s" % ( self.url, request_id )
url += "&comment=%s&sample_state_id=%s" % ( comment, self.security.encode_id( new_sample_state_id ) )
# select_sample_%i=true must be included twice for each sample to simulate a CheckboxField checked setting.
for sample_id in sample_ids:
url += "&select_sample_%i=true&select_sample_%i=true" % ( sample_id, sample_id )
url += "&sample_operation=Change%20state&refresh=true"
- url += "&change_state_button=Save"
+ url += "&save_changes_button=Save&editing_samples=True"
self.visit_url( url )
- self.check_page_for_string( 'Sequencing Request "%s"' % request_name )
+ self.check_page_for_string( 'Edit Current Samples of Request "%s"' % request_name )
for sample_id, sample_name in zip( sample_ids, sample_names ):
self.visit_url( "%s/requests_common/sample_events?cntrller=requests_admin&sample_id=%s" % ( self.url, self.security.encode_id( sample_id ) ) )
self.check_page_for_string( 'Events for Sample "%s"' % sample_name )
--- a/templates/requests/common/events.mako
+++ b/templates/requests/common/events.mako
@@ -1,20 +1,36 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
-<h2>History of Sequencing Request "${request.name}"</h2>
+<%
+ 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_add_samples = request.is_unsubmitted
+%>
+
+<br/><br/><ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse all requests</a>
- </li>
+ <li><a class="action-button" id="request-${request.id}-popup" class="menubutton">Request Actions</a></li>
+ <div popupmenu="request-${request.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Browse this request</a>
+ %if can_edit_request:
+ <a class="action-button" href="${h.url_for( controller='requests_common', action='edit_basic_request_info', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Edit</a>
+ %endif
+ %if can_add_samples:
+ <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit</a>
+ %endif
+ %if is_admin and request.is_submitted:
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</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>
--- a/lib/galaxy/web/controllers/requests_common.py
+++ b/lib/galaxy/web/controllers/requests_common.py
@@ -70,14 +70,14 @@ class RequestsGrid( grids.Grid ):
columns = [
NameColumn( "Name",
key="name",
- link=( lambda item: iff( item.deleted, None, dict( operation="manage_request", id=item.id ) ) ),
+ link=( lambda item: iff( item.deleted, None, dict( operation="view_request", id=item.id ) ) ),
attach_popup=True,
filterable="advanced" ),
DescriptionColumn( "Description",
key='desc',
filterable="advanced" ),
SamplesColumn( "Samples",
- link=( lambda item: iff( item.deleted, None, dict( operation="manage_request", id=item.id ) ) ) ),
+ link=( lambda item: iff( item.deleted, None, dict( operation="edit_samples", id=item.id ) ) ) ),
TypeColumn( "Sequencer",
link=( lambda item: iff( item.deleted, None, dict( operation="view_type", id=item.type.id ) ) ) ),
grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
@@ -148,7 +148,7 @@ class RequestsCommon( BaseController, Us
except TypeError, e:
# We must have an email address rather than an encoded user id
# This is because the galaxy.base.js creates a search+select box
- # when there are more than 20 items in a selectfield
+ # when there are more than 20 items in a SelectField.
user = trans.sa_session.query( trans.model.User ) \
.filter( trans.model.User.table.c.email==util.restore_text( user_id ) ) \
.first()
@@ -178,7 +178,8 @@ class RequestsCommon( BaseController, Us
message=message ,
status='done' ) )
elif params.get( 'add_sample_button', False ):
- return self.__add_sample( trans, cntrller, request, **kwd )
+ request_id = trans.security.encode_id( request.id )
+ return self.add_sample( trans, cntrller, request_id, **kwd )
request_type_select_field = self.__build_request_type_id_select_field( trans, selected_value=request_type_id )
# Widgets to be rendered on the request form
widgets = []
@@ -194,7 +195,7 @@ class RequestsCommon( BaseController, Us
widgets += request_type.request_form.get_widgets( user, **kwd )
# In case there is an error on the form, make sure to populate widget fields with anything the user
# may have already entered.
- self.populate_widgets_from_kwd( trans, widgets, **kwd )
+ widgets = self.populate_widgets_from_kwd( trans, widgets, **kwd )
if request_type is not None or status == 'error':
# Either the user selected a request_type or an error exists on the form.
if is_admin:
@@ -214,6 +215,29 @@ class RequestsCommon( BaseController, Us
message=message,
status=status )
@web.expose
+ @web.require_login( "view request" )
+ def view_request( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ request_id = params.get( 'id', None )
+ try:
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
+ except:
+ return invalid_id_redirect( trans, cntrller, request_id )
+ sample_state_id = params.get( 'sample_state_id', None )
+ # Get the user entered sample information
+ current_samples = self.__get_sample_widgets( trans, request, request.samples, **kwd )
+ request_widgets = self.__get_request_widgets( trans, request.id )
+ return trans.fill_template( '/requests/common/view_request.mako',
+ cntrller=cntrller,
+ request=request,
+ request_widgets=request_widgets,
+ current_samples=current_samples,
+ status=status,
+ message=message )
+ @web.expose
@web.require_login( "edit sequencing requests" )
def edit_basic_request_info( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
@@ -226,7 +250,7 @@ class RequestsCommon( BaseController, Us
return invalid_id_redirect( trans, cntrller, request_id )
name = util.restore_text( params.get( 'name', '' ) )
desc = util.restore_text( params.get( 'desc', '' ) )
- if params.get( 'edit_basic_request_info_button', False ) or params.get( 'edit_samples_button', False ):
+ if params.get( 'edit_basic_request_info_button', False ):
if not name:
status = 'error'
message = 'Enter the name of the request'
@@ -244,7 +268,7 @@ class RequestsCommon( BaseController, Us
widgets = widgets + request.type.request_form.get_widgets( request.user, request.values.content, **kwd )
# In case there is an error on the form, make sure to populate widget fields with anything the user
# may have already entered.
- self.populate_widgets_from_kwd( trans, widgets, **kwd )
+ widgets = self.populate_widgets_from_kwd( trans, widgets, **kwd )
return trans.fill_template( 'requests/common/edit_basic_request_info.mako',
cntrller=cntrller,
request_type=request.type,
@@ -371,8 +395,8 @@ class RequestsCommon( BaseController, Us
status=status,
message=message ) )
@web.expose
- @web.require_login( "sequencing request page" )
- def manage_request( self, trans, cntrller, **kwd ):
+ @web.require_login( "manage samples" )
+ def edit_samples( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
message = util.restore_text( params.get( 'message', '' ) )
@@ -382,80 +406,64 @@ class RequestsCommon( BaseController, Us
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
return invalid_id_redirect( trans, cntrller, request_id )
- sample_state_id = params.get( 'sample_state_id', None )
+ # This method is called when the user is adding new samples as well as
+ # editing existing samples, so we use the editing_samples flag to keep
+ # track of what's occurring.
+ # TODO: CRITICAL: We need another round of code fixes to abstract out
+ # adding samples vs editing samples. We need to eliminate the need for
+ # this editing_samples flag since it is not maintainable. Greg will do
+ # this work as soon as possible.
+ editing_samples = util.string_as_bool( params.get( 'editing_samples', False ) )
+ if params.get( 'cancel_changes_button', False ):
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ id=request_id,
+ editing_samples=editing_samples ) )
+ # Get all libraries for which the current user has permission to add items.
+ libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
# Get the user entered sample information
- current_samples, managing_samples, libraries = self.__get_sample_info( trans, request, **kwd )
- selected_samples = self.__get_selected_samples( trans, request, **kwd )
- selected_value = params.get( 'sample_operation', 'none' )
- if selected_value != 'none' and not selected_samples:
+ current_samples = self.__get_sample_widgets( trans, request, request.samples, **kwd )
+ encoded_selected_sample_ids = self.__get_encoded_selected_sample_ids( trans, request, **kwd )
+ sample_operation = params.get( 'sample_operation', 'none' )
+ def handle_error( **kwd ):
+ kwd[ 'status' ] = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ **kwd ) )
+ if not encoded_selected_sample_ids and sample_operation != 'none':
+ # Probably occurred due to refresh_on_change...is there a better approach?
+ kwd[ 'sample_operation' ] = 'none'
message = 'Select at least one sample before selecting an operation.'
- status = 'error'
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=request_id,
- status=status,
- message=message ) )
- sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, selected_value )
- sample_operation_selected_value = sample_operation_select_field.get_selected( return_value=True )
+ kwd[ 'message' ] = message
+ handle_error( **kwd )
if params.get( 'import_samples_button', False ):
# Import sample field values from a csv file
return self.__import_samples( trans, cntrller, request, current_samples, libraries, **kwd )
elif params.get( 'add_sample_button', False ):
- return self.__add_sample( trans, cntrller, request, **kwd )
+ return self.add_sample( trans, cntrller, request_id, **kwd )
elif params.get( 'save_samples_button', False ):
- return self.__save_sample( trans, cntrller, request, current_samples, **kwd )
- elif params.get( 'edit_samples_button', False ):
- managing_samples = True
- elif params.get( 'cancel_changes_button', False ):
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=request_id ) )
- pass
- elif params.get( 'change_state_button', False ):
- sample_event_comment = util.restore_text( params.get( 'sample_event_comment', '' ) )
- new_state = trans.sa_session.query( trans.model.SampleState ).get( trans.security.decode_id( sample_state_id ) )
- self.update_sample_state(trans, cntrller, selected_samples, new_state, comment=sample_event_comment )
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- cntrller=cntrller,
- action='update_request_state',
- request_id=request_id ) )
- elif params.get( 'cancel_change_state_button', False ):
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=request_id ) )
- elif params.get( 'change_lib_button', False ):
- library_id = params.get( 'sample_0_library_id', None )
- try:
- library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( library_id ) )
- except:
- invalid_id_redirect( trans, cntrller, library_id )
- folder_id = params.get( 'sample_0_folder_id', None )
- try:
- folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) )
- except:
- invalid_id_redirect( trans, cntrller, folder_id )
- for sample_id in selected_samples:
- sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
- sample.library = library
- sample.folder = folder
- trans.sa_session.add( sample )
- trans.sa_session.flush()
- trans.sa_session.refresh( request )
- message = 'Changes made to the selected samples have been saved. '
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=request_id,
- status=status,
- message=message ) )
- elif params.get( 'cancel_change_lib_button', False ):
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=trans.security.encode_id( request.id ) ) )
+ if encoded_selected_sample_ids:
+ # This gets tricky because we need the list of samples to include the same number
+ # of objects that that current_samples ( i.e., request.samples ) has. We'll first
+ # get the set of samples corresponding to the checked sample ids.
+ samples = []
+ selected_samples = []
+ for encoded_sample_id in encoded_selected_sample_ids:
+ sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( encoded_sample_id ) )
+ selected_samples.append( sample )
+ # Now build the list of samples, inserting None for samples that have not been checked.
+ for sample in request.samples:
+ if sample in selected_samples:
+ samples.append( sample )
+ else:
+ samples.append( None )
+ # The __save_samples method requires samples widgets, not sample objects
+ samples = self.__get_sample_widgets( trans, request, samples, **kwd )
+ else:
+ samples = current_samples
+ return self.__save_samples( trans, cntrller, request, samples, **kwd )
request_widgets = self.__get_request_widgets( trans, request.id )
sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
libraries_select_field, folders_select_field = self.__build_library_and_folder_select_fields( trans,
@@ -464,12 +472,13 @@ class RequestsCommon( BaseController, Us
libraries,
None,
**kwd )
- # Build the sample_state_id_select_field SelectField
+ sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, sample_operation )
+ sample_state_id = params.get( 'sample_state_id', None )
sample_state_id_select_field = self.__build_sample_state_id_select_field( trans, request, sample_state_id )
- return trans.fill_template( '/requests/common/manage_request.mako',
+ return trans.fill_template( '/requests/common/edit_samples.mako',
cntrller=cntrller,
request=request,
- selected_samples=selected_samples,
+ encoded_selected_sample_ids=encoded_selected_sample_ids,
request_widgets=request_widgets,
current_samples=current_samples,
sample_copy=sample_copy,
@@ -478,7 +487,7 @@ class RequestsCommon( BaseController, Us
libraries_select_field=libraries_select_field,
folders_select_field=folders_select_field,
sample_state_id_select_field=sample_state_id_select_field,
- managing_samples=managing_samples,
+ editing_samples=editing_samples,
status=status,
message=message )
@web.expose
@@ -489,7 +498,7 @@ class RequestsCommon( BaseController, Us
except:
if cntrller == 'api':
trans.response.status = 400
- return "Malformed sample id ( %s ) specified, unable to decode." % str( sample_id )
+ return "Invalid sample id ( %s ) specified, unable to decode." % str( sample_id )
else:
return invalid_id_redirect( trans, cntrller, sample_id )
event = trans.model.SampleEvent( sample, new_state, comment )
@@ -516,7 +525,7 @@ class RequestsCommon( BaseController, Us
if ok_for_now:
request.deleted = True
trans.sa_session.add( request )
- # delete all the samples belonging to this request
+ # Delete all the samples belonging to this request
for s in request.samples:
s.deleted = True
trans.sa_session.add( s )
@@ -546,7 +555,7 @@ class RequestsCommon( BaseController, Us
if ok_for_now:
request.deleted = False
trans.sa_session.add( request )
- # undelete all the samples belonging to this request
+ # Undelete all the samples belonging to this request
for s in request.samples:
s.deleted = False
trans.sa_session.add( s )
@@ -655,9 +664,10 @@ class RequestsCommon( BaseController, Us
if cntrller == 'api':
return 200, message
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='edit_samples',
cntrller=cntrller,
id=request_id,
+ editing_samples=True,
status=status,
message=message ) )
final_state = False
@@ -673,8 +683,8 @@ class RequestsCommon( BaseController, Us
event = trans.model.RequestEvent( request, state, comments )
trans.sa_session.add( event )
trans.sa_session.flush()
- # check if an email notification is configured to be sent when the samples
- # are in this state
+ # See if an email notification is configured to be sent when the samples
+ # are in this state.
retval = request.send_email_notification( trans, common_state, final_state )
if retval:
message = comments + retval
@@ -683,113 +693,10 @@ class RequestsCommon( BaseController, Us
if cntrller == 'api':
return 200, message
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='edit_samples',
cntrller=cntrller,
- id=trans.security.encode_id(request.id),
- status='done',
- message=message ) )
- def __save_sample( self, trans, cntrller, request, current_samples, **kwd ):
- # Save all the new/unsaved samples entered by the user
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
- is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
- selected_value = params.get( 'sample_operation', 'none' )
- # Check for duplicate sample names
- message = ''
- for index in range( len( current_samples ) - len( request.samples ) ):
- sample_index = index + len( request.samples )
- current_sample = current_samples[ sample_index ]
- sample_name = current_sample[ 'name' ]
- if not sample_name.strip():
- message = 'Enter the name of sample number %i' % sample_index
- break
- count = 0
- for i in range( len( current_samples ) ):
- if sample_name == current_samples[ i ][ 'name' ]:
- count += 1
- if count > 1:
- message = "This request has %i samples with the name (%s). Samples belonging to a request must have unique names." % ( count, sample_name )
- break
- if message:
- selected_samples = self.__get_selected_samples( trans, request, **kwd )
- request_widgets = self.__get_request_widgets( trans, request.id )
- sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
- sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, selected_value )
- status = 'error'
- return trans.fill_template( '/requests/common/manage_request.mako',
- cntrller=cntrller,
- request=request,
- selected_samples=selected_samples,
- request_widgets=request_widgets,
- current_samples=current_samples,
- sample_copy=sample_copy,
- managing_samples=managing_samples,
- sample_operation_select_field=sample_operation_select_field,
- status=status,
- message=message )
- if not managing_samples:
- for index in range( len( current_samples ) - len( request.samples ) ):
- sample_index = len( request.samples )
- current_sample = current_samples[ sample_index ]
- form_values = trans.model.FormValues( request.type.sample_form, current_sample[ 'field_values' ] )
- trans.sa_session.add( form_values )
- trans.sa_session.flush()
- s = trans.model.Sample( current_sample[ 'name' ],
- '',
- request,
- form_values,
- current_sample[ 'barcode' ],
- current_sample[ 'library' ],
- current_sample[ 'folder' ] )
- trans.sa_session.add( s )
- trans.sa_session.flush()
- else:
- message = 'Changes made to the samples are saved. '
- for sample_index in range( len( current_samples ) ):
- sample = request.samples[ sample_index ]
- current_sample = current_samples[ sample_index ]
- sample.name = current_sample[ 'name' ]
- sample.library = current_sample[ 'library' ]
- sample.folder = current_sample[ 'folder' ]
- if request.is_submitted:
- bc_message = self.__validate_barcode( trans, sample, current_sample[ 'barcode' ] )
- if bc_message:
- status = 'error'
- message += bc_message
- else:
- if not sample.bar_code:
- # If this is a 'new' (still in its first state) sample
- # change the state to the next
- if sample.state.id == request.type.states[0].id:
- event = trans.model.SampleEvent( sample,
- request.type.states[1],
- 'Sample added to the system' )
- trans.sa_session.add( event )
- trans.sa_session.flush()
- # Now check if all the samples' barcode has been entered.
- # If yes then send notification email if configured
- common_state = request.samples_have_common_state
- if common_state:
- if common_state.id == request.type.states[1].id:
- event = trans.model.RequestEvent( request,
- request.states.SUBMITTED,
- "All samples are in %s state." % common_state.name )
- trans.sa_session.add( event )
- trans.sa_session.flush()
- request.send_email_notification( trans, request.type.states[1] )
- sample.bar_code = current_samples[sample_index]['barcode']
- trans.sa_session.add( sample )
- trans.sa_session.flush()
- form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
- form_values.content = current_sample[ 'field_values' ]
- trans.sa_session.add( form_values )
- trans.sa_session.flush()
- return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
- cntrller=cntrller,
- id=trans.security.encode_id( request.id ),
+ id=request_id,
+ editing_samples=True,
status=status,
message=message ) )
@web.expose
@@ -876,25 +783,33 @@ class RequestsCommon( BaseController, Us
cntrller=cntrller,
events_list=events_list,
sample=sample )
- def __add_sample( self, trans, cntrller, request, **kwd ):
+ @web.expose
+ @web.require_login( "add sample" )
+ def add_sample( self, trans, cntrller, request_id, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
+ try:
+ request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
+ except:
+ return invalid_id_redirect( trans, cntrller, request_id )
is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
# Get the widgets for rendering the request form
request_widgets = self.__get_request_widgets( trans, request.id )
- current_samples, managing_samples, libraries = self.__get_sample_info( trans, request, **kwd )
+ current_samples = self.__get_sample_widgets( trans, request, request.samples, **kwd )
if not current_samples:
# Form field names are zero-based.
sample_index = 0
else:
sample_index = len( current_samples )
if params.get( 'add_sample_button', False ):
+ # Get all libraries for which the current user has permission to add items
+ libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
num_samples_to_add = int( params.get( 'num_sample_to_copy', 1 ) )
# See if the user has selected a sample to copy.
copy_sample_index = int( params.get( 'copy_sample_index', -1 ) )
for index in range( num_samples_to_add ):
+ id_index = len( current_samples ) + 1
if copy_sample_index != -1:
# The user has selected a sample to copy.
library_id = current_samples[ copy_sample_index][ 'library_select_field' ].get_selected( return_value=True )
@@ -902,7 +817,7 @@ class RequestsCommon( BaseController, Us
name = current_samples[ copy_sample_index ][ 'name' ] + '_%i' % ( len( current_samples ) + 1 )
field_values = [ val for val in current_samples[ copy_sample_index ][ 'field_values' ] ]
else:
- # The user has not selected a sample to copy (may just be adding a sample).
+ # The user has not selected a sample to copy, just adding a new generic sample.
library_id = None
folder_id = None
name = 'Sample_%i' % ( len( current_samples ) + 1 )
@@ -910,7 +825,7 @@ class RequestsCommon( BaseController, Us
# Build the library_select_field and folder_select_field for the new sample being added.
library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
user=request.user,
- sample_index=len( current_samples ),
+ sample_index=id_index,
libraries=libraries,
sample=None,
library_id=library_id,
@@ -926,101 +841,21 @@ class RequestsCommon( BaseController, Us
field_values=field_values,
library_select_field=library_select_field,
folder_select_field=folder_select_field ) )
- selected_samples = self.__get_selected_samples( trans, request, **kwd )
- selected_value = params.get( 'sample_operation', 'none' )
- sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, selected_value )
+ encoded_selected_sample_ids = self.__get_encoded_selected_sample_ids( trans, request, **kwd )
+ sample_operation = params.get( 'sample_operation', 'none' )
+ sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, sample_operation )
sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
- return trans.fill_template( '/requests/common/manage_request.mako',
+ return trans.fill_template( '/requests/common/edit_samples.mako',
cntrller=cntrller,
request=request,
- selected_samples=selected_samples,
+ encoded_selected_sample_ids=encoded_selected_sample_ids,
request_widgets=request_widgets,
current_samples=current_samples,
sample_operation_select_field=sample_operation_select_field,
sample_copy=sample_copy,
- managing_samples=managing_samples,
+ editing_samples=False,
message=message,
status=status )
- def __get_sample_info( self, trans, request, **kwd ):
- """
- Retrieves all user entered sample information and returns a
- list of all the samples and their field values.
- """
- params = util.Params( kwd )
- managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
- # Bet all data libraries accessible to this user
- libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
- # Build the list of widgets which will be used to render each sample row on the request page
- current_samples = []
- for index, sample in enumerate( request.samples ):
- library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
- request.user,
- index,
- libraries,
- sample,
- **kwd )
- current_samples.append( dict( name=sample.name,
- barcode=sample.bar_code,
- library=sample.library,
- folder=sample.folder,
- field_values=sample.values.content,
- library_select_field=library_select_field,
- folder_select_field=folder_select_field ) )
- if not managing_samples:
- sample_index = len( request.samples )
- else:
- sample_index = 0
- while True:
- library_id = params.get( 'sample_%i_library_id' % sample_index, None )
- folder_id = params.get( 'sample_%i_folder_id' % sample_index, None )
- if params.get( 'sample_%i_name' % sample_index, False ):
- # Data library
- try:
- library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( library_id ) )
- #library_id = library.id
- except:
- library = None
- if library is not None:
- # Folder
- try:
- folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) )
- #folder_id = folder.id
- except:
- if library:
- folder = library.root_folder
- else:
- folder = None
- else:
- folder = None
- sample_info = dict( name=util.restore_text( params.get( 'sample_%i_name' % sample_index, '' ) ),
- barcode=util.restore_text( params.get( 'sample_%i_barcode' % sample_index, '' ) ),
- library=library,
- folder=folder)
- sample_info[ 'field_values' ] = []
- for field_index in range( len( request.type.sample_form.fields ) ):
- sample_info[ 'field_values' ].append( util.restore_text( params.get( 'sample_%i_field_%i' % ( sample_index, field_index ), '' ) ) )
- if not managing_samples:
- sample_info[ 'library_select_field' ], sample_info[ 'folder_select_field' ] = self.__build_library_and_folder_select_fields( trans,
- request.user,
- sample_index,
- libraries,
- None,
- library_id,
- folder_id,
- **kwd )
- current_samples.append( sample_info )
- else:
- sample_info[ 'library_select_field' ], sample_info[ 'folder_select_field' ] = self.__build_library_and_folder_select_fields( trans,
- request.user,
- sample_index,
- libraries,
- request.samples[ sample_index ],
- **kwd )
- current_samples[ sample_index ] = sample_info
- sample_index += 1
- else:
- break
- return current_samples, managing_samples, libraries
@web.expose
@web.require_login( "delete sample from sequencing request" )
def delete_sample( self, trans, cntrller, **kwd ):
@@ -1032,7 +867,7 @@ class RequestsCommon( BaseController, Us
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
return invalid_id_redirect( trans, cntrller, request_id )
- current_samples, managing_samples, libraries = self.__get_sample_info( trans, request, **kwd )
+ current_samples = self.__get_sample_widgets( trans, request, request.samples, **kwd )
sample_index = int( params.get( 'sample_id', 0 ) )
sample_name = current_samples[sample_index]['name']
sample = request.has_sample( sample_name )
@@ -1042,9 +877,10 @@ class RequestsCommon( BaseController, Us
trans.sa_session.flush()
message = 'Sample (%s) has been deleted.' % sample_name
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='edit_samples',
cntrller=cntrller,
id=trans.security.encode_id( request.id ),
+ editing_samples=True,
status=status,
message=message ) )
@web.expose
@@ -1059,12 +895,12 @@ class RequestsCommon( BaseController, Us
sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
except:
return invalid_id_redirect( trans, cntrller, sample_id )
- # check if a library and folder has been set for this sample yet.
+ # See if a library and folder have been set for this sample.
if not sample.library or not sample.folder:
status = 'error'
message = "Set a data library and folder for sequencing request (%s) to transfer datasets." % sample.name
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='edit_samples',
cntrller=cntrller,
id=trans.security.encode_id( sample.request.id ),
status=status,
@@ -1101,7 +937,6 @@ class RequestsCommon( BaseController, Us
SampleName,DataLibrary,DataLibraryFolder,Field1,Field2....
"""
params = util.Params( kwd )
- managing_samples = util.string_as_bool( params.get( 'managing_samples', False ) )
file_obj = params.get( 'file_data', '' )
try:
reader = csv.reader( file_obj.file )
@@ -1137,23 +972,211 @@ class RequestsCommon( BaseController, Us
folder_select_field=folder_select_field,
field_values=row[3:] ) )
except Exception, e:
- status = 'error'
- message = 'Error thrown when importing samples file: %s' % str( e )
+ if str( e ) == "'unicode' object has no attribute 'file'":
+ message = "Select a file"
+ else:
+ message = 'Error attempting to create samples from selected file: %s.' % str( e )
+ message += ' Make sure the selected csv file uses the format: SampleName,DataLibrary,DataLibraryFolder,FieldValue1,FieldValue2...'
return trans.response.send_redirect( web.url_for( controller='requests_common',
- action='manage_request',
+ action='add_sample',
cntrller=cntrller,
- id=trans.security.encode_id( request.id ),
- status=status,
+ request_id=trans.security.encode_id( request.id ),
+ add_sample_button='Add sample',
+ status='error',
message=message ) )
request_widgets = self.__get_request_widgets( trans, request.id )
sample_copy = self.__build_copy_sample_select_field( trans, current_samples )
- return trans.fill_template( '/requests/common/manage_request.mako',
+ return trans.fill_template( '/requests/common/edit_samples.mako',
cntrller=cntrller,
request=request,
request_widgets=request_widgets,
current_samples=current_samples,
sample_copy=sample_copy,
- managing_samples=managing_samples )
+ 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.
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ editing_samples = util.string_as_bool( params.get( 'editing_samples', False ) )
+ is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
+ sample_operation = params.get( 'sample_operation', 'none' )
+ # Check for duplicate sample names within the request
+ self.__validate_sample_names( trans, cntrller, request, samples, **kwd )
+ if editing_samples:
+ library = None
+ folder = None
+ def handle_error( **kwd ):
+ kwd[ 'status' ] = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ **kwd ) )
+ # Here we handle saving changes to single samples as well as saving changes to
+ # selected sets of samples. If samples are selected, the sample_operation param
+ # will have a value other than 'none', and the samples param will be a list of
+ # encoded sample ids. There are currently only 2 multi-select operations;
+ # 'Change state' and 'Select data library and folder'. If sample_operation is
+ # 'none, then the samples param will be a list of sample objects.
+ if sample_operation == 'Change state':
+ sample_state_id = params.get( 'sample_state_id', None )
+ if sample_state_id in [ None, 'none' ]:
+ message = "Select a new state from the <b>Change current state</b> list before clicking the <b>Save</b> button."
+ kwd[ 'message' ] = message
+ del kwd[ 'save_changes_button' ]
+ handle_error( **kwd )
+ sample_event_comment = util.restore_text( params.get( 'sample_event_comment', '' ) )
+ new_state = trans.sa_session.query( trans.model.SampleState ).get( trans.security.decode_id( sample_state_id ) )
+ # 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
+ for sample_index in range( len( samples ) ):
+ current_sample = samples[ sample_index ]
+ if current_sample is None:
+ # We have a None value because the user did not select this sample
+ # 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 )
+ 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,
+ action='update_request_state',
+ request_id=trans.security.encode_id( request.id ) ) )
+ elif sample_operation == 'Select data library and folder':
+ 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 )
+ # See if all the samples' barcodes 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:
+ event = trans.model.RequestEvent( request,
+ request.states.SUBMITTED,
+ "All samples are in %s state." % common_state.name )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ request.send_email_notification( trans, request.type.states[1] )
+ message = 'Changes made to the samples have been saved. '
+ else:
+ # Saving a newly created sample.
+ for index in range( len( samples ) - len( request.samples ) ):
+ sample_index = len( request.samples )
+ current_sample = samples[ sample_index ]
+ form_values = trans.model.FormValues( request.type.sample_form, current_sample[ 'field_values' ] )
+ trans.sa_session.add( form_values )
+ trans.sa_session.flush()
+ s = trans.model.Sample( current_sample[ 'name' ],
+ '',
+ request,
+ form_values,
+ current_sample[ 'barcode' ],
+ current_sample[ 'library' ],
+ current_sample[ 'folder' ] )
+ trans.sa_session.add( s )
+ trans.sa_session.flush()
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ id=trans.security.encode_id( request.id ),
+ editing_samples=editing_samples,
+ status=status,
+ message=message ) )
+ def __update_samples( self, trans, 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.
+ def handle_error( **kwd ):
+ kwd[ 'status' ] = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ 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, obj in enumerate( sample_widgets ):
+ if obj is not None:
+ # obj will be None if the user checked sample check boxes and selected an action
+ # to perform on multiple samples, but did not select certain samples.
+ sample_updated = False
+ # If this sample has values in kwd, then kwd will include a
+ # key whose value is this sample's ( possibly changed ) name. An
+ # example of this key is 'sample_0_name'.
+ for k, v in kwd.items():
+ name_key = 'sample_%i_name' % index
+ if k == name_key:
+ sample_updated = True
+ break
+ if sample_updated:
+ id_index = index + 1
+ if sample_operation == 'none':
+ # We are handling changes to a single sample.
+ library_id = params.get( 'sample_%i_library_id' % id_index, 'none' )
+ folder_id = params.get( 'sample_%i_folder_id' % id_index, 'none' )
+ # Update the corresponding sample's values as well as the sample_widget.
+ sample = request.samples[ index ]
+ sample.name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
+ # The bar_code field requires special handling because after a request is submitted, the
+ # state of a sample cannot be changed without a bar_code assocaited with the sample.
+ bar_code = util.restore_text( params.get( 'sample_%i_barcode' % index, '' ) )
+ if not bar_code and not sample.bar_code:
+ # If this is a 'new' (still in its first state) sample, create an event
+ if sample.state.id == request.states[0].id:
+ event = trans.model.SampleEvent( sample,
+ request.type.states[1],
+ 'Sample added to the system' )
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ elif bar_code:
+ bc_message = self.__validate_barcode( trans, sample, bar_code )
+ if bc_message:
+ kwd[ 'message' ] = bc_message
+ del kwd[ 'save_samples_button' ]
+ handle_error( **kwd )
+ sample.bar_code = bar_code
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ sample.library = library
+ sample.folder = folder
+ field_values = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ form_values = trans.sa_session.query( trans.model.FormValues ).get( sample.values.id )
+ form_values.content = field_values
+ trans.sa_session.add_all( ( sample, form_values ) )
+ trans.sa_session.flush()
+ def __get_library_and_folder( self, trans, library_id, folder_id ):
+ try:
+ library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( library_id ) )
+ except:
+ library = None
+ if library and folder_id == 'none':
+ folder = library.root_folder
+ elif library and folder_id != 'none':
+ try:
+ folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( folder_id ) )
+ except:
+ if library:
+ folder = library.root_folder
+ else:
+ folder = None
+ else:
+ folder = None
+ return library, folder
# ===== Methods for handling form definition widgets =====
def __get_request_widgets( self, trans, id ):
"""Get the widgets for the request"""
@@ -1179,27 +1202,104 @@ class RequestsCommon( BaseController, Us
value=request.values.content[ index ],
helptext=field[ 'helptext' ] + ' (' + required_label + ')' ) )
return request_widgets
- def __get_samples_widgets( self, trans, request, libraries, **kwd ):
- """Get the widgets for all of the samples currently associated with the request"""
- # The current_samples_widgets list is a list of dictionaries
- current_samples_widgets = []
- for index, sample in enumerate( request.samples ):
- # Build the library_select_field and folder_select_field for each existing sample
- library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
+ def __get_sample_widgets( self, trans, request, samples, **kwd ):
+ """
+ Returns a list of dictionaries, each representing the widgets that define a sample on a form.
+ The widgets are populated from kwd based on the set of samples received. The set of samples
+ corresponds to a reques.samples list, but if the user checked specific check boxes on the form,
+ those samples that were not check will have None objects in the list of samples. In this case,
+ the corresponding sample_widget is populated from the db rather than kwd.
+ """
+ params = util.Params( kwd )
+ sample_operation = params.get( 'sample_operation', 'none' )
+ # This method is called when the user is adding new samples as well as
+ # editing existing samples, so we use the editing_samples flag to keep
+ # track of what's occurring.
+ editing_samples = util.string_as_bool( params.get( 'editing_samples', False ) )
+ sample_widgets = []
+ if sample_operation != 'none':
+ # The sample_operatin param has a value other than 'none', and a specified
+ # set of samples was received.
+ library_id = util.restore_text( params.get( 'sample_0_library_id', 'none' ) )
+ folder_id = util.restore_text( params.get( 'sample_0_folder_id', 'none' ) )
+ # Build the list of widgets which will be used to render each sample row on the request page
+ if not request:
+ return sample_widgets
+ # Get the list of libraries for which the current user has permission to add items.
+ libraries = request.user.accessible_libraries( trans, [ trans.app.security_agent.permitted_actions.LIBRARY_ADD ] )
+ # Build the list if sample widgets, populating the values from kwd.
+ for index, sample in enumerate( samples ):
+ id_index = index + 1
+ if sample is None:
+ # Id sample is None, then we'll use the sample from the request object since it will
+ # not have updated =values from kwd.
+ sample = request.samples[ index ]
+ name = sample.name
+ bar_code = sample.bar_code
+ library = sample.library
+ folder = sample.folder
+ field_values = sample.values.content,
+ else:
+ # Update the sample attributes from kwd
+ 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 ) )
+ 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 )
+ folder_id = util.restore_text( params.get( 'sample_%i_folder_id' % id_index, '' ) )
+ if not folder_id and sample.folder:
+ folder_id = trans.security.encode_id( sample.folder.id )
+ library, folder = self.__get_library_and_folder( trans, library_id, folder_id )
+ field_values = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans=trans,
user=request.user,
- sample_index=index,
+ sample_index=id_index,
libraries=libraries,
sample=sample,
+ library_id=library_id,
+ folder_id=folder_id,
**kwd )
- # Append the dictionary for the current sample to the current_samples_widgets list
- current_samples_widgets.append( dict( name=sample.name,
- barcode=sample.bar_code,
- library=sample.library,
- folder=sample.folder,
- field_values=sample.values.content,
- library_select_field=library_select_field,
- folder_select_field=folder_select_field ) )
- return current_samples_widgets
+ sample_widgets.append( dict( name=name,
+ barcode=bar_code,
+ library=library,
+ folder=folder,
+ field_values=field_values,
+ library_select_field=library_select_field,
+ folder_select_field=folder_select_field ) )
+ # There may be additional new samples on the form that have not yet been associated with the request.
+ # TODO: factor this code so it is not duplicating what's above.
+ index = len( samples )
+ while True:
+ name = util.restore_text( params.get( 'sample_%i_name' % index, '' ) )
+ if not name:
+ break
+ id_index = index + 1
+ bar_code = util.restore_text( params.get( 'sample_%i_barcode' % 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 )
+ field_values = []
+ for field_index in range( len( request.type.sample_form.fields ) ):
+ field_values.append( util.restore_text( params.get( 'sample_%i_field_%i' % ( index, field_index ), '' ) ) )
+ library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans=trans,
+ user=request.user,
+ sample_index=id_index,
+ libraries=libraries,
+ sample=None,
+ library_id=library_id,
+ folder_id=folder_id,
+ **kwd )
+ sample_widgets.append( dict( name=name,
+ barcode=bar_code,
+ library=library,
+ folder=folder,
+ field_values=field_values,
+ library_select_field=library_select_field,
+ folder_select_field=folder_select_field ) )
+ index += 1
+ return sample_widgets
# ===== Methods for building SelectFields used on various request forms =====
def __build_copy_sample_select_field( self, trans, current_samples ):
copy_sample_index_select_field = SelectField( 'copy_sample_index' )
@@ -1240,26 +1340,30 @@ class RequestsCommon( BaseController, Us
# The libraries dictionary looks like: { library : '1,2' }, library : '3' }. Its keys are the libraries that
# should be displayed for the current user and its values are strings of comma-separated folder ids that should
# NOT be displayed.
- #
- # TODO: all object ids received in the params must be encoded.
params = util.Params( kwd )
library_select_field_name= "sample_%i_library_id" % sample_index
folder_select_field_name = "sample_%i_folder_id" % sample_index
if not library_id:
- library_id = params.get( library_select_field_name, 'none' )
+ library_id = params.get( library_select_field_name, None )
+ if not folder_id:
+ folder_id = params.get( folder_select_field_name, None )
selected_library = None
selected_hidden_folder_ids = []
showable_folders = []
- if sample and sample.library and library_id == 'none':
- library_id = str( sample.library.id )
+ 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():
+ 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'
+ selected_library = None
+ elif sample and sample.library:
+ library_id = trans.security.encode_id( sample.library.id )
selected_library = sample.library
- # If we have a selected library, get the list of it's folders that are not accessible to the current user
- for library, hidden_folder_ids in libraries.items():
- encoded_id = trans.security.encode_id( library.id )
- if encoded_id == str( library_id ):
- selected_library = library
- selected_hidden_folder_ids = hidden_folder_ids.split( ',' )
- break
# sample_%i_library_id SelectField with refresh on change enabled
library_select_field = build_select_field( trans,
libraries.keys(),
@@ -1275,20 +1379,12 @@ class RequestsCommon( BaseController, Us
selected_library,
[ trans.app.security_agent.permitted_actions.LIBRARY_ADD ],
selected_hidden_folder_ids )
- if sample:
- # The user is editing the request, and may have previously selected a folder
- if sample.folder:
- selected_folder_id = sample.folder.id
+ if folder_id:
+ selected_folder_id = folder_id
+ elif sample and sample.folder:
+ selected_folder_id = trans.security.encode_id( sample.folder.id )
else:
- # If a library is selected but not a folder, use the library's root folder
- if sample.library:
- selected_folder_id = sample.library.root_folder.id
- else:
- # The user just selected a folder
- selected_folder_id = params.get( folder_select_field_name, 'none' )
- elif folder_id:
- # TODO: not sure when this would be passed
- selected_folder_id = folder_id
+ selected_folder_id = trans.security.encode_id( selected_library.root_folder.id )
else:
selected_folder_id = 'none'
# TODO: Change the name of the library root folder to "Library root" to clarify to the
@@ -1328,9 +1424,35 @@ class RequestsCommon( BaseController, Us
message += '<b>' + ef + '</b> '
return message
return None
+ def __validate_sample_names( self, trans, cntrller, request, current_samples, **kwd ):
+ # Check for duplicate sample names for all samples of the request.
+ editing_samples = util.string_as_bool( kwd.get( 'editing_samples', False ) )
+ message = ''
+ for index in range( len( current_samples ) - len( request.samples ) ):
+ sample_index = index + len( request.samples )
+ current_sample = current_samples[ sample_index ]
+ sample_name = current_sample[ 'name' ]
+ if not sample_name.strip():
+ message = 'Enter the name of sample number %i' % sample_index
+ break
+ count = 0
+ for i in range( len( current_samples ) ):
+ if sample_name == current_samples[ i ][ 'name' ]:
+ count += 1
+ if count > 1:
+ message = "You tried to add %i samples with the name (%s). Samples belonging to a request must have unique names." % ( count, sample_name )
+ break
+ if message:
+ del kwd[ 'save_samples_button' ]
+ kwd[ 'message' ] = message
+ kwd[ 'status' ] = 'error'
+ return trans.response.send_redirect( web.url_for( controller='requests_common',
+ action='edit_samples',
+ cntrller=cntrller,
+ **kwd ) )
def __validate_barcode( self, trans, sample, barcode ):
"""
- Makes sure that the barcode about to be assigned to a sample is gobally unique.
+ 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.
"""
message = ''
@@ -1338,13 +1460,18 @@ class RequestsCommon( BaseController, Us
for index in range( len( sample.request.samples ) ):
# Check for empty bar code
if not barcode.strip():
- message = 'Fill in the barcode for sample (%s).' % sample.name
- break
+ 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_has_bar_code in trans.sa_session.query( trans.model.Sample ) \
- .filter( trans.model.Sample.table.c.bar_code == barcode ):
- if sample_has_bar_code and sample_has_bar_code.id != sample.id:
+ 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:
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 )
@@ -1360,15 +1487,15 @@ class RequestsCommon( BaseController, Us
elif len( email ) > 255:
error = "(%s) exceeds maximum allowable length. " % str( email )
return error
- # ===== Other miscellaneoud utility methods =====
- def __get_selected_samples( self, trans, request, **kwd ):
- selected_samples = []
+ # ===== Other miscellaneous utility methods =====
+ def __get_encoded_selected_sample_ids( self, trans, request, **kwd ):
+ encoded_selected_sample_ids = []
for sample in request.samples:
if CheckboxField.is_checked( kwd.get( 'select_sample_%i' % sample.id, '' ) ):
- selected_samples.append( trans.security.encode_id( sample.id ) )
- return selected_samples
+ encoded_selected_sample_ids.append( trans.security.encode_id( sample.id ) )
+ return encoded_selected_sample_ids
-# ===== Miscellaneoud utility methods outside of the RequestsCommon class =====
+# ===== Miscellaneous utility methods outside of the RequestsCommon class =====
def invalid_id_redirect( trans, cntrller, obj_id, action='browse_requests' ):
status = 'error'
message = "Invalid request id (%s)" % str( obj_id )
--- a/templates/requests/common/dataset_transfer.mako
+++ b/templates/requests/common/dataset_transfer.mako
@@ -1,47 +1,46 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
+<br/><br/>
+
+<ul class="manage-table-actions">
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">Refresh</a></li>
+ <li><a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( sample.library.id ) )}">Target Data Library</a></li>
+ <li><a class="action-button" href="${h.url_for( controller='requests_common', action='view_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a></li>
+</ul>
+
%if message:
${render_msg( message, status )}
%endif
-<h2>Datasets of Sample "${sample.name}"</h2>
-
-<ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">Refresh</a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( sample.library.id ) )}">${sample.library.name} Data Library</a>
- </li>
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( sample.request.id ) )}">Browse this request</a>
- </li>
-</ul>
-
-%if dataset_files:
- <div class="form-row">
- <table class="grid">
- <thead>
- <tr>
- <th>Name</th>
- <th>Size</th>
- <th>Status</th>
- </tr>
- <thead>
- <tbody>
- %for dataset_file in dataset_files:
- <tr>
- <td>${dataset_file.name}</td>
- <td>${dataset_file.size}</td>
- <td>${dataset_file.status}</td>
- </tr>
- %endfor
- </tbody>
- </table>
+<div class="toolForm">
+ <div class="toolFormTitle">Sample "${sample.name}"</div>
+ <div class="toolFormBody">
+ %if dataset_files:
+ <div class="form-row">
+ <table class="grid">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Size</th>
+ <th>Status</th>
+ </tr>
+ <thead>
+ <tbody>
+ %for dataset_file in dataset_files:
+ <tr>
+ <td>${dataset_file.name}</td>
+ <td>${dataset_file.size}</td>
+ <td>${dataset_file.status}</td>
+ </tr>
+ %endfor
+ </tbody>
+ </table>
+ </div>
+ %else:
+ <div class="form-row">
+ There are no datasets associated with this sample.
+ </div>
+ %endif
</div>
-%else:
- <div class="form-row">
- There are no datasets associated with this sample.
- </div>
-%endif
+</div>
--- a/templates/requests/common/manage_request.mako
+++ /dev/null
@@ -1,615 +0,0 @@
-<%inherit file="/base.mako"/>
-<%namespace file="/message.mako" import="render_msg" />
-<%namespace file="/requests/common/sample_state.mako" import="render_sample_state" />
-<%namespace file="/requests/common/sample_datasets.mako" import="render_sample_datasets" />
-
-<%def name="stylesheets()">
- ${parent.stylesheets()}
- ${h.css( "library" )}
-</%def>
-
-<%def name="javascripts()">
- ${parent.javascripts()}
- <script type="text/javascript">
- function showContent(vThis)
- {
- // http://www.javascriptjunkie.com
- // alert(vSibling.className + " " + vDef_Key);
- vParent = vThis.parentNode;
- vSibling = vParent.nextSibling;
- while (vSibling.nodeType==3) {
- // Fix for Mozilla/FireFox Empty Space becomes a TextNode or Something
- vSibling = vSibling.nextSibling;
- };
- if(vSibling.style.display == "none")
- {
- vThis.src="/static/images/fugue/toggle.png";
- vThis.alt = "Hide";
- vSibling.style.display = "block";
- } else {
- vSibling.style.display = "none";
- vThis.src="/static/images/fugue/toggle-expand.png";
- vThis.alt = "Show";
- }
- return;
- }
-
- $(document).ready(function(){
- //hide the all of the element with class msg_body
- $(".msg_body").hide();
- //toggle the component with class msg_body
- $(".msg_head").click(function(){
- $(this).next(".msg_body").slideToggle(0);
- });
- });
-
- // Looks for changes in sample states using an async request. Keeps
- // calling itself (via setTimeout) until all samples are in a terminal
- // state.
- var updater = function ( sample_states ) {
- // Check if there are any items left to track
- var empty = true;
- for ( i in sample_states ) {
- empty = false;
- break;
- }
- if ( ! empty ) {
- setTimeout( function() { updater_callback( sample_states ) }, 1000 );
- }
- };
-
- var updater_callback = function ( sample_states ) {
- // Build request data
- var ids = []
- var states = []
- $.each( sample_states, function ( id, state ) {
- ids.push( id );
- states.push( state );
- });
- // Make ajax call
- $.ajax( {
- type: "POST",
- url: "${h.url_for( controller='requests_common', action='sample_state_updates' )}",
- dataType: "json",
- data: { ids: ids.join( "," ), states: states.join( "," ) },
- success : function ( data ) {
- $.each( data, function( id, val ) {
- // Replace HTML
- var cell1 = $("#sampleState-" + id);
- cell1.html( val.html_state );
- var cell2 = $("#sampleDatasets-" + id);
- cell2.html( val.html_datasets );
- sample_states[ parseInt( id ) ] = val.state;
- });
- updater( sample_states );
- },
- error: function() {
- // Just retry, like the old method, should try to be smarter
- updater( sample_states );
- }
- });
- };
-
- function checkAllFields()
- {
- var chkAll = document.getElementById('checkAll');
- var checks = document.getElementsByTagName('input');
- var boxLength = checks.length;
- var allChecked = false;
- var totalChecked = 0;
- if ( chkAll.checked == true )
- {
- for ( i=0; i < boxLength; i++ )
- {
- if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
- {
- checks[i].checked = true;
- }
- }
- }
- else
- {
- for ( i=0; i < boxLength; i++ )
- {
- if ( checks[i].name.indexOf( 'select_sample_' ) != -1)
- {
- checks[i].checked = false
- }
- }
- }
- }
-
- function stopRKey(evt) {
- var evt = (evt) ? evt : ((event) ? event : null);
- var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
- if ((evt.keyCode == 13) && (node.type=="text")) {return false;}
- }
- document.onkeypress = stopRKey
- </script>
-</%def>
-
-<% is_admin = cntrller == 'requests_admin' and trans.user_is_admin() %>
-
-<div class="grid-header">
- <h2>Sequencing Request "${request.name}"</h2>
-
- <ul class="manage-table-actions">
- <li><a class="action-button" id="seqreq-${request.id}-popup" class="menubutton">Sequencing Request Actions</a></li>
- <div popupmenu="seqreq-${request.id}-popup">
- %if request.is_unsubmitted and request.samples:
- <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller='requests_common', action='submit_request', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Submit</a>
- %endif
- <a class="action-button" href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">History</a>
- %if is_admin:
- %if request.is_submitted:
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='reject', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">Reject</a>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='get_data', request_id=trans.security.encode_id( request.id ) )}">Select datasets to transfer</a>
- %endif
- %endif
- </div>
- <li><a class="action-button" href="${h.url_for( controller=cntrller, action='browse_requests' )}">Browse requests</a></li>
- </ul>
-
- <div class="toolParamHelp" style="clear: both;">
- <b>Sequencer</b>: ${request.type.name}
- %if is_admin:
- | <b>User</b>: ${request.user.email}
- %endif
- %if request.is_submitted:
- | <b>State</b>: <i>${request.state}</i>
- %else:
- | <b>State</b>: ${request.state}
- %endif
- </div>
-</div>
-
-%if request.samples_without_library_destinations:
- ${render_msg( "Select a target data library and folder for all the samples before starting the sequence run", "warning" )}
-%endif
-
-%if request.is_rejected:
- ${render_msg( "Reason for rejection: " + request.last_comment, "warning" )}
-%endif
-
-%if message:
- ${render_msg( message, status )}
-%endif
-
-<h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> Request Information</h4>
-<div style="display:none;">
- <div class="form-row">
- <ul class="manage-table-actions">
- <li>
- <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 request informaton</a>
- </li>
- </ul>
- </div>
- <table class="grid" border="0">
- <tbody>
- <tr>
- <td valign="top" width="50%">
- <div class="form-row">
- <label>Description:</label>
- ${request.desc}
- </div>
- <div style="clear: both"></div>
- %for index, rd in enumerate( request_widgets ):
- <%
- field_label = rd[ 'label' ]
- field_value = rd[ 'value' ]
- %>
- <div class="form-row">
- <label>${field_label}:</label>
- %if field_label == 'State':
- <a href="${h.url_for( controller='requests_common', action='request_events', cntrller=cntrller, id=trans.security.encode_id( request.id ) )}">${field_value}</a>
- %else:
- ${field_value}
- %endif
- </div>
- <div style="clear: both"></div>
- %endfor
- </td>
- <td valign="top" width="50%">
- <div class="form-row">
- <label>Date created:</label>
- ${request.create_time}
- </div>
- <div class="form-row">
- <label>Date updated:</label>
- ${request.update_time}
- </div>
- <div class="form-row">
- <label>Email notification recipients:</label>
- <%
- if request.notification:
- emails = ', '.join( request.notification[ 'email' ] )
- else:
- emails = ''
- %>
- ${emails}
- </div>
- <div style="clear: both"></div>
- <div class="form-row">
- <label>Email notification on sample states:</label>
- <%
- if request.notification:
- states = []
- for ss in request.type.states:
- if ss.id in request.notification[ 'sample_states' ]:
- states.append( ss.name )
- states = ', '.join( states )
- else:
- states = ''
- %>
- ${states}
- </div>
- <div style="clear: both"></div>
- </td>
- </tr>
- </tbody>
- </table>
-</div>
-<br/>
-<form id="manage_request" name="manage_request" action="${h.url_for( controller='requests_common', action='manage_request', cntrller=cntrller, id=trans.security.encode_id( request.id ), managing_samples=managing_samples )}" method="post">
- %if current_samples:
- <% sample_operation_selected_value = sample_operation_select_field.get_selected( return_value=True ) %>
- ## first render the basic info grid
- ${render_basic_info_grid()}
- %if not request.is_new and not managing_samples and len( sample_operation_select_field.options ) > 1:
- <div class="form-row" style="background-color:#FAFAFA;">
- For selected samples:
- ${sample_operation_select_field.get_html()}
- </div>
- %if sample_operation_selected_value != 'none' and selected_samples:
- <div class="form-row" style="background-color:#FAFAFA;">
- %if sample_operation_selected_value == trans.model.Sample.bulk_operations.CHANGE_STATE:
- ## sample_operation_selected_value == 'Change state'
- <div class="form-row">
- <label>Change current state</label>
- ${sample_state_id_select_field.get_html()}
- <label>Comments</label>
- <input type="text" name="sample_event_comment" value=""/>
- <div class="toolParamHelp" style="clear: both;">
- Optional
- </div>
- </div>
- <div class="form-row">
- <input type="submit" name="change_state_button" value="Save"/>
- <input type="submit" name="cancel_change_state_button" value="Cancel"/>
- </div>
- %elif sample_operation_selected_value == trans.app.model.Sample.bulk_operations.SELECT_LIBRARY:
- <% libraries_selected_value = libraries_select_field.get_selected( return_value=True ) %>
- <div class="form-row">
- <label>Select data library:</label>
- ${libraries_select_field.get_html()}
- </div>
- %if libraries_selected_value != 'none':
- <div class="form-row">
- <label>Select folder:</label>
- ${folders_select_field.get_html()}
- </div>
- <div class="form-row">
- <input type="submit" name="change_lib_button" value="Save"/>
- <input type="submit" name="cancel_change_lib_button" value="Cancel"/>
- </div>
- %endif
- %endif
- </div>
- %endif
- %endif
- ## Render the other grids
- <% trans.sa_session.refresh( request.type.sample_form ) %>
- %for grid_index, grid_name in enumerate( request.type.sample_form.layout ):
- ${render_grid( grid_index, grid_name, request.type.sample_form.fields_of_grid( grid_index ) )}
- %endfor
- %else:
- <label>There are no samples.</label>
- %endif
- %if request.samples and request.is_submitted:
- <script type="text/javascript">
- // Updater
- updater({${ ",".join( [ '"%s" : "%s"' % ( s.id, s.state.name ) for s in request.samples ] ) }});
- </script>
- %endif
- %if not managing_samples:
- <table class="grid">
- <tbody>
- <tr>
- <div class="form-row">
- %if request.is_unsubmitted:
- <td>
- %if current_samples:
- <label>Copy </label>
- <input type="integer" name="num_sample_to_copy" value="1" size="3"/>
- <label>samples from sample</label>
- ${sample_copy.get_html()}
- %endif
- <input type="submit" name="add_sample_button" value="Add New"/>
- </td>
- %endif
- <td>
- %if current_samples and len( current_samples ) <= len( request.samples ):
- <input type="submit" name="edit_samples_button" value="Edit samples"/>
- %endif
- </td>
- </div>
- </tr>
- </tbody>
- </table>
- %endif
- %if request.samples or current_samples:
- %if managing_samples:
- <div class="form-row">
- <input type="submit" name="save_samples_button" value="Save"/>
- <input type="submit" name="cancel_changes_button" value="Cancel"/>
- </div>
- %elif len( current_samples ) > len( request.samples ):
- <div class="form-row">
- <input type="submit" name="save_samples_button" value="Save"/>
- <input type="submit" name="cancel_changes_button" value="Cancel"/>
- </div>
- %endif
- %endif
-</form>
-<br/>
-%if request.is_unsubmitted:
- <form id="import" name="import" action="${h.url_for( controller='requests_common', action='manage_request', managing_samples=managing_samples, id=trans.security.encode_id( request.id ) )}" enctype="multipart/form-data" method="post" >
- <h4><img src="/static/images/fugue/toggle-expand.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> Import samples</h4>
- <div style="display:none;">
- <input type="file" name="file_data" />
- <input type="submit" name="import_samples_button" value="Import samples"/>
- <br/>
- <div class="toolParamHelp" style="clear: both;">
- The csv file must be in the following format:<br/>
- SampleName,DataLibrary,DataLibraryFolder,FieldValue1,FieldValue2...
- </div>
- </div>
- </form>
-%endif
-
-<%def name="render_grid( grid_index, grid_name, fields_dict )">
- <br/>
- <% if not grid_name:
- grid_name = "Grid "+ grid_index
- %>
- <div>
- %if managing_samples or len( current_samples ) > len( request.samples ):
- <h4><img src="/static/images/fugue/toggle.png" alt="Show" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
- <div>
- %else:
- <h4><img src="/static/images/fugue/toggle-expand.png" alt="Hide" onclick="showContent(this);" style="cursor:pointer;"/> ${grid_name}</h4>
- <div style="display:none;">
- %endif
- <table class="grid">
- <thead>
- <tr>
- <th>Name</th>
- %for index, field in fields_dict.items():
- <th>
- ${field['label']}
- <div class="toolParamHelp" style="clear: both;">
- <i>${field['helptext']}</i>
- </div>
- </th>
- %endfor
- <th></th>
- </tr>
- <thead>
- <tbody>
- <% trans.sa_session.refresh( request ) %>
- %for sample_index, sample in enumerate( current_samples ):
- %if managing_samples:
- <tr>${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict)}</tr>
- %else:
- <tr>
- %if sample_index in range( len( request.samples ) ):
- ${render_sample( sample_index, sample['name'], sample['field_values'], fields_dict )}
- %else:
- ${render_sample_form( sample_index, sample['name'], sample['field_values'], fields_dict)}
- %endif
- </tr>
- %endif
- %endfor
- </tbody>
- </table>
- </div>
- </div>
-</%def>
-
-## This function displays the "Basic Information" grid
-<%def name="render_basic_info_grid()">
- <h3>Sample Information</h3>
- <table class="grid">
- <thead>
- <tr>
- <th><input type="checkbox" id="checkAll" name=select_all_samples_checkbox value="true" onclick='checkAllFields(1);'><input type="hidden" name=select_all_samples_checkbox value="true"></th>
- <th>Name</th>
- <th>Barcode</th>
- <th>State</th>
- <th>Data Library</th>
- <th>Folder</th>
- %if request.is_submitted or request.is_complete:
- <th>Datasets Transferred</th>
- %endif
- <th></th>
- </tr>
- <thead>
- <tbody>
- <% trans.sa_session.refresh( request ) %>
- %for sample_index, info in enumerate( current_samples ):
- <%
- if sample_index in range( len(request.samples ) ):
- sample = request.samples[sample_index]
- else:
- sample = None
- %>
- %if managing_samples:
- <tr>${show_basic_info_form( sample_index, sample, info )}</tr>
- %else:
- <tr>
- %if sample_index in range( len( request.samples ) ):
- %if trans.security.encode_id( sample.id ) in selected_samples:
- <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true" checked><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"></td>
- %else:
- <td><input type="checkbox" name=select_sample_${sample.id} id="sample_checkbox" value="true"><input type="hidden" name=select_sample_${sample.id} id="sample_checkbox" value="true"></td>
- %endif
- <td>${info['name']}</td>
- <td>${info['barcode']}</td>
- %if sample.request.is_unsubmitted:
- <td>Unsubmitted</td>
- %else:
- <td><a id="sampleState-${sample.id}" href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${render_sample_state( sample )}</a></td>
- %endif
- %if info['library']:
- %if cntrller == 'requests':
- <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
- %elif is_admin:
- <td><a href="${h.url_for( controller='library_common', action='browse_library', cntrller='library_admin', id=trans.security.encode_id( info['library'].id ) )}">${info['library'].name}</a></td>
- %endif
- %else:
- <td></td>
- %endif
- %if info['folder']:
- <td>${info['folder'].name}</td>
- %else:
- <td></td>
- %endif
- %if request.is_submitted or request.is_complete:
- <td><a id="sampleDatasets-${sample.id}" href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">
- ${render_sample_datasets( sample )}
- </a></td>
- %endif
- %else:
- ${show_basic_info_form( sample_index, sample, info )}
- %endif
- %if request.is_unsubmitted or request.is_rejected:
- <td>
- %if sample:
- %if sample.request.is_unsubmitted:
- <a class="action-button" href="${h.url_for( controller='requests_common', cntrller=cntrller, action='delete_sample', request_id=trans.security.encode_id( request.id ), sample_id=sample_index )}"><img src="${h.url_for('/static/images/delete_icon.png')}" style="cursor:pointer;"/></a>
- %endif
- %endif
- </td>
- %endif
- </tr>
- %endif
- %endfor
- </tbody>
- </table>
-</%def>
-
-<%def name="show_basic_info_form( sample_index, sample, info )">
- <td></td>
- <td>
- <input type="text" name="sample_${sample_index}_name" value="${info['name']}" size="10"/>
- <div class="toolParamHelp" style="clear: both;">
- <i>${' (required)' }</i>
- </div>
- </td>
- %if cntrller == 'requests':
- %if sample:
- %if sample.request.is_unsubmitted:
- <td></td>
- %else:
- <td><input type="text" name="sample_${sample_index}_barcode" value="${info['barcode']}" size="10"/></td>
- %endif
- %else:
- <td></td>
- %endif
- %elif is_admin:
- %if sample:
- %if sample.request.is_unsubmitted:
- <td></td>
- %else:
- <td><input type="text" name="sample_${sample_index}_barcode" value="${info['barcode']}" size="10"/></td>
- %endif
- %else:
- <td></td>
- %endif
- %endif
- %if sample:
- %if sample.request.is_unsubmitted:
- <td>Unsubmitted</td>
- %else:
- <td><a href="${h.url_for( controller='requests_common', action='sample_events', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${sample.state.name}</a></td>
- %endif
- %else:
- <td></td>
- %endif
- <td>${info['library_select_field'].get_html()}</td>
- <td>${info['folder_select_field'].get_html()}</td>
- %if request.is_submitted or request.is_complete:
- <%
- if sample:
- label = str( len( sample.datasets ) )
- else:
- label = 'Add'
- %>
- <td><a href="${h.url_for( controller='requests_common', action='view_dataset_transfer', cntrller=cntrller, sample_id=trans.security.encode_id( sample.id ) )}">${label}</a></td>
- %endif
-</%def>
-
-<%def name="render_sample( index, sample_name, sample_values, fields_dict )">
- <td>
- ${sample_name}
- </td>
- %for field_index, field in fields_dict.items():
- <td>
- %if sample_values[field_index]:
- %if field['type'] == 'WorkflowField':
- %if str(sample_values[field_index]) != 'none':
- <% workflow = trans.sa_session.query( trans.app.model.StoredWorkflow ).get( int(sample_values[field_index]) ) %>
- <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name}</a>
- %endif
- %else:
- ${sample_values[field_index]}
- %endif
- %else:
- <i>None</i>
- %endif
- </td>
- %endfor
-</%def>
-
-<%def name="render_sample_form( index, sample_name, sample_values, fields_dict )">
- <td>${sample_name}</td>
- %for field_index, field in fields_dict.items():
- <td>
- %if field['type'] == 'TextField':
- <input type="text" name="sample_${index}_field_${field_index}" value="${sample_values[field_index]}" size="7"/>
- %elif field['type'] == 'SelectField':
- <select name="sample_${index}_field_${field_index}" last_selected_value="2">
- %for option_index, option in enumerate(field['selectlist']):
- %if option == sample_values[field_index]:
- <option value="${option}" selected>${option}</option>
- %else:
- <option value="${option}">${option}</option>
- %endif
- %endfor
- </select>
- %elif field['type'] == 'WorkflowField':
- <select name="sample_${index}_field_${field_index}">
- %if str(sample_values[field_index]) == 'none':
- <option value="none" selected>Select one</option>
- %else:
- <option value="none">Select one</option>
- %endif
- %for option_index, option in enumerate(request.user.stored_workflows):
- %if not option.deleted:
- %if str(option.id) == str(sample_values[field_index]):
- <option value="${option.id}" selected>${option.name}</option>
- %else:
- <option value="${option.id}">${option.name}</option>
- %endif
- %endif
- %endfor
- </select>
- %elif field['type'] == 'CheckboxField':
- <input type="checkbox" name="sample_${index}_field_${field_index}" value="Yes"/>
- %endif
- <div class="toolParamHelp" style="clear: both;">
- <i>${'('+field['required']+')' }</i>
- </div>
- </td>
- %endfor
-</%def>
1
0

galaxy-dist commit 9f74170b7781: Fix for multiple hide dataset actions when using workflow outputs.
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 1288377653 14400
# Node ID 9f74170b7781122577b8cf7ffe38f5c986617844
# Parent a2e4d36c4318bc4ce73af9df869ae4071af33d55
Fix for multiple hide dataset actions when using workflow outputs.
--- a/lib/galaxy/web/controllers/workflow.py
+++ b/lib/galaxy/web/controllers/workflow.py
@@ -1299,6 +1299,11 @@ class WorkflowController( BaseController
step_outputs = [s.output_name for s in step.workflow_outputs]
for output in tool.outputs.keys():
if output not in step_outputs:
+ # Necessary, unfortunately, to clean up workflows that might have more than one at this point.
+ for pja in step.post_job_actions:
+ if pja.action_type == "HideDatasetAction" and pja.output_name == output:
+ step.post_job_actions.remove(pja)
+ trans.sa_session.delete(pja)
# Create a PJA for hiding this output.
n_pja = PostJobAction('HideDatasetAction', step, output, {})
else:
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 1288377687 14400
# Node ID a22917c97fbe89aa2c0393d30f51d2a2183fd2f8
# Parent 9f74170b7781122577b8cf7ffe38f5c986617844
# Parent cbb895e2b2729d324c1693882de7939360e683a4
Merge.
--- a/test-data/cuffdiff_out5.tracking
+++ /dev/null
@@ -1,2 +0,0 @@
-tracking_id class_code nearest_ref_id gene_short_name tss_id locus q0_FPKM q0_conf_lo q0_conf_hi q1_FPKM q1_conf_lo q1_conf_hi
-TCONS_00000001 c Xkr4 - - chr1:3204754-3204833 0 0 0 67745 0 163551
1
0

galaxy-dist commit 0518ce52d8d3: Allow jobs to be stopped when using track_jobs_in_database/enable_job_running (multiprocess config).
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1288379862 14400
# Node ID 0518ce52d8d3ca15ec6070f2addb4bfe7608c23b
# Parent a22917c97fbe89aa2c0393d30f51d2a2183fd2f8
Allow jobs to be stopped when using track_jobs_in_database/enable_job_running (multiprocess config).
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -813,6 +813,8 @@ class JobStopQueue( object ):
self.sa_session = app.model.context
self.dispatcher = dispatcher
+ self.track_jobs_in_database = app.config.get_bool( 'track_jobs_in_database', False )
+
# Keep track of the pid that started the job manager, only it
# has valid threads
self.parent_pid = os.getpid()
@@ -848,21 +850,29 @@ class JobStopQueue( object ):
Called repeatedly by `monitor` to stop jobs.
"""
# Pull all new jobs from the queue at once
- jobs = []
- try:
- while 1:
- ( job_id, error_msg ) = self.queue.get_nowait()
- if job_id is self.STOP_SIGNAL:
- return
- # Append to watch queue
- jobs.append( ( job_id, error_msg ) )
- except Empty:
- pass
-
- for job_id, error_msg in jobs:
- job = self.sa_session.query( model.Job ).get( job_id )
- self.sa_session.refresh( job )
- # if desired, error the job so we can inform the user.
+ jobs_to_check = []
+ if self.track_jobs_in_database:
+ # Clear the session so we get fresh states for job and all datasets
+ self.sa_session.expunge_all()
+ # Fetch all new jobs
+ newly_deleted_jobs = self.sa_session.query( model.Job ) \
+ .options( lazyload( "external_output_metadata" ), lazyload( "parameters" ) ) \
+ .filter( model.Job.state == model.Job.states.DELETED_NEW ).all()
+ for job in newly_deleted_jobs:
+ jobs_to_check.append( ( job, None ) )
+ else:
+ try:
+ while 1:
+ message = self.queue.get_nowait()
+ if message is self.STOP_SIGNAL:
+ return
+ # Unpack the message
+ job_id, error_msg = message
+ # Get the job object and append to watch queue
+ jobs_to_check.append( ( self.sa_session.query( model.Job ).get( job_id ), error_msg ) )
+ except Empty:
+ pass
+ for job, error_msg in jobs_to_check:
if error_msg is not None:
job.state = job.states.ERROR
job.info = error_msg
@@ -870,9 +880,6 @@ class JobStopQueue( object ):
job.state = job.states.DELETED
self.sa_session.add( job )
self.sa_session.flush()
- # if job is in JobQueue or FooJobRunner's put method,
- # job_runner_name will be unset and the job will be dequeued due to
- # state change above
if job.job_runner_name is not None:
# tell the dispatcher to stop the job
self.dispatcher.stop( job )
@@ -888,7 +895,8 @@ class JobStopQueue( object ):
else:
log.info( "sending stop signal to worker thread" )
self.running = False
- self.queue.put( ( self.STOP_SIGNAL, None ) )
+ if not self.track_jobs_in_database:
+ self.queue.put( self.STOP_SIGNAL )
self.sleeper.wake()
log.info( "job stopper stopped" )
--- a/lib/galaxy/web/controllers/root.py
+++ b/lib/galaxy/web/controllers/root.py
@@ -432,7 +432,8 @@ class RootController( BaseController, Us
if job.state in [ self.app.model.Job.states.QUEUED, self.app.model.Job.states.RUNNING, self.app.model.Job.states.NEW ]:
# Are *all* of the job's other output datasets deleted?
if job.check_if_output_datasets_deleted():
- job.mark_deleted()
+ job.mark_deleted( self.app.config.get_bool( 'enable_job_running', True ),
+ self.app.config.get_bool( 'track_jobs_in_database', False ) )
self.app.job_manager.job_stop_queue.put( job.id )
trans.sa_session.flush()
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -97,7 +97,8 @@ class Job( object ):
RUNNING = 'running',
OK = 'ok',
ERROR = 'error',
- DELETED = 'deleted' )
+ DELETED = 'deleted',
+ DELETED_NEW = 'deleted_new' )
def __init__( self ):
self.session_id = None
self.user_id = None
@@ -152,11 +153,17 @@ class Job( object ):
if not dataset.deleted:
return False
return True
- def mark_deleted( self ):
+ def mark_deleted( self, enable_job_running=True, track_jobs_in_database=False ):
"""
Mark this job as deleted, and mark any output datasets as discarded.
"""
- self.state = Job.states.DELETED
+ # This could be handled with *just* track_jobs_in_database, but I
+ # didn't want to make setting track_jobs_in_database required in
+ # non-runner configs.
+ if not enable_job_running or track_jobs_in_database:
+ self.state = Job.states.DELETED_NEW
+ else:
+ self.state = Job.states.DELETED
self.info = "Job output deleted by user before job completed."
for dataset_assoc in self.output_datasets:
dataset = dataset_assoc.dataset
1
0

galaxy-dist commit 1efe19f6f3d1: Fixes for rerun action to recurse grouping options when checking unvalidated values and cloned HDAs. Better selection of corresponding HDAs from cloned histories, when multiple copies exist.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Daniel Blankenberg <dan(a)bx.psu.edu>
# Date 1288640846 14400
# Node ID 1efe19f6f3d1a75bda9c198633dd8102d3063410
# Parent 2c934a168af968dc84300412220c2f2cb74bf676
Fixes for rerun action to recurse grouping options when checking unvalidated values and cloned HDAs. Better selection of corresponding HDAs from cloned histories, when multiple copies exist.
--- a/lib/galaxy/web/controllers/tool_runner.py
+++ b/lib/galaxy/web/controllers/tool_runner.py
@@ -123,16 +123,6 @@ class ToolRunner( BaseController ):
params_objects = job.get_param_values( trans.app )
except:
raise Exception( "Failed to get paramemeters for dataset id %d " % data.id )
- # Unpack unvalidated values to strings, they'll be validated when the
- # form is submitted (this happens when re-running a job that was
- # initially run by a workflow)
- validated_params = {}
- for name, value in params_objects.items():
- if isinstance( value, UnvalidatedValue ):
- validated_params [ str(name) ] = str(value)
- else:
- validated_params [ str(name) ] = value
- params_objects = validated_params
# Need to remap dataset parameters. Job parameters point to original
# dataset used; parameter should be the analygous dataset in the
# current history.
@@ -141,12 +131,22 @@ class ToolRunner( BaseController ):
for hda in history.datasets:
source_hda = hda.copied_from_history_dataset_association
while source_hda:#should this check library datasets as well?
- hda_source_dict[ source_hda ] = hda
+ #FIXME: could be multiple copies of a hda in a single history, this does a better job of matching on cloned histories,
+ #but is still less than perfect when eg individual datasets are copied between histories
+ if source_hda not in hda_source_dict or source_hda.hid == hda.hid:
+ hda_source_dict[ source_hda ] = hda
source_hda = source_hda.copied_from_history_dataset_association
- for name, value in validated_params.items():
- if isinstance( value, trans.app.model.HistoryDatasetAssociation ):
- if value not in history.datasets:
- validated_params[ name ] = hda_source_dict[ value ]
+ # Unpack unvalidated values to strings, they'll be validated when the
+ # form is submitted (this happens when re-running a job that was
+ # initially run by a workflow)
+ #This needs to be done recursively through grouping parameters
+ def rerun_callback( input, value, prefixed_name, prefixed_label ):
+ if isinstance( value, UnvalidatedValue ):
+ return str( value )
+ if isinstance( input, DataToolParameter ):
+ if value not in history.datasets and value in hda_source_dict:
+ return hda_source_dict[ value ]
+ visit_input_values( tool.inputs, params_objects, rerun_callback )
# Create a fake tool_state for the tool, with the parameters values
state = tool.new_state( trans )
state.inputs = params_objects
1
0

galaxy-dist commit cf7ec71c5613: Shift management of the interaction between workflow outputs and HideDatasetActions to the front end editor.
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 1288645730 14400
# Node ID cf7ec71c561323940ce95c8148d2008508920092
# Parent 1efe19f6f3d1a75bda9c198633dd8102d3063410
Shift management of the interaction between workflow outputs and HideDatasetActions to the front end editor.
This resolves the issue with multiple HideDatasetActions being created.
Existing workflows displaying multiple HideDatasetActions per step on the Run Workflow screen will persist.
These extra HideDatasetActions are harmless, but a simple edit workflow -> save will remove them.
--- a/lib/galaxy/web/controllers/workflow.py
+++ b/lib/galaxy/web/controllers/workflow.py
@@ -1270,13 +1270,6 @@ class WorkflowController( BaseController
workflow_invocation = model.WorkflowInvocation()
workflow_invocation.workflow = workflow
outputs = odict()
- # Find out if there are any workflow outputs defined, as that influences our actions.
- use_workflow_outputs = False
- for step in workflow.steps:
- if step.type == 'tool' or step.type is None:
- if step.workflow_outputs:
- use_workflow_outputs = True
- break
for i, step in enumerate( workflow.steps ):
# Execute module
job = None
@@ -1294,24 +1287,6 @@ class WorkflowController( BaseController
job, out_data = tool.execute( trans, step.state.inputs )
outputs[ step.id ] = out_data
# Create new PJA associations with the created job, to be run on completion.
- if use_workflow_outputs:
- # We're using outputs. Check the step for outputs to be displayed. Create PJAs to hide the rest upon completion.
- step_outputs = [s.output_name for s in step.workflow_outputs]
- for output in tool.outputs.keys():
- if output not in step_outputs:
- # Necessary, unfortunately, to clean up workflows that might have more than one at this point.
- for pja in step.post_job_actions:
- if pja.action_type == "HideDatasetAction" and pja.output_name == output:
- step.post_job_actions.remove(pja)
- trans.sa_session.delete(pja)
- # Create a PJA for hiding this output.
- n_pja = PostJobAction('HideDatasetAction', step, output, {})
- else:
- # Remove any HideDatasetActions, step is flagged for output.
- for pja in step.post_job_actions:
- if pja.action_type == "HideDatasetAction" and pja.output_name == output:
- step.post_job_actions.remove(pja)
- trans.sa_session.delete(pja)
for pja in step.post_job_actions:
if pja.action_type in ActionBox.immediate_actions:
ActionBox.execute(trans.app, trans.sa_session, pja, job)
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -475,6 +475,63 @@ function Workflow( canvas_container ) {
wf.remove_node( v );
});
},
+ rectify_workflow_outputs : function() {
+ console.log("RECTIFICATION!");
+ // Find out if we're using workflow_outputs or not.
+ var using_workflow_outputs = false;
+ $.each( this.nodes, function ( k, node ) {
+ if (node.workflow_outputs && node.workflow_outputs.length > 0){
+ using_workflow_outputs = true;
+ }
+ });
+ if (using_workflow_outputs == false){
+ //We're done, leave PJAs alone.
+ return true;
+ }
+ wf = this;
+ $.each(this.nodes, function (k, node ){
+ if (node.type == 'tool'){
+ var node_changed = false;
+ if (node.post_job_actions == null){
+ console.log("CREATED FOR NEW NODE");
+ node.post_job_actions = {};
+ }
+ var pjas_to_rem = [];
+ $.each(node.post_job_actions, function(pja_id, pja){
+ if (pja.action_type == "HideDatasetAction"){
+ pjas_to_rem.push(pja_id);
+ }
+ });
+ if (pjas_to_rem.length > 0 && node == workflow.active_node)
+ $.each(pjas_to_rem, function(i, pja_name){
+ node_changed = true;
+ delete node.post_job_actions[pja_name];
+ })
+ $.each(node.output_terminals, function(ot_id, ot){
+ var create_pja = true;
+ $.each(node.workflow_outputs, function(i, wo_name){
+ if (ot.name == wo_name){
+ create_pja = false;
+ }
+ });
+ if (create_pja == true){
+ node_changed = true;
+ var pja = {
+ action_type : "HideDatasetAction",
+ output_name : ot.name,
+ action_arguments : {}
+ }
+ node.post_job_actions['HideDatasetAction'+ot.name] = null;
+ node.post_job_actions['HideDatasetAction'+ot.name] = pja;
+ }
+ });
+ // lastly, if this is the active node, and we made changes, reload the display at right.
+ if (wf.active_node == node && node_changed == true) {
+ wf.reload_active_node();
+ }
+ }
+ });
+ },
to_simple : function () {
var nodes = {};
$.each( this.nodes, function ( i, node ) {
@@ -491,7 +548,6 @@ function Workflow( canvas_container ) {
if (node.post_job_actions){
$.each( node.post_job_actions, function ( i, act ) {
var pja = {
- job_id : act.id,
action_type : act.action_type,
output_name : act.output_name,
action_arguments : act.action_arguments
@@ -559,6 +615,10 @@ function Workflow( canvas_container ) {
this.active_form_has_changes = false;
}
},
+ reload_active_node : function() {
+ this.clear_active_node();
+ this.activate_node(node);
+ },
clear_active_node : function() {
if ( this.active_node ) {
this.active_node.make_inactive();
--- a/templates/workflow/editor.mako
+++ b/templates/workflow/editor.mako
@@ -627,6 +627,7 @@
}
return;
}
+ workflow.rectify_workflow_outputs();
var savefn = function(callback) {
$.ajax( {
url: "${h.url_for( action='save_workflow' )}",
1
0

galaxy-dist commit cbb895e2b272: If a job's input fails to set metadata, fail the job rather than leaving it in the 'new' state indefinitely.
by commits-noreply@bitbucket.org 20 Nov '10
by commits-noreply@bitbucket.org 20 Nov '10
20 Nov '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1288376593 14400
# Node ID cbb895e2b2729d324c1693882de7939360e683a4
# Parent 23844e86e6167c79737a7e0396c39e350e95d1c4
If a job's input fails to set metadata, fail the job rather than leaving it in the 'new' state indefinitely.
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -241,6 +241,9 @@ class JobQueue( object ):
elif idata.state == idata.states.ERROR:
JobWrapper( job, self ).fail( "input data %d is in error state" % ( idata.hid ) )
return JOB_INPUT_ERROR
+ elif idata.state == idata.states.FAILED_METADATA:
+ JobWrapper( job, self ).fail( "input data %d failed to properly set metadata" % ( idata.hid ) )
+ return JOB_INPUT_ERROR
elif idata.state != idata.states.OK and not ( idata.state == idata.states.SETTING_METADATA and job.tool_id is not None and job.tool_id == self.app.datatypes_registry.set_external_metadata_tool.id ):
# need to requeue
return JOB_WAIT
1
0