mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Request events & request rejection (resolves issue #221)
- added request_events table - removed 'state' field in the request table - added 'new' & 'rejected' request states - color coded request states in the request grid Grids framework - added a confirm flag in grid operations which require confirmation before going through with operation
This commit is contained in:
@@ -1260,11 +1260,12 @@ class FormValues( object ):
|
||||
self.content = content
|
||||
|
||||
class Request( object ):
|
||||
states = Bunch( UNSUBMITTED = 'Unsubmitted',
|
||||
states = Bunch( NEW = 'New',
|
||||
SUBMITTED = 'Submitted',
|
||||
REJECTED = 'Rejected',
|
||||
COMPLETE = 'Complete')
|
||||
def __init__(self, name=None, desc=None, request_type=None, user=None,
|
||||
form_values=None, library=None, folder=None, state=False):
|
||||
form_values=None, library=None, folder=None):
|
||||
self.name = name
|
||||
self.desc = desc
|
||||
self.type = request_type
|
||||
@@ -1272,19 +1273,39 @@ class Request( object ):
|
||||
self.user = user
|
||||
self.library = library
|
||||
self.folder = folder
|
||||
self.state = state
|
||||
self.samples_list = []
|
||||
def state(self):
|
||||
if self.events:
|
||||
return self.events[0].state
|
||||
return None
|
||||
def last_comment(self):
|
||||
if self.events:
|
||||
if self.events[0].comment:
|
||||
return self.events[0].comment
|
||||
else:
|
||||
return ''
|
||||
return 'No comment'
|
||||
def has_sample(self, sample_name):
|
||||
for s in self.samples:
|
||||
if s.name == sample_name:
|
||||
return s
|
||||
return False
|
||||
def submitted(self):
|
||||
return self.state == self.states.SUBMITTED
|
||||
def unsubmitted(self):
|
||||
return self.state == self.states.UNSUBMITTED
|
||||
return self.state() in [ self.states.REJECTED, self.states.NEW ]
|
||||
def rejected(self):
|
||||
return self.state() == self.states.REJECTED
|
||||
def submitted(self):
|
||||
return self.state() == self.states.SUBMITTED
|
||||
def new(self):
|
||||
return self.state() == self.states.NEW
|
||||
def complete(self):
|
||||
return self.state == self.states.COMPLETE
|
||||
return self.state() == self.states.COMPLETE
|
||||
|
||||
class RequestEvent( object ):
|
||||
def __init__(self, request=None, request_state=None, comment=''):
|
||||
self.request = request
|
||||
self.state = request_state
|
||||
self.comment = comment
|
||||
|
||||
class RequestType( object ):
|
||||
def __init__(self, name=None, desc=None, request_form=None, sample_form=None):
|
||||
|
||||
@@ -618,9 +618,16 @@ Request.table = Table('request', metadata,
|
||||
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
|
||||
Column( "library_id", Integer, ForeignKey( "library.id" ), index=True ),
|
||||
Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ),
|
||||
Column( "state", TrimmedString( 255 ), index=True ),
|
||||
Column( "deleted", Boolean, index=True, default=False ) )
|
||||
|
||||
RequestEvent.table = Table('request_event', metadata,
|
||||
Column( "id", Integer, primary_key=True),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ),
|
||||
Column( "request_id", Integer, ForeignKey( "request.id" ), index=True ),
|
||||
Column( "state", TrimmedString( 255 ), index=True ),
|
||||
Column( "comment", TEXT ) )
|
||||
|
||||
Sample.table = Table('sample', metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
@@ -768,9 +775,14 @@ assign_mapper( context, Request, Request.table,
|
||||
folder=relation( LibraryFolder,
|
||||
primaryjoin=( Request.table.c.folder_id == LibraryFolder.table.c.id ) ),
|
||||
library=relation( Library,
|
||||
primaryjoin=( Request.table.c.library_id == Library.table.c.id ) )
|
||||
primaryjoin=( Request.table.c.library_id == Library.table.c.id ) ),
|
||||
events=relation( RequestEvent, backref="request",
|
||||
order_by=desc(RequestEvent.table.c.update_time) )
|
||||
) )
|
||||
|
||||
assign_mapper( context, RequestEvent, RequestEvent.table,
|
||||
properties=None )
|
||||
|
||||
assign_mapper( context, RequestType, RequestType.table,
|
||||
properties=dict( states=relation( SampleState,
|
||||
backref="request_type",
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
"""
|
||||
This migration script adds the request_event table and
|
||||
removes the state field in the request table
|
||||
"""
|
||||
from sqlalchemy import *
|
||||
from sqlalchemy.orm import *
|
||||
from sqlalchemy.exc import *
|
||||
from migrate import *
|
||||
from migrate.changeset import *
|
||||
|
||||
import datetime
|
||||
now = datetime.datetime.utcnow
|
||||
|
||||
import sys, logging
|
||||
log = logging.getLogger( __name__ )
|
||||
log.setLevel(logging.DEBUG)
|
||||
handler = logging.StreamHandler( sys.stdout )
|
||||
format = "%(name)s %(levelname)s %(asctime)s %(message)s"
|
||||
formatter = logging.Formatter( format )
|
||||
handler.setFormatter( formatter )
|
||||
log.addHandler( handler )
|
||||
|
||||
# Need our custom types, but don't import anything else from model
|
||||
from galaxy.model.custom_types import *
|
||||
|
||||
metadata = MetaData( migrate_engine )
|
||||
db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) )
|
||||
|
||||
def display_migration_details():
|
||||
print "========================================"
|
||||
print "This migration script adds the request_event table and"
|
||||
print "removes the state field in the request table"
|
||||
print "========================================"
|
||||
|
||||
def localtimestamp():
|
||||
if migrate_engine.name == 'postgres' or migrate_engine.name == 'mysql':
|
||||
return "LOCALTIMESTAMP"
|
||||
elif migrate_engine.name == 'sqlite':
|
||||
return "current_date || ' ' || current_time"
|
||||
else:
|
||||
raise Exception( 'Unable to convert data for unknown database type: %s' % db )
|
||||
|
||||
def nextval( table, col='id' ):
|
||||
if migrate_engine.name == 'postgres':
|
||||
return "nextval('%s_%s_seq')" % ( table, col )
|
||||
elif migrate_engine.name == 'mysql' or migrate_engine.name == 'sqlite':
|
||||
return "null"
|
||||
else:
|
||||
raise Exception( 'Unable to convert data for unknown database type: %s' % migrate_engine.name )
|
||||
|
||||
|
||||
RequestEvent_table = Table('request_event', metadata,
|
||||
Column( "id", Integer, primary_key=True),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ),
|
||||
Column( "request_id", Integer, ForeignKey( "request.id" ), index=True ),
|
||||
Column( "state", TrimmedString( 255 ), index=True ),
|
||||
Column( "comment", TEXT ) )
|
||||
|
||||
def upgrade():
|
||||
display_migration_details()
|
||||
# Load existing tables
|
||||
metadata.reflect()
|
||||
# Add new request_event table
|
||||
try:
|
||||
RequestEvent_table.create()
|
||||
except Exception, e:
|
||||
log.debug( "Creating request_event table failed: %s" % str( e ) )
|
||||
# move the current state of all existing requests to the request_event table
|
||||
cmd = \
|
||||
"INSERT INTO request_event " + \
|
||||
"SELECT %s AS id," + \
|
||||
"%s AS create_time," + \
|
||||
"%s AS update_time," + \
|
||||
"request.id AS request_id," + \
|
||||
"request.state AS state " + \
|
||||
"FROM request;"
|
||||
cmd = cmd % ( nextval('request_event'), localtimestamp(), localtimestamp() )
|
||||
db_session.execute( cmd )
|
||||
|
||||
# Delete the state column
|
||||
try:
|
||||
Request_table = Table( "request", metadata, autoload=True )
|
||||
except NoSuchTableError:
|
||||
Request_table = None
|
||||
log.debug( "Failed loading table request" )
|
||||
if Request_table:
|
||||
try:
|
||||
Request_table.c.state.drop()
|
||||
except Exception, e:
|
||||
log.debug( "Deleting column 'state' to request table failed: %s" % ( str( e ) ) )
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
@@ -28,27 +28,57 @@ class RequestsGrid( grids.Grid ):
|
||||
return request.type.name
|
||||
class LastUpdateColumn( grids.TextColumn ):
|
||||
def get_value(self, trans, grid, request):
|
||||
return request.update_time
|
||||
delta = datetime.utcnow() - request.update_time
|
||||
if delta > timedelta( minutes=60 ):
|
||||
last_update = '%s hours' % int( delta.seconds / 60 / 60 )
|
||||
else:
|
||||
last_update = '%s minutes' % int( delta.seconds / 60 )
|
||||
return last_update
|
||||
class StateColumn( grids.GridColumn ):
|
||||
def __init__( self, col_name, key, model_class, event_class, filterable, link ):
|
||||
grids.GridColumn.__init__(self, col_name, key=key, model_class=model_class, filterable=filterable, link=link)
|
||||
self.event_class = event_class
|
||||
def get_value(self, trans, grid, request):
|
||||
if request.state() == request.states.REJECTED:
|
||||
return '<div class="count-box state-color-error">%s</div>' % request.state()
|
||||
elif request.state() == request.states.NEW:
|
||||
return '<div class="count-box state-color-queued">%s</div>' % request.state()
|
||||
elif request.state() == request.states.SUBMITTED:
|
||||
return '<div class="count-box state-color-running">%s</div>' % request.state()
|
||||
elif request.state() == request.states.COMPLETE:
|
||||
return '<div class="count-box state-color-ok">%s</div>' % request.state()
|
||||
return request.state()
|
||||
def filter( self, db_session, query, column_filter ):
|
||||
""" Modify query to filter request by state. """
|
||||
if column_filter == "All":
|
||||
return query
|
||||
if column_filter:
|
||||
query = query.filter( model.Request.state == column_filter )
|
||||
return query
|
||||
# select r.id, r.name, re.id, re.state
|
||||
# from request as r, request_event as re
|
||||
# where re.request_id=r.id and re.state='Complete' and re.create_time in
|
||||
# (select MAX( create_time)
|
||||
# from request_event
|
||||
# group by request_id)
|
||||
q = query.join(self.event_class.table)\
|
||||
.filter( self.model_class.table.c.id==self.event_class.table.c.request_id )\
|
||||
.filter( self.event_class.table.c.state==column_filter )\
|
||||
.filter( self.event_class.table.c.id.in_(select(columns=[func.max(self.event_class.table.c.id)],
|
||||
from_obj=self.event_class.table,
|
||||
group_by=self.event_class.table.c.request_id)))
|
||||
return q
|
||||
def get_accepted_filters( self ):
|
||||
""" Returns a list of accepted filters for this column. """
|
||||
accepted_filter_labels_and_vals = [ model.Request.states.UNSUBMITTED,
|
||||
model.Request.states.SUBMITTED,
|
||||
model.Request.states.COMPLETE,
|
||||
"All"]
|
||||
accepted_filters = []
|
||||
for val in accepted_filter_labels_and_vals:
|
||||
label = val.lower()
|
||||
args = { self.key: val }
|
||||
accepted_filters.append( grids.GridColumnFilter( label, args) )
|
||||
return accepted_filters
|
||||
""" Returns a list of accepted filters for this column. """
|
||||
accepted_filter_labels_and_vals = [ model.Request.states.NEW,
|
||||
model.Request.states.REJECTED,
|
||||
model.Request.states.SUBMITTED,
|
||||
model.Request.states.COMPLETE,
|
||||
"All"]
|
||||
accepted_filters = []
|
||||
for val in accepted_filter_labels_and_vals:
|
||||
label = val.lower()
|
||||
args = { self.key: val }
|
||||
accepted_filters.append( grids.GridColumnFilter( label, args) )
|
||||
return accepted_filters
|
||||
class DeletedColumn( grids.GridColumn ):
|
||||
def get_accepted_filters( self ):
|
||||
""" Returns a list of accepted filters for this column. """
|
||||
@@ -66,7 +96,7 @@ class RequestsGrid( grids.Grid ):
|
||||
num_rows_per_page = 50
|
||||
preserve_state = True
|
||||
use_paging = True
|
||||
default_filter = dict( deleted="False", state=model.Request.states.UNSUBMITTED)
|
||||
default_filter = dict( deleted="False", state=model.Request.states.NEW)
|
||||
columns = [
|
||||
NameColumn( "Name",
|
||||
key="name",
|
||||
@@ -83,23 +113,27 @@ class RequestsGrid( grids.Grid ):
|
||||
TypeColumn( "Type" ),
|
||||
LastUpdateColumn( "Last update",
|
||||
format=time_ago ),
|
||||
StateColumn( "State",
|
||||
key='state',
|
||||
filterable="advanced"),
|
||||
DeletedColumn( "Deleted",
|
||||
key="deleted",
|
||||
visible=True,
|
||||
filterable="advanced" )
|
||||
visible=False,
|
||||
filterable="advanced" ),
|
||||
StateColumn( "State",
|
||||
model_class=model.Request,
|
||||
event_class=model.RequestEvent,
|
||||
key='state',
|
||||
filterable="advanced",
|
||||
link=( lambda item: iff( item.deleted, None, dict( operation="events", id=item.id ) ) ) )
|
||||
]
|
||||
columns.append( grids.MulticolFilterColumn( "Search",
|
||||
cols_to_filter=[ columns[0], columns[1] ],
|
||||
cols_to_filter=[ columns[0], columns[1], columns[6] ],
|
||||
key="free-text-search",
|
||||
visible=False,
|
||||
filterable="standard" ) )
|
||||
operations = [
|
||||
grids.GridOperation( "Submit", allow_multiple=False, condition=( lambda item: not item.deleted and item.unsubmitted() and item.samples ) ),
|
||||
grids.GridOperation( "Submit", allow_multiple=False, condition=( lambda item: not item.deleted and item.unsubmitted() and item.samples ),
|
||||
confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." ),
|
||||
grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted and item.unsubmitted() ) ),
|
||||
grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted and item.unsubmitted() ) ),
|
||||
grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted and item.new() ) ),
|
||||
grids.GridOperation( "Undelete", allow_multiple=True, condition=( lambda item: item.deleted ) )
|
||||
|
||||
]
|
||||
@@ -145,9 +179,23 @@ class Requests( BaseController ):
|
||||
return self.__undelete_request( trans, **kwd )
|
||||
elif operation == "edit":
|
||||
return self.__edit_request( trans, **kwd )
|
||||
elif operation == "events":
|
||||
return self.__request_events( trans, **kwd )
|
||||
# if there are one or more requests that has been rejected by the admin
|
||||
# recently, then show a msg as a reminder to the user
|
||||
rlist = trans.sa_session.query( trans.app.model.Request ) \
|
||||
.filter( trans.app.model.Request.table.c.deleted==False ) \
|
||||
.filter( trans.app.model.Request.table.c.user_id==trans.user.id )
|
||||
rejected = 0
|
||||
for r in rlist:
|
||||
if r.rejected():
|
||||
rejected = rejected + 1
|
||||
if rejected:
|
||||
kwd['status'] = 'warning'
|
||||
kwd['message'] = "%d requests (highlighted in red) were rejected, click on the request name for details." \
|
||||
% rejected
|
||||
# Render the list view
|
||||
return self.request_grid( trans, **kwd )
|
||||
|
||||
def __show_request(self, trans, **kwd):
|
||||
params = util.Params( kwd )
|
||||
msg = util.restore_text( params.get( 'msg', '' ) )
|
||||
@@ -172,6 +220,28 @@ class Requests( BaseController ):
|
||||
sample_copy=self.__copy_sample(current_samples),
|
||||
details='hide', edit_mode='False',
|
||||
msg=msg, messagetype=messagetype )
|
||||
def __request_events(self, trans, **kwd):
|
||||
try:
|
||||
request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id(kwd['id']) )
|
||||
except:
|
||||
msg = "Invalid request ID"
|
||||
log.warn( msg )
|
||||
return trans.response.send_redirect( web.url_for( controller='requests',
|
||||
action='list',
|
||||
status='error',
|
||||
message=msg,
|
||||
**kwd) )
|
||||
events_list = []
|
||||
all_events = request.events
|
||||
for event in all_events:
|
||||
delta = datetime.utcnow() - event.update_time
|
||||
if delta > timedelta( minutes=60 ):
|
||||
last_update = '%s hours' % int( delta.seconds / 60 / 60 )
|
||||
else:
|
||||
last_update = '%s minutes' % int( delta.seconds / 60 )
|
||||
events_list.append((event.state, last_update, event.comment))
|
||||
return trans.fill_template( '/requests/events.mako',
|
||||
events_list=events_list, request=request)
|
||||
def request_details(self, trans, id):
|
||||
'''
|
||||
Shows the request details
|
||||
@@ -187,7 +257,7 @@ class Requests( BaseController ):
|
||||
value=request.type.name,
|
||||
helptext=''))
|
||||
request_details.append(dict(label='State',
|
||||
value=request.state,
|
||||
value=request.state(),
|
||||
helptext=''))
|
||||
request_details.append(dict(label='Date created',
|
||||
value=request.create_time,
|
||||
@@ -366,9 +436,9 @@ class Requests( BaseController ):
|
||||
trans.sa_session.add( sample )
|
||||
trans.sa_session.flush()
|
||||
return trans.response.send_redirect( web.url_for( controller='requests',
|
||||
action='list',
|
||||
operation='show_request',
|
||||
id=trans.security.encode_id(request.id)) )
|
||||
action='list',
|
||||
operation='show_request',
|
||||
id=trans.security.encode_id(request.id)) )
|
||||
elif params.get('edit_samples_button', False) == 'Edit samples':
|
||||
edit_mode = 'True'
|
||||
return trans.fill_template( '/requests/show_request.mako',
|
||||
@@ -700,10 +770,15 @@ class Requests( BaseController ):
|
||||
if not request:
|
||||
request = trans.app.model.Request(name, desc, request_type,
|
||||
trans.user, form_values,
|
||||
library=library, folder=folder,
|
||||
state=trans.app.model.Request.states.UNSUBMITTED)
|
||||
library=library, folder=folder)
|
||||
trans.sa_session.add( request )
|
||||
trans.sa_session.flush()
|
||||
trans.sa_session.refresh( request )
|
||||
# create an event with state 'New' for this new request
|
||||
comments = "Request created."
|
||||
event = trans.app.model.RequestEvent(request, request.states.NEW, comments)
|
||||
trans.sa_session.add( event )
|
||||
trans.sa_session.flush()
|
||||
else:
|
||||
request.name = name
|
||||
request.desc = desc
|
||||
@@ -712,7 +787,6 @@ class Requests( BaseController ):
|
||||
request.values = form_values
|
||||
request.library = library
|
||||
request.folder = folder
|
||||
request.state = trans.app.model.Request.states.UNSUBMITTED
|
||||
trans.sa_session.add( request )
|
||||
trans.sa_session.flush()
|
||||
return request
|
||||
@@ -731,7 +805,7 @@ class Requests( BaseController ):
|
||||
message="Invalid request ID",
|
||||
**kwd) )
|
||||
if params.get('show', False) == 'True':
|
||||
return self.__edit_request(trans, **kwd)
|
||||
return self.__edit_request(trans, id=trans.security.encode_id(request.id), **kwd)
|
||||
elif params.get('save_changes_request_button', False) == 'Save changes' \
|
||||
or params.get('edit_samples_button', False) == 'Edit samples':
|
||||
request_type = trans.sa_session.query( trans.app.model.RequestType ).get( int( params.select_request_type ) )
|
||||
@@ -760,7 +834,7 @@ class Requests( BaseController ):
|
||||
messagetype='done',
|
||||
**new_kwd) )
|
||||
elif params.get('refresh', False) == 'true':
|
||||
return self.__edit_request(trans, **kwd)
|
||||
return self.__edit_request(trans, id=trans.security.encode_id(request.id), **kwd)
|
||||
|
||||
def __edit_request(self, trans, **kwd):
|
||||
try:
|
||||
@@ -819,7 +893,7 @@ class Requests( BaseController ):
|
||||
message=msg,
|
||||
**kwd) )
|
||||
# a request cannot be deleted once its submitted
|
||||
if not request.unsubmitted():
|
||||
if not request.new():
|
||||
delete_failed.append(request.name)
|
||||
else:
|
||||
request.deleted = True
|
||||
@@ -870,21 +944,22 @@ class Requests( BaseController ):
|
||||
msg = self.__validate(trans, request)
|
||||
if msg:
|
||||
return trans.response.send_redirect( web.url_for( controller='requests',
|
||||
action='edit',
|
||||
action='list',
|
||||
operation='edit',
|
||||
messagetype = 'error',
|
||||
msg=msg,
|
||||
request_id=request.id,
|
||||
show='True') )
|
||||
id=trans.security.encode_id(request.id) ))
|
||||
# change the request state to 'Submitted'
|
||||
comments = "Request moved to 'Submitted' state."
|
||||
event = trans.app.model.RequestEvent(request, request.states.SUBMITTED, comments)
|
||||
trans.sa_session.add( event )
|
||||
trans.sa_session.flush()
|
||||
# get the new state
|
||||
new_state = request.type.states[0]
|
||||
for s in request.samples:
|
||||
event = trans.app.model.SampleEvent(s, new_state, 'Samples submitted to the system')
|
||||
trans.sa_session.add( event )
|
||||
trans.sa_session.flush()
|
||||
# change request's submitted field
|
||||
request.state = request.states.SUBMITTED
|
||||
trans.sa_session.add( request )
|
||||
trans.sa_session.flush()
|
||||
return trans.response.send_redirect( web.url_for( controller='requests',
|
||||
action='list',
|
||||
id=trans.security.encode_id(request.id),
|
||||
|
||||
@@ -8,6 +8,8 @@ import logging, tempfile, zipfile, tarfile, os, sys
|
||||
from galaxy.web.form_builder import *
|
||||
from datetime import datetime, timedelta
|
||||
from galaxy.web.controllers.forms import get_all_forms
|
||||
from sqlalchemy.sql.expression import func, and_
|
||||
from sqlalchemy.sql import select
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
@@ -31,27 +33,58 @@ class RequestsGrid( grids.Grid ):
|
||||
return request.type.name
|
||||
class LastUpdateColumn( grids.TextColumn ):
|
||||
def get_value(self, trans, grid, request):
|
||||
return request.update_time
|
||||
delta = datetime.utcnow() - request.update_time
|
||||
if delta > timedelta( minutes=60 ):
|
||||
last_update = '%s hours' % int( delta.seconds / 60 / 60 )
|
||||
else:
|
||||
last_update = '%s minutes' % int( delta.seconds / 60 )
|
||||
return last_update
|
||||
class StateColumn( grids.GridColumn ):
|
||||
def __init__( self, col_name, key, model_class, event_class, filterable, link ):
|
||||
grids.GridColumn.__init__(self, col_name, key=key, model_class=model_class, filterable=filterable, link=link)
|
||||
self.event_class = event_class
|
||||
def get_value(self, trans, grid, request):
|
||||
if request.state() == request.states.REJECTED:
|
||||
return '<div class="count-box state-color-error">%s</div>' % request.state()
|
||||
elif request.state() == request.states.NEW:
|
||||
return '<div class="count-box state-color-queued">%s</div>' % request.state()
|
||||
elif request.state() == request.states.SUBMITTED:
|
||||
return '<div class="count-box state-color-running">%s</div>' % request.state()
|
||||
elif request.state() == request.states.COMPLETE:
|
||||
return '<div class="count-box state-color-ok">%s</div>' % request.state()
|
||||
return request.state()
|
||||
def filter( self, db_session, query, column_filter ):
|
||||
""" Modify query to filter request by state. """
|
||||
if column_filter == "All":
|
||||
return query
|
||||
if column_filter:
|
||||
query = query.filter( model.Request.state == column_filter )
|
||||
return query
|
||||
# select r.id, r.name, re.id, re.state
|
||||
# from request as r, request_event as re
|
||||
# where re.request_id=r.id and re.state='Complete' and re.create_time in
|
||||
# (select MAX( create_time)
|
||||
# from request_event
|
||||
# group by request_id)
|
||||
q = query.join(self.event_class.table)\
|
||||
.filter( self.model_class.table.c.id==self.event_class.table.c.request_id )\
|
||||
.filter( self.event_class.table.c.state==column_filter )\
|
||||
.filter( self.event_class.table.c.id.in_(select(columns=[func.max(self.event_class.table.c.id)],
|
||||
from_obj=self.event_class.table,
|
||||
group_by=self.event_class.table.c.request_id)))
|
||||
#print column_filter, q
|
||||
return q
|
||||
def get_accepted_filters( self ):
|
||||
""" Returns a list of accepted filters for this column. """
|
||||
accepted_filter_labels_and_vals = [ model.Request.states.UNSUBMITTED,
|
||||
model.Request.states.SUBMITTED,
|
||||
model.Request.states.COMPLETE,
|
||||
"All"]
|
||||
accepted_filters = []
|
||||
for val in accepted_filter_labels_and_vals:
|
||||
label = val.lower()
|
||||
args = { self.key: val }
|
||||
accepted_filters.append( grids.GridColumnFilter( label, args) )
|
||||
return accepted_filters
|
||||
""" Returns a list of accepted filters for this column. """
|
||||
accepted_filter_labels_and_vals = [ model.Request.states.NEW,
|
||||
model.Request.states.REJECTED,
|
||||
model.Request.states.SUBMITTED,
|
||||
model.Request.states.COMPLETE,
|
||||
"All"]
|
||||
accepted_filters = []
|
||||
for val in accepted_filter_labels_and_vals:
|
||||
label = val.lower()
|
||||
args = { self.key: val }
|
||||
accepted_filters.append( grids.GridColumnFilter( label, args) )
|
||||
return accepted_filters
|
||||
class UserColumn( grids.TextColumn ):
|
||||
def get_value(self, trans, grid, request):
|
||||
return request.user.email
|
||||
@@ -86,20 +119,25 @@ class RequestsGrid( grids.Grid ):
|
||||
filterable="advanced" ),
|
||||
SamplesColumn( "Sample(s)",
|
||||
link=( lambda item: iff( item.deleted, None, dict( operation="show_request", id=item.id ) ) ), ),
|
||||
TypeColumn( "Type" ),
|
||||
TypeColumn( "Type",
|
||||
link=( lambda item: iff( item.deleted, None, dict( operation="view_type", id=item.type.id ) ) ), ),
|
||||
LastUpdateColumn( "Last update",
|
||||
format=time_ago ),
|
||||
StateColumn( "State",
|
||||
key='state',
|
||||
filterable="advanced"),
|
||||
UserColumn( "User",
|
||||
key='user.email',
|
||||
model_class=model.Request,
|
||||
filterable="advanced" ),
|
||||
DeletedColumn( "Deleted",
|
||||
key="deleted",
|
||||
visible=True,
|
||||
filterable="advanced" )
|
||||
visible=False,
|
||||
filterable="advanced" ),
|
||||
StateColumn( "State",
|
||||
model_class=model.Request,
|
||||
event_class=model.RequestEvent,
|
||||
key='state',
|
||||
filterable="advanced",
|
||||
link=( lambda item: iff( item.deleted, None, dict( operation="events", id=item.id ) ) ),
|
||||
),
|
||||
UserColumn( "User",
|
||||
#key='user.email',
|
||||
model_class=model.Request)
|
||||
|
||||
]
|
||||
columns.append( grids.MulticolFilterColumn( "Search",
|
||||
cols_to_filter=[ columns[0], columns[1], columns[6] ],
|
||||
@@ -107,10 +145,11 @@ class RequestsGrid( grids.Grid ):
|
||||
visible=False,
|
||||
filterable="standard" ) )
|
||||
operations = [
|
||||
grids.GridOperation( "Submit", allow_multiple=False, condition=( lambda item: not item.deleted and item.unsubmitted() and item.samples ) ),
|
||||
grids.GridOperation( "Submit", allow_multiple=False, condition=( lambda item: not item.deleted and item.unsubmitted() and item.samples ),
|
||||
confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." ),
|
||||
grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ),
|
||||
grids.GridOperation( "Reject", allow_multiple=False, condition=( lambda item: not item.deleted and item.submitted() ) ),
|
||||
grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted and item.unsubmitted() ) ),
|
||||
grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted and item.new() ) ),
|
||||
grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ),
|
||||
]
|
||||
global_actions = [
|
||||
@@ -227,6 +266,11 @@ class Requests( BaseController ):
|
||||
return self.__edit_request( trans, **kwd )
|
||||
elif operation == "reject":
|
||||
return self.__reject_request( trans, **kwd )
|
||||
elif operation == "events":
|
||||
return self.__request_events( trans, **kwd )
|
||||
elif operation == "view_type":
|
||||
return self.__view_request_type( trans, **kwd )
|
||||
|
||||
# Render the grid view
|
||||
return self.request_grid( trans, **kwd )
|
||||
def __show_request(self, trans, **kwd):
|
||||
@@ -253,7 +297,52 @@ class Requests( BaseController ):
|
||||
sample_copy=self.__copy_sample(current_samples),
|
||||
details='hide', edit_mode='False',
|
||||
msg=msg, messagetype=messagetype )
|
||||
|
||||
@web.expose
|
||||
@web.require_admin
|
||||
def edit(self, trans, **kwd):
|
||||
params = util.Params( kwd )
|
||||
msg = util.restore_text( params.get( 'msg', '' ) )
|
||||
messagetype = params.get( 'messagetype', 'done' )
|
||||
try:
|
||||
request = trans.sa_session.query( trans.app.model.Request ).get( int( params.get( 'request_id', None ) ) )
|
||||
except:
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='list',
|
||||
status='error',
|
||||
message="Invalid request ID",
|
||||
**kwd) )
|
||||
if params.get('show', False) == 'True':
|
||||
return self.__edit_request(trans, id=trans.security.encode_id(request.id), **kwd)
|
||||
elif params.get('save_changes_request_button', False) == 'Save changes' \
|
||||
or params.get('edit_samples_button', False) == 'Edit samples':
|
||||
request_type = trans.sa_session.query( trans.app.model.RequestType ).get( int( params.select_request_type ) )
|
||||
if not util.restore_text(params.get('name', '')):
|
||||
msg = 'Please enter the <b>Name</b> of the request'
|
||||
kwd['messagetype'] = 'error'
|
||||
kwd['msg'] = msg
|
||||
kwd['show'] = 'True'
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='edit',
|
||||
**kwd) )
|
||||
request = self.__save_request(trans, request, **kwd)
|
||||
msg = 'The changes made to the request named %s has been saved' % request.name
|
||||
if params.get('save_changes_request_button', False) == 'Save changes':
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='list',
|
||||
message=msg ,
|
||||
status='done') )
|
||||
elif params.get('edit_samples_button', False) == 'Edit samples':
|
||||
new_kwd = {}
|
||||
new_kwd['request_id'] = request.id
|
||||
new_kwd['edit_samples_button'] = 'Edit samples'
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='show_request',
|
||||
msg=msg ,
|
||||
messagetype='done',
|
||||
**new_kwd) )
|
||||
elif params.get('refresh', False) == 'true':
|
||||
return self.__edit_request(trans, id=trans.security.encode_id(request.id), **kwd)
|
||||
|
||||
def __edit_request(self, trans, **kwd):
|
||||
try:
|
||||
request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id(kwd['id']) )
|
||||
@@ -311,7 +400,7 @@ class Requests( BaseController ):
|
||||
message=msg,
|
||||
**kwd) )
|
||||
# a request cannot be deleted once its submitted
|
||||
if not request.unsubmitted():
|
||||
if not request.new():
|
||||
delete_failed.append(request.name)
|
||||
else:
|
||||
request.deleted = True
|
||||
@@ -362,18 +451,24 @@ class Requests( BaseController ):
|
||||
msg = self.__validate(trans, request)
|
||||
if msg:
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='edit',
|
||||
action='list',
|
||||
operation='edit',
|
||||
messagetype = 'error',
|
||||
msg=msg,
|
||||
request_id=request.id,
|
||||
show='True') )
|
||||
# get the new state
|
||||
id=trans.security.encode_id(request.id) ) )
|
||||
# change the request state to 'Submitted'
|
||||
if request.user.email is not trans.user:
|
||||
comments = "Request moved to 'Submitted' state by admin (%s) on behalf of %s." % (trans.user.email, request.user.email)
|
||||
else:
|
||||
comments = ""
|
||||
event = trans.app.model.RequestEvent(request, request.states.SUBMITTED, comments)
|
||||
trans.sa_session.add( event )
|
||||
trans.sa_session.flush()
|
||||
# change the state of each of the samples of thus request
|
||||
new_state = request.type.states[0]
|
||||
for s in request.samples:
|
||||
event = trans.app.model.SampleEvent(s, new_state, 'Samples submitted to the system')
|
||||
trans.sa_session.add( event )
|
||||
# change request's submitted field
|
||||
request.state = request.states.SUBMITTED
|
||||
trans.sa_session.add( request )
|
||||
trans.sa_session.flush()
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
@@ -393,15 +488,64 @@ class Requests( BaseController ):
|
||||
status='error',
|
||||
message=msg,
|
||||
**kwd) )
|
||||
# change request's submitted field
|
||||
request.state = request.states.UNSUBMITTED
|
||||
trans.sa_session.add( request )
|
||||
return trans.fill_template( '/admin/requests/reject.mako',
|
||||
request=request)
|
||||
@web.expose
|
||||
@web.require_admin
|
||||
def reject(self, trans, **kwd):
|
||||
params = util.Params( kwd )
|
||||
if params.get('cancel_reject_button', False):
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='list',
|
||||
operation='show_request',
|
||||
id=kwd['id']))
|
||||
try:
|
||||
request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id(kwd['id']) )
|
||||
except:
|
||||
msg = "Invalid request ID"
|
||||
log.warn( msg )
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='list',
|
||||
status='error',
|
||||
message=msg,
|
||||
**kwd) )
|
||||
# validate
|
||||
if not params.get('comment', ''):
|
||||
return trans.fill_template( '/admin/requests/reject.mako',
|
||||
request=request, messagetype='error',
|
||||
msg='A comment is required for rejecting a request.')
|
||||
# create an event with state 'Rejected' for this request
|
||||
comments = util.restore_text( params.comment )
|
||||
event = trans.app.model.RequestEvent(request, request.states.REJECTED, comments)
|
||||
trans.sa_session.add( event )
|
||||
trans.sa_session.flush()
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='list',
|
||||
status='done',
|
||||
message='The request <b>%s</b> is now unsubmitted.' % request.name
|
||||
) )
|
||||
message='Request <b>%s</b> has been rejected.' % request.name) )
|
||||
|
||||
def __request_events(self, trans, **kwd):
|
||||
try:
|
||||
request = trans.sa_session.query( trans.app.model.Request ).get( trans.security.decode_id(kwd['id']) )
|
||||
except:
|
||||
msg = "Invalid request ID"
|
||||
log.warn( msg )
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='list',
|
||||
status='error',
|
||||
message=msg,
|
||||
**kwd) )
|
||||
events_list = []
|
||||
all_events = request.events
|
||||
for event in all_events:
|
||||
delta = datetime.utcnow() - event.update_time
|
||||
if delta > timedelta( minutes=60 ):
|
||||
last_update = '%s hours' % int( delta.seconds / 60 / 60 )
|
||||
else:
|
||||
last_update = '%s minutes' % int( delta.seconds / 60 )
|
||||
events_list.append((event.state, last_update, event.comment))
|
||||
return trans.fill_template( '/admin/requests/events.mako',
|
||||
events_list=events_list, request=request)
|
||||
#
|
||||
#---- Request Creation ----------------------------------------------------------
|
||||
#
|
||||
@@ -732,9 +876,18 @@ class Requests( BaseController ):
|
||||
if not request:
|
||||
request = trans.app.model.Request(name, desc, request_type,
|
||||
user, form_values,
|
||||
library=library, folder=folder,
|
||||
state=trans.app.model.Request.states.UNSUBMITTED)
|
||||
library=library, folder=folder)
|
||||
trans.sa_session.add( request )
|
||||
trans.sa_session.flush()
|
||||
trans.sa_session.refresh( request )
|
||||
# create an event with state 'New' for this new request
|
||||
if request.user.email is not trans.user:
|
||||
comments = "Request created by admin (%s) on behalf of %s." % (trans.user.email, request.user.email)
|
||||
else:
|
||||
comments = "Request created."
|
||||
event = trans.app.model.RequestEvent(request, request.states.NEW, comments)
|
||||
trans.sa_session.add( event )
|
||||
trans.sa_session.flush()
|
||||
else:
|
||||
request.name = name
|
||||
request.desc = desc
|
||||
@@ -744,58 +897,14 @@ class Requests( BaseController ):
|
||||
request.library = library
|
||||
request.folder = folder
|
||||
trans.sa_session.add( request )
|
||||
trans.sa_session.flush()
|
||||
trans.sa_session.flush()
|
||||
|
||||
return request
|
||||
|
||||
|
||||
#
|
||||
#---- Request Editing ----------------------------------------------------------
|
||||
#---- Request Page ----------------------------------------------------------
|
||||
#
|
||||
@web.expose
|
||||
@web.require_admin
|
||||
def edit(self, trans, **kwd):
|
||||
params = util.Params( kwd )
|
||||
msg = util.restore_text( params.get( 'msg', '' ) )
|
||||
messagetype = params.get( 'messagetype', 'done' )
|
||||
try:
|
||||
request = trans.sa_session.query( trans.app.model.Request ).get( int( params.get( 'request_id', None ) ) )
|
||||
except:
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='list',
|
||||
status='error',
|
||||
message="Invalid request ID",
|
||||
**kwd) )
|
||||
if params.get('show', False) == 'True':
|
||||
return self.__edit_request(trans, request.id, **kwd)
|
||||
elif params.get('save_changes_request_button', False) == 'Save changes' \
|
||||
or params.get('edit_samples_button', False) == 'Edit samples':
|
||||
request_type = trans.sa_session.query( trans.app.model.RequestType ).get( int( params.select_request_type ) )
|
||||
if not util.restore_text(params.get('name', '')):
|
||||
msg = 'Please enter the <b>Name</b> of the request'
|
||||
kwd['messagetype'] = 'error'
|
||||
kwd['msg'] = msg
|
||||
kwd['show'] = 'True'
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='edit',
|
||||
**kwd) )
|
||||
request = self.__save_request(trans, request, **kwd)
|
||||
msg = 'The changes made to the request named %s has been saved' % request.name
|
||||
if params.get('save_changes_request_button', False) == 'Save changes':
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='list',
|
||||
message=msg ,
|
||||
status='done') )
|
||||
elif params.get('edit_samples_button', False) == 'Edit samples':
|
||||
new_kwd = {}
|
||||
new_kwd['request_id'] = request.id
|
||||
new_kwd['edit_samples_button'] = 'Edit samples'
|
||||
return trans.response.send_redirect( web.url_for( controller='requests_admin',
|
||||
action='show_request',
|
||||
msg=msg ,
|
||||
messagetype='done',
|
||||
**new_kwd) )
|
||||
elif params.get('refresh', False) == 'true':
|
||||
return self.__edit_request(trans, request.id, **kwd)
|
||||
def __update_samples(self, request, **kwd):
|
||||
'''
|
||||
This method retrieves all the user entered sample information and
|
||||
@@ -1013,7 +1122,7 @@ class Requests( BaseController ):
|
||||
value=request.type.name,
|
||||
helptext=''))
|
||||
request_details.append(dict(label='State',
|
||||
value=request.state,
|
||||
value=request.state(),
|
||||
helptext=''))
|
||||
request_details.append(dict(label='Date created',
|
||||
value=request.create_time,
|
||||
@@ -1167,11 +1276,13 @@ class Requests( BaseController ):
|
||||
if s.current_state().id != request.type.states[-1].id:
|
||||
complete = False
|
||||
if complete:
|
||||
request.state = request.states.COMPLETE
|
||||
else:
|
||||
request.state = request.states.SUBMITTED
|
||||
trans.sa_session.add( request )
|
||||
trans.sa_session.flush()
|
||||
# change the request state to 'Complete'
|
||||
comments = "All samples of this request are in the last sample state (%s)." % request.type.states[-1].name
|
||||
event = trans.app.model.RequestEvent(request, request.states.COMPLETE, comments)
|
||||
trans.sa_session.add( event )
|
||||
trans.sa_session.flush()
|
||||
# trans.sa_session.add( request )
|
||||
# trans.sa_session.flush()
|
||||
def change_state(self, trans, sample):
|
||||
possible_states = sample.request.type.states
|
||||
curr_state = sample.current_state()
|
||||
|
||||
@@ -402,7 +402,7 @@ class MulticolFilterColumn( TextColumn ):
|
||||
return query.filter( complete_filter )
|
||||
|
||||
class GridOperation( object ):
|
||||
def __init__( self, label, key=None, condition=None, allow_multiple=True, allow_popup=True, target=None, url_args=None, async_compatible=False ):
|
||||
def __init__( self, label, key=None, condition=None, allow_multiple=True, allow_popup=True, target=None, url_args=None, async_compatible=False, confirm=None ):
|
||||
self.label = label
|
||||
self.key = key
|
||||
self.allow_multiple = allow_multiple
|
||||
@@ -411,6 +411,8 @@ class GridOperation( object ):
|
||||
self.target = target
|
||||
self.url_args = url_args
|
||||
self.async_compatible = async_compatible
|
||||
# if 'confirm' is set, then ask before completing the operation
|
||||
self.confirm = confirm
|
||||
def get_url_args( self, item ):
|
||||
if self.url_args:
|
||||
temp = dict( self.url_args )
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
<h2>History of Sequencing Request "${request.name}"</h2>
|
||||
<ul class="manage-table-actions">
|
||||
<li>
|
||||
<a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='show_request', id=trans.security.encode_id(request.id) )}">
|
||||
<span>Browse this request</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>User: ${request.user.email}</h3>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<table class="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>State</th>
|
||||
<th>Last Update</th>
|
||||
<th>Comments</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%for state, updated, comments in events_list:
|
||||
<tr class="libraryRow libraryOrFolderRow" id="libraryRow">
|
||||
<td><b><a>${state}</a></b></td>
|
||||
<td><a>${updated}</a></td>
|
||||
<td><a>${comments}</a></td>
|
||||
</tr>
|
||||
%endfor
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,42 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<h2>Reject Sequencing Request "${request.name}"</h2>
|
||||
<ul class="manage-table-actions">
|
||||
<li>
|
||||
<a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='show_request', id=trans.security.encode_id(request.id) )}">
|
||||
<span>Browse this request</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='events', id=trans.security.encode_id(request.id) )}">
|
||||
<span>Events</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>User: ${request.user.email}</h3>
|
||||
|
||||
|
||||
|
||||
<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" >
|
||||
<div class="form-row">
|
||||
Rejecting this request will move the request state to <b>Rejected</b>.
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Comments</label>
|
||||
<textarea name="comment" rows="5" cols="40"></textarea>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
Required
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="submit" name="reject_button" value="Reject"/>
|
||||
<input type="submit" name="cancel_reject_button" value="Cancel"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -5,11 +5,16 @@
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if request.rejected():
|
||||
${render_msg( "Reason for rejection: "+request.last_comment(), "warning" )}
|
||||
%endif
|
||||
|
||||
<div class="grid-header">
|
||||
<h2>Sequencing Request "${request.name}"</h2>
|
||||
</div>
|
||||
|
||||
<ul class="manage-table-actions">
|
||||
|
||||
%if request.unsubmitted() and request.samples:
|
||||
<li>
|
||||
<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_admin', action='list', operation='Submit', id=trans.security.encode_id(request.id) )}">
|
||||
@@ -28,6 +33,10 @@
|
||||
<span>Bar codes</span></a>
|
||||
</li>
|
||||
%endif
|
||||
<li>
|
||||
<a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='events', id=trans.security.encode_id(request.id) )}">
|
||||
<span>History</span></a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -82,7 +91,7 @@
|
||||
%if grid_index == 0:
|
||||
<td>${sample.bar_code}</td>
|
||||
<td>
|
||||
%if sample.request.unsubmitted():
|
||||
%if sample.request.new():
|
||||
Unsubmitted
|
||||
%else:
|
||||
<a href="${h.url_for( controller='requests_admin', action='show_events', sample_id=sample.id)}">${sample.current_state().name}</a>
|
||||
@@ -117,6 +126,8 @@
|
||||
%else:
|
||||
<i>None</i>
|
||||
%endif
|
||||
%elif rd['label'] == 'State':
|
||||
<a href="${h.url_for( controller='requests_admin', action='list', operation='events', id=trans.security.encode_id(request.id) )}">${rd['value']}</a>
|
||||
%else:
|
||||
${rd['value']}
|
||||
%endif
|
||||
|
||||
@@ -860,7 +860,11 @@ ${self.render_grid_table()}
|
||||
if operation.target:
|
||||
target = "target='" + operation.target + "'"
|
||||
%>
|
||||
<a class="action-button" ${target} href="${ url( **operation.get_url_args( item ) ) }">${operation.label}</a>
|
||||
%if operation.confirm:
|
||||
<a class="action-button" ${target} confirm="${operation.confirm}" href="${ url( **operation.get_url_args( item ) ) }">${operation.label}</a>
|
||||
%else:
|
||||
<a class="action-button" ${target} href="${ url( **operation.get_url_args( item ) ) }">${operation.label}</a>
|
||||
%endif
|
||||
%endif
|
||||
%endfor
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
<h2>History of Sequencing Request "${request.name}"</h2>
|
||||
<ul class="manage-table-actions">
|
||||
<li>
|
||||
<a class="action-button" href="${h.url_for( controller='requests', action='list', operation='show_request', id=trans.security.encode_id(request.id) )}">
|
||||
<span>Browse this request</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="action-button" href="${h.url_for( controller='requests', action='list')}">
|
||||
<span>Browse all requests</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<table class="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>State</th>
|
||||
<th>Last Update</th>
|
||||
<th>Comments</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%for state, updated, comments in events_list:
|
||||
<tr class="libraryRow libraryOrFolderRow" id="libraryRow">
|
||||
<td><b><a>${state}</a></b></td>
|
||||
<td><a>${updated}</a></td>
|
||||
<td><a>${comments}</a></td>
|
||||
</tr>
|
||||
%endfor
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -6,6 +6,9 @@
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if request.rejected():
|
||||
${render_msg( "Reason for rejection: "+request.last_comment(), "warning" )}
|
||||
%endif
|
||||
|
||||
<div class="grid-header">
|
||||
<h2>Sequencing Request "${request.name}"</h2>
|
||||
@@ -18,6 +21,10 @@
|
||||
<span>Submit request</span></a>
|
||||
</li>
|
||||
%endif
|
||||
<li>
|
||||
<a class="action-button" href="${h.url_for( controller='requests', action='list', operation='events', id=trans.security.encode_id(request.id) )}">
|
||||
<span>History</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="action-button" href="${h.url_for( controller='requests', action='list')}">
|
||||
<span>Browse requests</span></a>
|
||||
@@ -73,7 +80,7 @@
|
||||
</td>
|
||||
%if grid_index == 0:
|
||||
<td>
|
||||
%if sample.request.unsubmitted():
|
||||
%if sample.request.new():
|
||||
Unsubmitted
|
||||
%else:
|
||||
<a href="${h.url_for( controller='requests', action='show_events', sample_id=sample.id)}">${sample.current_state().name}</a>
|
||||
@@ -105,9 +112,11 @@
|
||||
%if rd['label'] == 'Data library':
|
||||
%if rd['value']:
|
||||
<a href="${h.url_for( controller='library', action='browse_library', obj_id=request.library.id )}">${rd['value']}</a>
|
||||
%else:
|
||||
<i>None</i>
|
||||
%endif
|
||||
%elif rd['label'] == 'State':
|
||||
<a href="${h.url_for( controller='requests', action='list', operation='events', id=trans.security.encode_id(request.id) )}">${rd['value']}</a>
|
||||
%else:
|
||||
<i>None</i>
|
||||
%endif
|
||||
%else:
|
||||
${rd['value']}
|
||||
%endif
|
||||
|
||||
@@ -21,24 +21,25 @@
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
|
||||
<table class="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>State</th>
|
||||
<th>Description</th>
|
||||
<th>Updated</th>
|
||||
<th>Comments</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%for state, desc, updated, comments in events_list:
|
||||
<tr class="libraryRow libraryOrFolderRow" id="libraryRow">
|
||||
<td><b><a>${state}</a></b></td>
|
||||
<td><a>${desc}</a></td>
|
||||
<td><a>${updated}</a></td>
|
||||
<td><a>${comments}</a></td>
|
||||
</tr>
|
||||
%endfor
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="toolForm">
|
||||
<table class="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>State</th>
|
||||
<th>Description</th>
|
||||
<th>Updated</th>
|
||||
<th>Comments</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%for state, desc, updated, comments in events_list:
|
||||
<tr class="libraryRow libraryOrFolderRow" id="libraryRow">
|
||||
<td><b><a>${state}</a></b></td>
|
||||
<td><a>${desc}</a></td>
|
||||
<td><a>${updated}</a></td>
|
||||
<td><a>${comments}</a></td>
|
||||
</tr>
|
||||
%endfor
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -1298,10 +1298,15 @@ class TwillTestCase( unittest.TestCase ):
|
||||
self.home()
|
||||
self.visit_url( "%s/requests_admin/list?operation=Submit&id=%s" % ( self.url, self.security.encode_id( request_id ) ))
|
||||
self.check_page_for_string( 'The request <b>%s</b> has been submitted.' % request_name )
|
||||
def reject_request( self, request_id, request_name ):
|
||||
def reject_request( self, request_id, request_name, comment ):
|
||||
self.home()
|
||||
self.visit_url( "%s/requests_admin/list?operation=Reject&id=%s" % ( self.url, self.security.encode_id( request_id ) ))
|
||||
self.check_page_for_string( 'The request <b>%s</b> is now unsubmitted.' % request_name )
|
||||
self.check_page_for_string( 'Reject Sequencing Request "%s"' % request_name )
|
||||
tc.fv( "1", "comment", comment )
|
||||
tc.submit( "reject_button" )
|
||||
self.check_page_for_string( 'Request <b>%s</b> has been rejected.' % request_name )
|
||||
self.visit_url( "%s/requests/list?sort=-create_time&operation=show_request&id=%s" % ( self.url, self.security.encode_id( request_id ) ))
|
||||
self.check_page_for_string( comment )
|
||||
def add_bar_codes( self, request_id, request_name, bar_codes ):
|
||||
self.home()
|
||||
self.visit_url( "%s/requests_admin/bar_codes?request_id=%i" % (self.url, request_id) )
|
||||
|
||||
@@ -194,9 +194,9 @@ class TestFormsAndRequests( TwillTestCase ):
|
||||
.filter( and_( galaxy.model.Request.table.c.name==request_name,
|
||||
galaxy.model.Request.table.c.deleted==False ) ) \
|
||||
.first()
|
||||
# check if the request's state is now set to 'unsubmitted'
|
||||
assert request_one.state is not request_one.states.UNSUBMITTED, "The state of the request '%s' should be set to '%s'" \
|
||||
% ( request_one.name, request_one.states.UNSUBMITTED )
|
||||
# check if the request's state is now set to 'new'
|
||||
assert request_one.state is not request_one.states.NEW, "The state of the request '%s' should be set to '%s'" \
|
||||
% ( request_one.name, request_one.states.NEW )
|
||||
# sample fields
|
||||
samples = [ ( 'Sample One', [ 'S1 Field 0 Value' ] ),
|
||||
( 'Sample Two', [ 'S2 Field 0 Value' ] ) ]
|
||||
@@ -207,8 +207,8 @@ class TestFormsAndRequests( TwillTestCase ):
|
||||
self.edit_request(request_one.id, request_one.name, request_one.name+' (Renamed)',
|
||||
request_one.desc+' (Re-described)', library_one.id, folder_one.id, fields)
|
||||
sa_session.refresh( request_one )
|
||||
# check if the request is showing in the 'unsubmitted' filter
|
||||
self.check_request_grid(state='Unsubmitted', request_name=request_one.name)
|
||||
# check if the request is showing in the 'new' filter
|
||||
self.check_request_grid(state='New', request_name=request_one.name)
|
||||
# submit the request
|
||||
self.submit_request( request_one.id, request_one.name )
|
||||
sa_session.refresh( request_one )
|
||||
@@ -257,11 +257,11 @@ class TestFormsAndRequests( TwillTestCase ):
|
||||
.filter( and_( galaxy.model.Request.table.c.name==request_name,
|
||||
galaxy.model.Request.table.c.deleted==False ) ) \
|
||||
.first()
|
||||
# check if the request is showing in the 'unsubmitted' filter
|
||||
self.check_request_admin_grid(state='Unsubmitted', request_name=request_two.name)
|
||||
# check if the request's state is now set to 'unsubmitted'
|
||||
assert request_two.state is not request_two.states.UNSUBMITTED, "The state of the request '%s' should be set to '%s'" \
|
||||
% ( request_two.name, request_two.states.UNSUBMITTED )
|
||||
# check if the request is showing in the 'new' filter
|
||||
self.check_request_admin_grid(state='New', request_name=request_two.name)
|
||||
# check if the request's state is now set to 'new'
|
||||
assert request_two.state is not request_two.states.NEW, "The state of the request '%s' should be set to '%s'" \
|
||||
% ( request_two.name, request_two.states.NEW )
|
||||
# sample fields
|
||||
samples = [ ( 'Sample One', [ 'S1 Field 0 Value' ] ),
|
||||
( 'Sample Two', [ 'S2 Field 0 Value' ] ) ]
|
||||
@@ -282,10 +282,10 @@ class TestFormsAndRequests( TwillTestCase ):
|
||||
'''Testing rejecting a request'''
|
||||
self.logout()
|
||||
self.login( email='test@bx.psu.edu' )
|
||||
self.reject_request( request_two.id, request_two.name )
|
||||
self.reject_request( request_two.id, request_two.name, "Rejection test comment" )
|
||||
sa_session.refresh( request_two )
|
||||
# check if the request is showing in the 'unsubmitted' filter
|
||||
self.check_request_admin_grid(state='Unsubmitted', request_name=request_two.name)
|
||||
# check if the request is showing in the 'rejected' filter
|
||||
self.check_request_admin_grid(state='Rejected', request_name=request_two.name)
|
||||
# check if the request's state is now set to 'submitted'
|
||||
assert request_two.state is not request_two.states.UNSUBMITTED, "The state of the request '%s' should be set to '%s'" \
|
||||
% ( request_two.name, request_two.states.UNSUBMITTED )
|
||||
assert request_two.state is not request_two.states.REJECTED, "The state of the request '%s' should be set to '%s'" \
|
||||
% ( request_two.name, request_two.states.REJECTED )
|
||||
|
||||
Reference in New Issue
Block a user