from galaxy.web.base.controller import *
from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy.model.orm import *
from galaxy import model, util
from galaxy.web.form_builder import *
from galaxy.web.controllers.requests_common import RequestsGrid, invalid_id_redirect
from amqplib import client_0_8 as amqp
import logging, os, pexpect, ConfigParser
log = logging.getLogger( __name__ )
class AdminRequestsGrid( RequestsGrid ):
class UserColumn( grids.TextColumn ):
def get_value( self, trans, grid, request ):
return request.user.email
# Grid definition
columns = [ col for col in RequestsGrid.columns ]
columns.append( UserColumn( "User",
model_class=model.User,
key='username' ) )
operations = [ operation for operation in RequestsGrid.operations ]
operations.append( grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ) )
operations.append( grids.GridOperation( "Reject", allow_multiple=False, condition=( lambda item: not item.deleted and item.is_submitted ) ) )
operations.append( grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted ) ) )
operations.append( grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ) )
operations.append( grids.GridOperation( "Purge",
allow_multiple=False,
confirm="This will permanently delete this sequencing request. Click OK to proceed.",
condition=( lambda item: item.deleted ) ) )
global_actions = [
grids.GridAction( "Create new request", dict( controller='requests_common',
action='create_request',
cntrller='requests_admin' ) )
]
class RequestTypeGrid( grids.Grid ):
# Custom column types
class NameColumn( grids.TextColumn ):
def get_value(self, trans, grid, request_type):
return request_type.name
class DescriptionColumn( grids.TextColumn ):
def get_value(self, trans, grid, request_type):
return request_type.desc
class RequestFormColumn( grids.TextColumn ):
def get_value(self, trans, grid, request_type):
return request_type.request_form.name
class SampleFormColumn( grids.TextColumn ):
def get_value(self, trans, grid, request_type):
return request_type.sample_form.name
# Grid definition
title = "Sequencer Configurations"
template = "admin/requests/grid.mako"
model_class = model.RequestType
default_sort_key = "-create_time"
num_rows_per_page = 50
preserve_state = True
use_paging = True
default_filter = dict( deleted="False" )
columns = [
NameColumn( "Name",
key="name",
link=( lambda item: iff( item.deleted, None, dict( operation="view", id=item.id ) ) ),
attach_popup=True,
filterable="advanced" ),
DescriptionColumn( "Description",
key='desc',
filterable="advanced" ),
RequestFormColumn( "Request Form",
link=( lambda item: iff( item.deleted, None, dict( operation="view_form_definition", id=item.request_form.id ) ) ) ),
SampleFormColumn( "Sample Form",
link=( lambda item: iff( item.deleted, None, dict( operation="view_form_definition", id=item.sample_form.id ) ) ) ),
grids.DeletedColumn( "Deleted",
key="deleted",
visible=False,
filterable="advanced" )
]
columns.append( grids.MulticolFilterColumn( "Search",
cols_to_filter=[ columns[0], columns[1] ],
key="free-text-search",
visible=False,
filterable="standard" ) )
operations = [
grids.GridOperation( "Permissions", allow_multiple=False, condition=( lambda item: not item.deleted ) ),
grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted ) ),
grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ),
]
global_actions = [
grids.GridAction( "Create new sequencer configuration", dict( controller='requests_admin', action='create_request_type' ) )
]
class DataTransferGrid( grids.Grid ):
# Custom column types
class NameColumn( grids.TextColumn ):
def get_value( self, trans, grid, sample_dataset ):
return sample_dataset.name
class SizeColumn( grids.TextColumn ):
def get_value( self, trans, grid, sample_dataset ):
return sample_dataset.size
class StatusColumn( grids.TextColumn ):
def get_value( self, trans, grid, sample_dataset ):
return sample_dataset.status
# Grid definition
title = "Sample Datasets"
template = "admin/requests/grid.mako"
model_class = model.SampleDataset
default_sort_key = "-create_time"
num_rows_per_page = 50
preserve_state = True
use_paging = True
columns = [
NameColumn( "Name",
link=( lambda item: dict( operation="view", id=item.id ) ),
attach_popup=True,
filterable="advanced" ),
SizeColumn( "Size",
filterable="advanced" ),
grids.GridColumn( "Last Updated",
key="update_time",
format=time_ago ),
StatusColumn( "Status",
filterable="advanced" ),
]
columns.append( grids.MulticolFilterColumn( "Search",
cols_to_filter=[ columns[0] ],
key="free-text-search",
visible=False,
filterable="standard" ) )
operations = [
grids.GridOperation( "Start Transfer", allow_multiple=True, condition=( lambda item: item.status in [ model.Sample.transfer_status.NOT_STARTED ] ) ),
grids.GridOperation( "Rename", allow_multiple=True, allow_popup=False, condition=( lambda item: item.status in [ model.Sample.transfer_status.NOT_STARTED ] ) ),
grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.status in [ model.Sample.transfer_status.NOT_STARTED ] ) ),
]
def apply_query_filter( self, trans, query, **kwd ):
sample_id = kwd.get( 'sample_id', None )
if not sample_id:
return query
return query.filter_by( sample_id=trans.security.decode_id( sample_id ) )
class RequestsAdmin( BaseController, UsesFormDefinitionWidgets ):
request_grid = AdminRequestsGrid()
requesttype_grid = RequestTypeGrid()
datatx_grid = DataTransferGrid()
@web.expose
@web.require_admin
def index( self, trans ):
return trans.fill_template( "/admin/requests/index.mako" )
@web.expose
@web.require_admin
def browse_requests( self, trans, **kwd ):
if 'operation' in kwd:
operation = kwd['operation'].lower()
if operation == "edit":
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='edit_basic_request_info',
cntrller='requests_admin',
**kwd ) )
if operation == "manage_request":
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='manage_request',
cntrller='requests_admin',
**kwd ) )
if operation == "request_events":
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='request_events',
cntrller='requests_admin',
**kwd ) )
if operation == "reject":
return self.reject_request( trans, **kwd )
if operation == "view_type":
return self.view_request_type( trans, **kwd )
if operation == "delete":
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='delete_request',
cntrller='requests_admin',
**kwd ) )
if operation == "undelete":
return trans.response.send_redirect( web.url_for( controller='requests_common',
action='undelete_request',
cntrller='requests_admin',
**kwd ) )
# Render the list view
return self.request_grid( trans, **kwd )
@web.expose
@web.require_admin
def reject( 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',
cntrller='requests_admin',
id=request_id ) )
try:
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
return invalid_id_redirect( trans, 'requests_admin', request_id )
# Validate
comment = util.restore_text( params.get( 'comment', '' ) )
if not comment:
status='error'
message='A reason for rejecting the request is required.'
return trans.fill_template( '/admin/requests/reject.mako',
cntrller='requests_admin',
request=request,
status=status,
message=message )
# Create an event with state 'Rejected' for this request
event = trans.model.RequestEvent( request, request.states.REJECTED, comment )
trans.sa_session.add( event )
trans.sa_session.flush()
message='Request (%s) has been rejected.' % request.name
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='browse_requests',
status=status,
message=message,
**kwd ) )
# Data transfer from sequencer
@web.expose
@web.require_admin
def manage_datasets( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
if 'operation' in kwd:
operation = kwd[ 'operation' ].lower()
sample_dataset_id = params.get( 'id', None )
if not sample_dataset_id:
return invalid_id_redirect( trans, 'requests_admin', sample_dataset_id )
id_list = util.listify( sample_dataset_id )
selected_sample_datasets = []
for sample_dataset_id in id_list:
try:
selected_sample_datasets.append( trans.sa_session.query( trans.model.SampleDataset ).get( trans.security.decode_id( sample_dataset_id ) ) )
except:
return invalid_id_redirect( trans, 'requests_admin', sample_dataset_id )
if operation == "view":
return trans.fill_template( '/admin/requests/dataset.mako',
sample_dataset=selected_sample_datasets[0] )
elif operation == "delete":
not_deleted = []
for sample_dataset in selected_sample_datasets:
# Make sure the dataset has been transferred before deleting it.
if sample_dataset in sample_dataset.sample.untransferred_dataset_files:
# save the sample to which these datasets belong to
sample = sample_dataset.sample
trans.sa_session.delete( sample_dataset )
trans.sa_session.flush()
else:
not_deleted.append( sample_dataset.name )
message = '%i datasets have been deleted.' % ( len( id_list ) - len( not_deleted ) )
if not_deleted:
status = 'warning'
message = message + ' %s could not be deleted because their transfer status is not "Not Started". ' % str( not_deleted )
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
sample_id=trans.security.encode_id( sample.id ),
status=status,
message=message ) )
elif operation == "rename":
# If one of the selected sample datasets is in the NOT_STARTED state,
# then display an error message. A NOT_STARTED state implies the dataset
# has not yet been transferred.
no_datasets_transferred = True
for selected_sample_dataset in selected_sample_datasets:
if selected_sample_dataset in selected_sample_dataset.sample.untransferred_dataset_files:
no_datasets_transferred = False
break
if no_datasets_transferred:
status = 'error'
message = 'A dataset can be renamed only if it is in the "Not Started" state.'
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 ),
status=status,
message=message ) )
return trans.fill_template( '/admin/requests/rename_datasets.mako',
sample=selected_sample_datasets[0].sample,
id_list=id_list )
elif operation == "start transfer":
self.__start_datatx( trans, selected_sample_datasets[0].sample, selected_sample_datasets )
# Render the grid view
sample_id = params.get( 'sample_id', None )
try:
sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id ( sample_id ) )
except:
return invalid_id_redirect( trans, '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',
sample_id=sample_id ) ),
grids.GridAction( "Select datasets",
dict( controller='requests_admin',
action='get_data',
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 this request",
dict( controller='requests_common',
action='manage_request',
cntrller='requests_admin',
id=request_id ) ) ]
return self.datatx_grid( trans, **kwd )
@web.expose
@web.require_admin
def rename_datasets( self, trans, **kwd ):
# This method is called from the DataTransferGrid when a user is renaming 1 or more
# SampleDatasets.
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
sample_id = kwd.get( 'sample_id', None )
try:
sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
except:
return invalid_id_redirect( trans, 'requests_admin', sample_id )
# id_list is list of SampleDataset ids, which is a subset of all
# of the SampleDatasets associated with the Sample. The user may
# or may not have selected all of the SampleDatasets for renaming.
id_list = util.listify( kwd.get( 'id_list', [] ) )
# Get all of the SampleDatasets
sample_datasets = []
for sample_dataset_id in id_list:
sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id( sample_dataset_id ) )
sample_datasets.append( sample_dataset )
if params.get( 'rename_datasets_button', False ):
for sample_dataset in sample_datasets:
encoded_id = trans.security.encode_id( sample_dataset.id )
selected_option = util.restore_text( params.get( 'rename_datasets_for_sample_%s' % encoded_id, '' ) )
new_name = util.restore_text( params.get( 'new_name_%s' % encoded_id, '' ) )
if selected_option == 'none':
sample_dataset.name = new_name
else:
sample_dataset.name = '%s_%s' % ( selected_option, new_name )
trans.sa_session.add( sample_dataset )
trans.sa_session.flush()
message = 'Changes saved successfully.'
return trans.fill_template( '/admin/requests/rename_datasets.mako',
sample=sample,
id_list=id_list,
message=message,
status=status )
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
sample_id=sample_id ) )
@web.expose
@web.require_admin
def get_data( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
request_id = kwd.get( 'request_id', None )
files = []
try:
request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
except:
return invalid_id_redirect( trans, 'requests_admin', request_id )
selected_files = util.listify( params.get( 'files_list', [] ) )
folder_path = util.restore_text( params.get( 'folder_path', request.type.datatx_info[ 'data_dir' ] ) )
selected_sample_id = kwd.get( 'sample_id', 'none' )
sample_id_select_field = self.__build_sample_id_select_field( trans, request, selected_sample_id )
# The __get_files() method redirects here with a status of 'error' and a message if there
# was a problem retrieving the files.
if folder_path and status != 'error':
folder_path = self.__check_path( folder_path )
if params.get( 'folder_up', False ):
if folder_path[-1] == os.sep:
folder_path = os.path.dirname( folder_path[:-1] )
folder_path = self.__check_path( folder_path )
elif params.get( 'open_folder', False ):
if len(selected_files) == 1:
folder_path = os.path.join(folder_path, selected_files[0])
folder_path = self.__check_path( folder_path )
elif params.get( 'select_show_datasets_button', False ) or params.get( 'select_more_button', False ):
# get the sample these datasets are associated with
try:
sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( selected_sample_id ) )
except:
return invalid_id_redirect( trans, 'requests_admin', selected_sample_id )
if sample in sample.request.samples_without_library_destinations:
# Display an error if a sample has been selected that
# has not yet been associated with a destination library.
status = 'error'
message = 'Select a sample with associated data library and folder before selecting the datasets.'
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='get_data',
request_id=request_id,
folder_path=folder_path,
status=status,
message=message ) )
# Save the sample datasets
sample_dataset_file_names = self.__save_sample_datasets( trans, sample, selected_files, folder_path )
if sample_dataset_file_names:
message = 'Datasets (%s) have been selected for sample (%s)' % \
( str( sample_dataset_file_names )[1:-1].replace( "'", "" ), sample.name )
if params.get( 'select_show_datasets_button', False ):
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
request_id=request_id,
sample_id=selected_sample_id,
message=message,
status=status ) )
else: # 'select_more_button' was clicked
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='get_data',
request_id=request_id,
folder_path=folder_path,
sample_id=sample.id,
message=message,
status=status ) )
# Get the filenames from the remote host
files = self.__get_files( trans, request, folder_path )
return trans.fill_template( '/admin/requests/get_data.mako',
cntrller='requests_admin',
request=request,
sample_id_select_field=sample_id_select_field,
files=files,
folder_path=folder_path,
status=status,
message=message )
@web.json
def get_file_details( self, trans, id, folder_path ):
def print_ticks( d ):
# pexpect timeout method
pass
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
request = trans.sa_session.query( trans.model.Request ).get( int( id ) )
datatx_info = request.type.datatx_info
cmd = 'ssh %s@%s "ls -oghp \'%s\'"' % ( datatx_info['username'],
datatx_info['host'],
folder_path )
output = pexpect.run( cmd,
events={ '.ssword:*' : datatx_info[ 'password'] + '\r\n', pexpect.TIMEOUT : print_ticks },
timeout=10 )
return unicode( output.replace( '\n', '
' ) )
@web.json
def open_folder( self, trans, id, folder_path ):
def print_ticks( d ):
# pexpect timeout method
pass
# Avoid caching
trans.response.headers['Pragma'] = 'no-cache'
trans.response.headers['Expires'] = '0'
request = trans.sa_session.query( trans.model.Request ).get( int( id ) )
return self.__get_files( trans, request, folder_path )
def __get_files( self, trans, request, folder_path ):
# Retrieves the filenames to be transferred from the remote host.
ok = True
datatx_info = request.type.datatx_info
if not datatx_info[ 'host' ] or not datatx_info[ 'username' ] or not datatx_info[ 'password' ]:
status = 'error'
message = "Error in sequencer login information."
ok = False
def print_ticks( d ):
pass
cmd = 'ssh %s@%s "ls -p \'%s\'"' % ( datatx_info['username'], datatx_info['host'], folder_path )
output = pexpect.run( cmd,
events={ '.ssword:*' : datatx_info['password'] + '\r\n', pexpect.TIMEOUT : print_ticks },
timeout=10 )
if 'No such file or directory' in output:
status = 'error'
message = "No folder named (%s) exists on the sequencer." % folder_path
ok = False
if ok:
return output.splitlines()
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='get_data',
request_id=trans.security.encode_id( request.id ),
folder_path=folder_path,
status=status,
message=message ) )
def __check_path( self, a_path ):
# Return a valid folder_path
if a_path and not a_path.endswith( os.sep ):
a_path += os.sep
return a_path
def __save_sample_datasets( self, trans, sample, selected_files, folder_path ):
sample_dataset_file_names = []
if selected_files:
for f in selected_files:
filepath = os.path.join( folder_path, f )
if f[-1] == os.sep:
# FIXME: The selected item is a folder so transfer all the folder contents
request_id = trans.security.ecnode_id( sample.request.id )
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='get_data',
request_id=request_id,
folder_path=folder_path,
open_folder=True ) )
else:
name = self.__dataset_name( sample, filepath.split( '/' )[-1] )
sample_dataset = trans.model.SampleDataset( sample=sample,
file_path=filepath,
status=sample.transfer_status.NOT_STARTED,
name=name,
error_msg='',
size=sample.dataset_size( filepath ) )
trans.sa_session.add( sample_dataset )
trans.sa_session.flush()
sample_dataset_file_names.append( str( sample_dataset.name ) )
return sample_dataset_file_names
def __dataset_name( self, sample, filepath ):
name = filepath.split( '/' )[-1]
options = sample.request.type.rename_dataset_options
option = sample.request.type.datatx_info.get( 'rename_dataset', options.NO )
if option == options.NO:
return name
if option == options.SAMPLE_NAME:
return sample.name + '_' + name
if option == options.EXPERIMENT_AND_SAMPLE_NAME:
return sample.request.name + '_' + sample.name + '_' + name
if opt == options.EXPERIMENT_NAME:
return sample.request.name + '_' + name
def __setup_datatx_user( self, trans, library, folder ):
"""
Sets up the datatx user:
- Checks if the user exists, if not creates them.
- Checks if the user had ADD_LIBRARY permission on the target library
and the target folder, if not sets up the permissions.
"""
# Retrieve the upload user login information from the config file
config = ConfigParser.ConfigParser()
config.read( 'transfer_datasets.ini' )
email = config.get( "data_transfer_user_login_info", "email" )
password = config.get( "data_transfer_user_login_info", "password" )
# check if the user already exists
datatx_user = trans.sa_session.query( trans.model.User ) \
.filter( trans.model.User.table.c.email==email ) \
.first()
if not datatx_user:
# if not create the user
datatx_user = trans.model.User( email=email, password=passsword )
if trans.app.config.use_remote_user:
datatx_user.external = True
trans.sa_session.add( datatx_user )
trans.sa_session.flush()
trans.app.security_agent.create_private_user_role( datatx_user )
trans.app.security_agent.user_set_default_permissions( datatx_user, history=False, dataset=False )
datatx_user_roles = datatx_user.all_roles()
datatx_user_private_role = trans.app.security_agent.get_private_user_role( datatx_user )
# Make sure this user has LIBRARY_ADD permissions on the target library and folder.
# If not, give them permission.
if not trans.app.security_agent.can_add_library_item( datatx_user_roles, library ):
lp = trans.model.LibraryPermissions( trans.app.security_agent.permitted_actions.LIBRARY_ADD.action,
library,
datatx_user_private_role )
trans.sa_session.add( lp )
if not trans.app.security_agent.can_add_library_item( datatx_user_roles, folder ):
lfp = trans.model.LibraryFolderPermissions( trans.app.security_agent.permitted_actions.LIBRARY_ADD.action,
folder,
datatx_user_private_role )
trans.sa_session.add( lfp )
trans.sa_session.flush()
return datatx_user
def __send_message( self, trans, datatx_info, sample, selected_sample_datasets ):
"""Ceates an xml message and sends it to the rabbitmq server"""
# Create the xml message based on the following template
xml = \
'''
%(DATA_HOST)s
%(DATA_USER)s
%(DATA_PASSWORD)s
%(SAMPLE_ID)s
%(LIBRARY_ID)s
%(FOLDER_ID)s
%(DATASETS)s
'''
dataset_xml = \
'''
%(ID)s
%(NAME)s
%(FILE)s
'''
datasets = ''
for sample_dataset in selected_sample_datasets:
if sample_dataset.status == sample.transfer_status.NOT_STARTED:
datasets = datasets + dataset_xml % dict( ID=str( sample_dataset.id ),
NAME=sample_dataset.name,
FILE=sample_dataset.file_path )
sample_dataset.status = sample.transfer_status.IN_QUEUE
trans.sa_session.add( sample_dataset )
trans.sa_session.flush()
data = xml % dict( DATA_HOST=datatx_info['host'],
DATA_USER=datatx_info['username'],
DATA_PASSWORD=datatx_info['password'],
SAMPLE_ID=str(sample.id),
LIBRARY_ID=str(sample.library.id),
FOLDER_ID=str(sample.folder.id),
DATASETS=datasets )
# Send the message
try:
conn = amqp.Connection( host=trans.app.config.amqp['host'] + ":" + trans.app.config.amqp['port'],
userid=trans.app.config.amqp['userid'],
password=trans.app.config.amqp['password'],
virtual_host=trans.app.config.amqp['virtual_host'],
insist=False )
chan = conn.channel()
msg = amqp.Message( data.replace( '\n', '' ).replace( '\r', '' ),
content_type='text/plain',
application_headers={'msg_type': 'data_transfer'} )
msg.properties["delivery_mode"] = 2
chan.basic_publish( msg,
exchange=trans.app.config.amqp['exchange'],
routing_key=trans.app.config.amqp['routing_key'] )
chan.close()
conn.close()
except Exception, e:
message = "Error in sending the data transfer message to the Galaxy AMQP message queue:
%s" % str(e)
status = "error"
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
sample_id=trans.security.encode_id( sample.id ),
status=status,
message=message) )
def __start_datatx( self, trans, sample, selected_sample_datasets ):
datatx_user = self.__setup_datatx_user( trans, sample.library, sample.folder )
# Validate sequencer information
datatx_info = sample.request.type.datatx_info
if not datatx_info['host'] or not datatx_info['username'] or not datatx_info['password']:
message = "Error in sequencer login information."
status = "error"
else:
self.__send_message( trans, datatx_info, sample, selected_sample_datasets )
message = "%i datasets have been queued for transfer from the sequencer. Click the Refresh button above to see the latest transfer status." % len( selected_sample_datasets )
status = "done"
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='manage_datasets',
sample_id=trans.security.encode_id( sample.id ),
status=status,
message=message) )
# Request Type Stuff
@web.expose
@web.require_admin
def browse_request_types( self, trans, **kwd ):
if 'operation' in kwd:
operation = kwd['operation'].lower()
obj_id = kwd.get( 'id', None )
if operation == "view_form_definition":
return self.view_form_definition( trans, **kwd )
elif operation == "view":
return self.view_request_type( trans, **kwd )
elif operation == "delete":
return self.delete_request_type( trans, **kwd )
elif operation == "undelete":
return self.undelete_request_type( trans, **kwd )
elif operation == "permissions":
return self.request_type_permissions( trans, **kwd )
# Render the grid view
return self.requesttype_grid( trans, **kwd )
@web.expose
@web.require_admin
def create_request_type( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
rt_info_widgets, rt_states_widgets = self.__create_request_type_form( trans, **kwd )
rename_dataset_select_field = self.__build_rename_dataset_select_field( trans )
if params.get( 'add_state_button', False ):
# FIXME: why do we append a tuple of 2 empty strings????
rt_states_widgets.append( ( "", "" ) )
elif params.get( 'remove_state_button', False ):
index = int( params.get( 'remove_state_button', '' ).split(" ")[2] )
del rt_states_widgets[ index-1 ]
elif params.get( 'save_request_type', False ):
request_type, message = self.__save_request_type( trans, **kwd )
if not request_type:
status='error'
else:
message = 'Sequencer configuration (%s) has been created' % request_type.name
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='browse_request_types',
message=message,
status=status ) )
elif params.get( 'save_changes', False ):
request_type_id = params.get( 'rt_id', None )
try:
request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
except:
return invalid_id_redirect( trans, 'requests_admin', request_type_id, action='browse_request_types' )
# Data transfer info - make sure password is retrieved from kwd rather
# than Params since Params may have munged the characters.
request_type.datatx_info = dict( host=util.restore_text( params.get( 'host', '' ) ),
username=util.restore_text( params.get( 'username', '' ) ),
password=kwd.get( 'password', '' ),
data_dir=util.restore_text( params.get( 'data_dir', '' ) ),
rename_dataset=util.restore_text( params.get( 'rename_dataset', False ) ) )
data_dir = self.__check_path( request_type.datatx_info[ 'data_dir' ] )
request_type.datatx_info[ 'data_dir' ] = data_dir
trans.sa_session.add( request_type )
trans.sa_session.flush()
message = 'Changes made to sequencer configuration %s has been saved' % request_type.name
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='view_request_type',
id=request_type_id,
message=message,
status=status ) )
return trans.fill_template( '/admin/requests/create_request_type.mako',
rt_info_widgets=rt_info_widgets,
rt_states_widgets=rt_states_widgets,
rename_dataset_select_field=rename_dataset_select_field,
message=message,
status=status )
def __create_request_type_form( self, trans, **kwd ):
request_form_definitions = self.get_all_forms( trans,
filter=dict( deleted=False ),
form_type=trans.model.FormDefinition.types.REQUEST )
sample_form_definitions = self.get_all_forms( trans,
filter=dict( deleted=False ),
form_type=trans.model.FormDefinition.types.SAMPLE )
if not request_form_definitions or not sample_form_definitions:
return [],[]
params = util.Params( kwd )
request_form_id = params.get( 'request_form_id', 'none' )
sample_form_id = params.get( 'sample_form_id', 'none' )
request_form_id_select_field = build_select_field( trans,
objs=request_form_definitions,
label_attr='name',
select_field_name='request_form_id',
selected_value=request_form_id,
refresh_on_change=False )
sample_form_id_select_field = build_select_field( trans,
objs=sample_form_definitions,
label_attr='name',
select_field_name='sample_form_id',
selected_value=sample_form_id,
refresh_on_change=False )
rt_info_widgets = [ dict( label='Name',
widget=TextField( 'name', 40, util.restore_text( params.get( 'name', '' ) ) ) ),
dict( label='Description',
widget=TextField( 'desc', 40, util.restore_text( params.get( 'desc', '' ) ) ) ),
dict( label='Request form',
widget=request_form_id_select_field ),
dict( label='Sample form',
widget=sample_form_id_select_field ) ]
# Possible sample states
rt_states = []
i=0
while True:
if kwd.has_key( 'state_name_%i' % i ):
rt_states.append( ( params.get( 'state_name_%i' % i, '' ),
params.get( 'state_desc_%i' % i, '' ) ) )
i += 1
else:
break
return rt_info_widgets, rt_states
def __save_request_type(self, trans, **kwd):
params = util.Params( kwd )
name = util.restore_text( params.get( 'name', '' ) )
desc = util.restore_text( params.get( 'desc', '' ) )
request_form_id = params.get( 'request_form_id', None )
request_form = trans.sa_session.query( trans.model.FormDefinition ).get( trans.security.decode_id( request_form_id ) )
sample_form_id = params.get( 'sample_form_id', None )
sample_form = trans.sa_session.query( trans.model.FormDefinition ).get( trans.security.decode_id( sample_form_id ) )
data_dir = util.restore_text( params.get( 'data_dir', '' ) )
data_dir = self.__check_path( data_dir )
# Data transfer info - Make sure password is retrieved from kwd rather than Params
# since Params may have munged the characters.
datatx_info = dict( host=util.restore_text( params.get( 'host', '' ) ),
username=util.restore_text( params.get( 'username', '' ) ),
password=kwd.get( 'password', '' ),
data_dir=data_dir,
rename_dataset=util.restore_text( params.get( 'rename_dataset', '' ) ) )
request_type = trans.model.RequestType( name=name, desc=desc, request_form=request_form, sample_form=sample_form, datatx_info=datatx_info )
trans.sa_session.add( request_type )
trans.sa_session.flush()
# set sample states
ss_list = trans.sa_session.query( trans.model.SampleState ) \
.filter( trans.model.SampleState.table.c.request_type_id == request_type.id )
for ss in ss_list:
trans.sa_session.delete( ss )
trans.sa_session.flush()
i = 0
while True:
if kwd.has_key( 'state_name_%i' % i ):
name = util.restore_text( params.get( 'state_name_%i' % i, None ) )
desc = util.restore_text( params.get( 'state_desc_%i' % i, None ) )
ss = trans.model.SampleState( name, desc, request_type )
trans.sa_session.add( ss )
trans.sa_session.flush()
i = i + 1
else:
break
message = "The new sequencer configuration named (%s) with %s states has been created" % ( request_type.name, i )
return request_type, message
@web.expose
@web.require_admin
def view_request_type( self, trans, **kwd ):
request_type_id = kwd.get( 'id', None )
try:
request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
except:
return invalid_id_redirect( trans, 'requests_admin', request_type_id, action='browse_request_types' )
forms = self.get_all_forms( trans )
rename_dataset_select_field = self.__build_rename_dataset_select_field( trans, request_type )
return trans.fill_template( '/admin/requests/view_request_type.mako',
request_type=request_type,
forms=forms,
rename_dataset_select_field=rename_dataset_select_field )
@web.expose
@web.require_admin
def view_form_definition( self, trans, **kwd ):
form_definition_id = kwd.get( 'id', None )
try:
form_definition = trans.sa_session.query( trans.model.FormDefinition ).get( trans.security.decode_id( form_definition_id ) )
except:
return invalid_id_redirect( trans, 'requests_admin', form_definition_id, action='browse_request_types' )
return trans.fill_template( '/admin/forms/show_form_read_only.mako',
form_definition=form_definition )
@web.expose
@web.require_admin
def delete_request_type( self, trans, **kwd ):
rt_id = kwd.get( 'id', '' )
rt_id_list = util.listify( rt_id )
for rt_id in rt_id_list:
try:
request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( rt_id ) )
except:
return invalid_id_redirect( trans, 'requests_admin', rt_id, action='browse_request_types' )
request_type.deleted = True
trans.sa_session.add( request_type )
trans.sa_session.flush()
status = 'done'
message = '%i sequencer configurations has been deleted' % len( rt_id_list )
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='browse_request_types',
message=message,
status='done' ) )
@web.expose
@web.require_admin
def undelete_request_type( self, trans, **kwd ):
rt_id = kwd.get( 'id', '' )
rt_id_list = util.listify( rt_id )
for rt_id in rt_id_list:
try:
request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( rt_id ) )
except:
return invalid_id_redirect( trans, 'requests_admin', rt_id, action='browse_request_types' )
request_type.deleted = False
trans.sa_session.add( request_type )
trans.sa_session.flush()
status = 'done'
message = '%i sequencer configurations have been undeleted' % len( rt_id_list )
return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='browse_request_types',
message=message,
status=status ) )
@web.expose
@web.require_admin
def request_type_permissions( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
request_type_id = kwd.get( 'id', '' )
try:
request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
except:
return invalid_id_redirect( trans, 'requests_admin', request_type_id, action='browse_request_types' )
roles = trans.sa_session.query( trans.model.Role ) \
.filter( trans.model.Role.table.c.deleted==False ) \
.order_by( trans.model.Role.table.c.name )
if params.get( 'update_roles_button', False ):
permissions = {}
for k, v in trans.model.RequestType.permitted_actions.items():
in_roles = [ trans.sa_session.query( trans.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.set_request_type_permissions( request_type, permissions )
trans.sa_session.refresh( request_type )
message = "Permissions updated for sequencer configuration '%s'" % request_type.name
return trans.fill_template( '/admin/requests/request_type_permissions.mako',
request_type=request_type,
roles=roles,
status=status,
message=message )
# ===== Methods for building SelectFields used on various admin_requests forms
def __build_sample_id_select_field( self, trans, request, selected_value ):
return build_select_field( trans, request.samples, 'name', 'sample_id', selected_value=selected_value, refresh_on_change=False )
def __build_rename_dataset_select_field( self, trans, request_type=None ):
if request_type:
selected_value = request_type.datatx_info.get( 'rename_dataset', trans.model.RequestType.rename_dataset_options.NO )
else:
selected_value = trans.model.RequestType.rename_dataset_options.NO
return build_select_field( trans,
objs=[ v for k, v in trans.model.RequestType.rename_dataset_options.items() ],
label_attr='self',
select_field_name='rename_dataset',
selected_value=selected_value,
refresh_on_change=False )
# ===== Methods for building SelectFields used on various admin_requests forms - used outside this controller =====
def build_rename_datasets_for_sample_select_field( trans, sample_dataset, selected_value='none' ):
options = []
for option_index, option in enumerate( sample_dataset.file_path.split( os.sep )[ :-1 ] ):
option = option.strip()
if option:
options.append( option )
return build_select_field( trans,
objs=options,
label_attr='self',
select_field_name='rename_datasets_for_sample_%s' % trans.security.encode_id( sample_dataset.id ),
selected_value=selected_value,
refresh_on_change=False )