PageRenderTime 63ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/galaxy/web/controllers/requests_common.py

https://bitbucket.org/cistrome/cistrome-harvard/
Python | 1051 lines | 1020 code | 5 blank | 26 comment | 51 complexity | fd55c095c7a089f72b1fd178d0f0b56f MD5 | raw file
  1. from galaxy.web.base.controller import *
  2. from galaxy.web.framework.helpers import time_ago, iff, grids
  3. from galaxy.model.orm import *
  4. from galaxy import model, util
  5. from galaxy.util.odict import odict
  6. from galaxy.web.form_builder import *
  7. from galaxy.security.validate_user_input import validate_email
  8. import logging, os, csv
  9. log = logging.getLogger( __name__ )
  10. class RequestsGrid( grids.Grid ):
  11. # Custom column types
  12. class NameColumn( grids.TextColumn ):
  13. def get_value( self, trans, grid, request ):
  14. return request.name
  15. class DescriptionColumn( grids.TextColumn ):
  16. def get_value(self, trans, grid, request):
  17. return request.desc
  18. class SamplesColumn( grids.GridColumn ):
  19. def get_value(self, trans, grid, request):
  20. return str( len( request.samples ) )
  21. class TypeColumn( grids.TextColumn ):
  22. def get_value( self, trans, grid, request ):
  23. return request.type.name
  24. class StateColumn( grids.StateColumn ):
  25. def get_value(self, trans, grid, request ):
  26. state = request.state
  27. if state == request.states.REJECTED:
  28. state_color = 'error'
  29. elif state == request.states.NEW:
  30. state_color = 'new'
  31. elif state == request.states.SUBMITTED:
  32. state_color = 'running'
  33. elif state == request.states.COMPLETE:
  34. state_color = 'ok'
  35. else:
  36. state_color = state
  37. return '<div class="count-box state-color-%s">%s</div>' % ( state_color, state )
  38. def filter( self, trans, user, query, column_filter ):
  39. """ Modify query to filter request by state. """
  40. if column_filter == "All":
  41. return query
  42. if column_filter:
  43. return query.join( model.RequestEvent.table ) \
  44. .filter( self.model_class.table.c.id == model.RequestEvent.table.c.request_id ) \
  45. .filter( model.RequestEvent.table.c.state == column_filter ) \
  46. .filter( model.RequestEvent.table.c.id.in_( select( columns=[ func.max( model.RequestEvent.table.c.id ) ],
  47. from_obj=model.RequestEvent.table,
  48. group_by=model.RequestEvent.table.c.request_id ) ) )
  49. # Grid definition
  50. title = "Sequencing Requests"
  51. template = "requests/grid.mako"
  52. model_class = model.Request
  53. default_sort_key = "-update_time"
  54. num_rows_per_page = 50
  55. use_paging = True
  56. default_filter = dict( state="All", deleted="False" )
  57. columns = [
  58. NameColumn( "Name",
  59. key="name",
  60. link=( lambda item: dict( operation="view_request", id=item.id ) ),
  61. attach_popup=True,
  62. filterable="advanced" ),
  63. DescriptionColumn( "Description",
  64. key='desc',
  65. filterable="advanced" ),
  66. SamplesColumn( "Samples",
  67. link=( lambda item: iff( item.deleted, None, dict( operation="edit_samples", id=item.id ) ) ) ),
  68. TypeColumn( "Type",
  69. link=( lambda item: iff( item.deleted, None, dict( operation="view_type", id=item.type.id ) ) ) ),
  70. grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
  71. grids.DeletedColumn( "Deleted",
  72. key="deleted",
  73. visible=False,
  74. filterable="advanced" ),
  75. StateColumn( "State",
  76. key='state',
  77. filterable="advanced",
  78. link=( lambda item: iff( item.deleted, None, dict( operation="view_request_history", id=item.id ) ) )
  79. )
  80. ]
  81. columns.append( grids.MulticolFilterColumn( "Search",
  82. cols_to_filter=[ columns[0], columns[1] ],
  83. key="free-text-search",
  84. visible=False,
  85. filterable="standard" ) )
  86. operations = [
  87. grids.GridOperation( "Submit",
  88. allow_multiple=False,
  89. condition=( lambda item: not item.deleted and item.is_unsubmitted and item.samples ),
  90. confirm="Samples cannot be added to this request after it is submitted. Click OK to submit." )
  91. ]
  92. class RequestsCommon( BaseUIController, UsesFormDefinitions ):
  93. @web.json
  94. def sample_state_updates( self, trans, ids=None, states=None ):
  95. # Avoid caching
  96. trans.response.headers['Pragma'] = 'no-cache'
  97. trans.response.headers['Expires'] = '0'
  98. # Create new HTML for any that have changed
  99. rval = {}
  100. if ids is not None and states is not None:
  101. ids = map( int, ids.split( "," ) )
  102. states = states.split( "," )
  103. for id, state in zip( ids, states ):
  104. sample = trans.sa_session.query( self.app.model.Sample ).get( id )
  105. if sample.state.name != state:
  106. rval[ id ] = { "state": sample.state.name,
  107. "html_state": unicode( trans.fill_template( "requests/common/sample_state.mako",
  108. sample=sample),
  109. 'utf-8' ) }
  110. return rval
  111. @web.json
  112. def sample_datasets_updates( self, trans, ids=None, datasets=None ):
  113. # Avoid caching
  114. trans.response.headers['Pragma'] = 'no-cache'
  115. trans.response.headers['Expires'] = '0'
  116. # Create new HTML for any that have changed
  117. rval = {}
  118. if ids is not None and datasets is not None:
  119. ids = map( int, ids.split( "," ) )
  120. number_of_datasets_list = map(int, datasets.split( "," ) )
  121. for id, number_of_datasets in zip( ids, number_of_datasets_list ):
  122. sample = trans.sa_session.query( self.app.model.Sample ).get( id )
  123. if len(sample.datasets) != number_of_datasets:
  124. rval[ id ] = { "datasets": len( sample.datasets ),
  125. "html_datasets": unicode( trans.fill_template( "requests/common/sample_datasets.mako",
  126. sample=sample),
  127. 'utf-8' ) }
  128. return rval
  129. @web.json
  130. def dataset_transfer_status_updates( self, trans, ids=None, transfer_status_list=None ):
  131. # Avoid caching
  132. trans.response.headers['Pragma'] = 'no-cache'
  133. trans.response.headers['Expires'] = '0'
  134. # Create new HTML for any that have changed
  135. rval = {}
  136. if ids is not None and transfer_status_list is not None:
  137. ids = ids.split( "," )
  138. transfer_status_list = transfer_status_list.split( "," )
  139. for id, transfer_status in zip( ids, transfer_status_list ):
  140. sample_dataset = trans.sa_session.query( self.app.model.SampleDataset ).get( trans.security.decode_id( id ) )
  141. if sample_dataset.status != transfer_status:
  142. rval[ id ] = { "status": sample_dataset.status,
  143. "html_status": unicode( trans.fill_template( "requests/common/sample_dataset_transfer_status.mako",
  144. sample_dataset=sample_dataset),
  145. 'utf-8' ) }
  146. return rval
  147. @web.expose
  148. @web.require_login( "create sequencing requests" )
  149. def create_request( self, trans, cntrller, **kwd ):
  150. params = util.Params( kwd )
  151. is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
  152. message = util.restore_text( params.get( 'message', '' ) )
  153. status = params.get( 'status', 'done' )
  154. request_type_id = params.get( 'request_type_id', 'none' )
  155. if request_type_id != 'none':
  156. request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
  157. else:
  158. request_type = None
  159. # user_id will not be 'none' if an admin user is submitting this request on behalf of another user
  160. # and they selected that user's id from the user_id SelectField.
  161. user_id_encoded = True
  162. user_id = params.get( 'user_id', 'none' )
  163. if user_id != 'none':
  164. try:
  165. user = trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( user_id ) )
  166. except TypeError, e:
  167. # We must have an email address rather than an encoded user id
  168. # This is because the galaxy.base.js creates a search+select box
  169. # when there are more than 20 items in a SelectField.
  170. user = trans.sa_session.query( trans.model.User ) \
  171. .filter( trans.model.User.table.c.email==util.restore_text( user_id ) ) \
  172. .first()
  173. user_id_encoded = False
  174. elif not is_admin:
  175. user = trans.user
  176. else:
  177. user = None
  178. if params.get( 'create_request_button', False ) or params.get( 'add_sample_button', False ):
  179. name = util.restore_text( params.get( 'name', '' ) )
  180. if is_admin and user_id == 'none':
  181. message = 'Select the user on behalf of whom you are submitting this request.'
  182. status = 'error'
  183. elif user is None:
  184. message = 'Invalid user ID (%s)' % str(user_id)
  185. status = 'error'
  186. # when creating a request from the user perspective, check if the
  187. # user has access permission to this request_type
  188. elif cntrller == 'requests' and not trans.app.security_agent.can_access_request_type( user.all_roles(), request_type ):
  189. message = '%s does not have access permission to the "%s" request type.' % ( user.email, request_type.name )
  190. status = 'error'
  191. elif not name:
  192. message = 'Enter the name of the request.'
  193. status = 'error'
  194. else:
  195. request = self.__save_request( trans, cntrller, **kwd )
  196. message = 'The sequencing request has been created.'
  197. if params.get( 'create_request_button', False ):
  198. return trans.response.send_redirect( web.url_for( controller=cntrller,
  199. action='browse_requests',
  200. message=message ,
  201. status='done' ) )
  202. elif params.get( 'add_sample_button', False ):
  203. request_id = trans.security.encode_id( request.id )
  204. return self.add_sample( trans, cntrller, request_id, **kwd )
  205. request_type_select_field = self.__build_request_type_id_select_field( trans, selected_value=request_type_id )
  206. # Widgets to be rendered on the request form
  207. widgets = []
  208. if request_type is not None or status == 'error':
  209. # Either the user selected a request_type or an error exists on the form.
  210. widgets.append( dict( label='Name of the Experiment',
  211. widget=TextField( 'name', 40, util.restore_text( params.get( 'name', '' ) ) ),
  212. helptext='(Required)') )
  213. widgets.append( dict( label='Description',
  214. widget=TextField( 'desc', 40, util.restore_text( params.get( 'desc', '' ) )),
  215. helptext='(Optional)') )
  216. if request_type is not None:
  217. widgets += request_type.request_form.get_widgets( user, **kwd )
  218. # In case there is an error on the form, make sure to populate widget fields with anything the user
  219. # may have already entered.
  220. widgets = self.populate_widgets_from_kwd( trans, widgets, **kwd )
  221. if request_type is not None or status == 'error':
  222. # Either the user selected a request_type or an error exists on the form.
  223. if is_admin:
  224. if not user_id_encoded and user:
  225. selected_user_id = trans.security.encode_id( user.id )
  226. else:
  227. selected_user_id = user_id
  228. user_widget = dict( label='Select user',
  229. widget=self.__build_user_id_select_field( trans, selected_value=selected_user_id ),
  230. helptext='Submit the request on behalf of the selected user (Required)')
  231. widgets = [ user_widget ] + widgets
  232. return trans.fill_template( '/requests/common/create_request.mako',
  233. cntrller=cntrller,
  234. request_type_select_field=request_type_select_field,
  235. request_type_select_field_selected=request_type_id,
  236. widgets=widgets,
  237. message=message,
  238. status=status )
  239. @web.expose
  240. @web.require_login( "view request" )
  241. def view_request( self, trans, cntrller, **kwd ):
  242. params = util.Params( kwd )
  243. is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
  244. message = util.restore_text( params.get( 'message', '' ) )
  245. status = params.get( 'status', 'done' )
  246. request_id = params.get( 'id', None )
  247. try:
  248. request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
  249. except:
  250. return invalid_id_redirect( trans, cntrller, request_id )
  251. sample_state_id = params.get( 'sample_state_id', None )
  252. # Build a list of sample widgets (based on the attributes of each sample) for display.
  253. displayable_sample_widgets = self.__get_sample_widgets( trans, request, request.samples, **kwd )
  254. request_widgets = self.__get_request_widgets( trans, request.id )
  255. return trans.fill_template( '/requests/common/view_request.mako',
  256. cntrller=cntrller,
  257. request=request,
  258. request_widgets=request_widgets,
  259. displayable_sample_widgets=displayable_sample_widgets,
  260. status=status,
  261. message=message )
  262. @web.expose
  263. @web.require_login( "edit sequencing requests" )
  264. def edit_basic_request_info( self, trans, cntrller, **kwd ):
  265. params = util.Params( kwd )
  266. message = util.restore_text( params.get( 'message', '' ) )
  267. status = params.get( 'status', 'done' )
  268. request_id = params.get( 'id', None )
  269. try:
  270. request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
  271. except:
  272. return invalid_id_redirect( trans, cntrller, request_id )
  273. name = util.restore_text( params.get( 'name', '' ) )
  274. desc = util.restore_text( params.get( 'desc', '' ) )
  275. if params.get( 'edit_basic_request_info_button', False ):
  276. if not name:
  277. status = 'error'
  278. message = 'Enter the name of the request'
  279. else:
  280. request = self.__save_request( trans, cntrller, request=request, **kwd )
  281. message = 'The changes made to request (%s) have been saved.' % request.name
  282. # Widgets to be rendered on the request form
  283. widgets = []
  284. widgets.append( dict( label='Name',
  285. widget=TextField( 'name', 40, request.name ),
  286. helptext='(Required)' ) )
  287. widgets.append( dict( label='Description',
  288. widget=TextField( 'desc', 40, request.desc ),
  289. helptext='(Optional)' ) )
  290. widgets = widgets + request.type.request_form.get_widgets( request.user, request.values.content, **kwd )
  291. # In case there is an error on the form, make sure to populate widget fields with anything the user
  292. # may have already entered.
  293. widgets = self.populate_widgets_from_kwd( trans, widgets, **kwd )
  294. return trans.fill_template( 'requests/common/edit_basic_request_info.mako',
  295. cntrller=cntrller,
  296. request_type=request.type,
  297. request=request,
  298. widgets=widgets,
  299. message=message,
  300. status=status )
  301. def __save_request( self, trans, cntrller, request=None, **kwd ):
  302. """
  303. Saves changes to an existing request, or creates a new
  304. request if received request is None.
  305. """
  306. params = util.Params( kwd )
  307. request_type_id = params.get( 'request_type_id', None )
  308. is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
  309. if request is None:
  310. # We're creating a new request, so we need the associated request_type
  311. request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
  312. if is_admin:
  313. # The admin user is creating a request on behalf of another user
  314. user_id = params.get( 'user_id', '' )
  315. user = trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( user_id ) )
  316. else:
  317. user = trans.user
  318. else:
  319. # We're saving changes to an existing request
  320. user = request.user
  321. request_type = request.type
  322. name = util.restore_text( params.get( 'name', '' ) )
  323. desc = util.restore_text( params.get( 'desc', '' ) )
  324. notification = dict( email=[ user.email ], sample_states=[ request_type.final_sample_state.id ], body='', subject='' )
  325. values = self.get_form_values( trans, user, request_type.request_form, **kwd )
  326. if request is None:
  327. form_values = trans.model.FormValues( request_type.request_form, values )
  328. trans.sa_session.add( form_values )
  329. # We're creating a new request
  330. request = trans.model.Request( name, desc, request_type, user, form_values, notification )
  331. trans.sa_session.add( request )
  332. trans.sa_session.flush()
  333. trans.sa_session.refresh( request )
  334. # Create an event with state 'New' for this new request
  335. comment = "Sequencing request created by %s" % trans.user.email
  336. if request.user != trans.user:
  337. comment += " on behalf of %s." % request.user.email
  338. event = trans.model.RequestEvent( request, request.states.NEW, comment )
  339. trans.sa_session.add( event )
  340. trans.sa_session.flush()
  341. else:
  342. # We're saving changes to an existing request
  343. request.name = name
  344. request.desc = desc
  345. request.type = request_type
  346. request.user = user
  347. request.notification = notification
  348. request.values.content = values
  349. trans.sa_session.add( request )
  350. trans.sa_session.add( request.values )
  351. trans.sa_session.flush()
  352. return request
  353. @web.expose
  354. @web.require_login( "submit sequencing requests" )
  355. def submit_request( self, trans, cntrller, **kwd ):
  356. params = util.Params( kwd )
  357. request_id = params.get( 'id', None )
  358. message = util.restore_text( params.get( 'message', '' ) )
  359. status = util.restore_text( params.get( 'status', 'done' ) )
  360. try:
  361. request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
  362. except:
  363. return invalid_id_redirect( trans, cntrller, request_id )
  364. ok = True
  365. if not request.samples:
  366. message = 'Add at least 1 sample to this request before submitting.'
  367. ok = False
  368. if ok:
  369. message = self.__validate_request( trans, cntrller, request )
  370. if message or not ok:
  371. return trans.response.send_redirect( web.url_for( controller='requests_common',
  372. action='edit_basic_request_info',
  373. cntrller=cntrller,
  374. id = request_id,
  375. status='error',
  376. message=message ) )
  377. # Change the request state to 'Submitted'
  378. comment = "Sequencing request submitted by %s" % trans.user.email
  379. if request.user != trans.user:
  380. comment += " on behalf of %s." % request.user.email
  381. event = trans.model.RequestEvent( request, request.states.SUBMITTED, comment )
  382. trans.sa_session.add( event )
  383. # Change the state of each of the samples of this request
  384. # request.type.states is the list of SampleState objects configured
  385. # by the admin for this RequestType.
  386. trans.sa_session.add( event )
  387. trans.sa_session.flush()
  388. # Samples will not have an associated SampleState until the request is submitted, at which
  389. # time all samples of the request will be set to the first SampleState configured for the
  390. # request's RequestType configured by the admin.
  391. initial_sample_state_after_request_submitted = request.type.states[0]
  392. for sample in request.samples:
  393. event_comment = 'Sequencing request submitted and sample state set to %s.' % request.type.states[0].name
  394. event = trans.model.SampleEvent( sample,
  395. initial_sample_state_after_request_submitted,
  396. event_comment )
  397. trans.sa_session.add( event )
  398. trans.sa_session.add( request )
  399. trans.sa_session.flush()
  400. request.send_email_notification( trans, initial_sample_state_after_request_submitted )
  401. message = 'The sequencing request has been submitted.'
  402. # show the request page after submitting the request
  403. return trans.response.send_redirect( web.url_for( controller='requests_common',
  404. action='view_request',
  405. cntrller=cntrller,
  406. id=request_id,
  407. status=status,
  408. message=message ) )
  409. @web.expose
  410. @web.require_login( "edit samples" )
  411. def edit_samples( self, trans, cntrller, **kwd ):
  412. params = util.Params( kwd )
  413. is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
  414. message = util.restore_text( params.get( 'message', '' ) )
  415. status = params.get( 'status', 'done' )
  416. request_id = params.get( 'id', None )
  417. try:
  418. request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
  419. except:
  420. return invalid_id_redirect( trans, cntrller, request_id )
  421. if params.get( 'cancel_changes_button', False ):
  422. return trans.response.send_redirect( web.url_for( controller='requests_common',
  423. action='edit_samples',
  424. cntrller=cntrller,
  425. id=request_id ) )
  426. libraries = trans.app.security_agent.get_accessible_libraries( trans, request.user )
  427. # Build a list of sample widgets (based on the attributes of each sample) for display.
  428. displayable_sample_widgets = self.__get_sample_widgets( trans, request, request.samples, **kwd )
  429. encoded_selected_sample_ids = self.__get_encoded_selected_sample_ids( trans, request, **kwd )
  430. sample_operation = params.get( 'sample_operation', 'none' )
  431. def handle_error( **kwd ):
  432. kwd[ 'status' ] = 'error'
  433. return trans.response.send_redirect( web.url_for( controller='requests_common',
  434. action='edit_samples',
  435. cntrller=cntrller,
  436. **kwd ) )
  437. if not encoded_selected_sample_ids and sample_operation != 'none':
  438. # Probably occurred due to refresh_on_change...is there a better approach?
  439. kwd[ 'sample_operation' ] = 'none'
  440. message = 'Select at least one sample before selecting an operation.'
  441. kwd[ 'message' ] = message
  442. handle_error( **kwd )
  443. if params.get( 'save_samples_button', False ):
  444. if encoded_selected_sample_ids:
  445. # We need the list of displayable_sample_widgets to include the same number
  446. # of objects that that request.samples has so that we can enumerate over each
  447. # list without problems. We have to be careful here since the user may have
  448. # used the multi-select check boxes when editing sample widgets, but didn't
  449. # select all of them. We'll first get the set of samples corresponding to the
  450. # checked sample ids.
  451. samples = []
  452. selected_samples = []
  453. for encoded_sample_id in encoded_selected_sample_ids:
  454. sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( encoded_sample_id ) )
  455. selected_samples.append( sample )
  456. # Now build the list of samples, inserting None for samples that have not been checked.
  457. for sample in request.samples:
  458. if sample in selected_samples:
  459. samples.append( sample )
  460. else:
  461. samples.append( None )
  462. # The __save_samples method requires sample_widgets, not sample objects, so we'll get what we
  463. # need by calling __get_sample_widgets(). However, we need to take care here because __get_sample_widgets()
  464. # is used to populate the sample widget dicts from kwd, and the method assumes that a None object in the
  465. # received list of samples should be populated from the db. Since we're just re-using the method here to
  466. # change our list of samples into a list of sample widgets, we'll need to make sure to keep track of our
  467. # None objects.
  468. sample_widgets = [ obj for obj in samples ]
  469. sample_widgets = self.__get_sample_widgets( trans, request, sample_widgets, **kwd )
  470. # Replace each sample widget dict with a None object if necessary
  471. for index, obj in enumerate( samples ):
  472. if obj is None:
  473. sample_widgets[ index ] = None
  474. else:
  475. sample_widgets = displayable_sample_widgets
  476. return self.__save_samples( trans, cntrller, request, sample_widgets, saving_new_samples=False, **kwd )
  477. request_widgets = self.__get_request_widgets( trans, request.id )
  478. sample_copy_select_field = self.__build_copy_sample_select_field( trans, displayable_sample_widgets )
  479. libraries_select_field, folders_select_field = self.__build_library_and_folder_select_fields( trans,
  480. request.user,
  481. 'sample_operation',
  482. libraries,
  483. None,
  484. **kwd )
  485. sample_operation_select_field = self.__build_sample_operation_select_field( trans, is_admin, request, sample_operation )
  486. sample_state_id = params.get( 'sample_state_id', None )
  487. sample_state_id_select_field = self.__build_sample_state_id_select_field( trans, request, sample_state_id )
  488. return trans.fill_template( '/requests/common/edit_samples.mako',
  489. cntrller=cntrller,
  490. request=request,
  491. encoded_selected_sample_ids=encoded_selected_sample_ids,
  492. request_widgets=request_widgets,
  493. displayable_sample_widgets=displayable_sample_widgets,
  494. sample_copy_select_field=sample_copy_select_field,
  495. libraries=libraries,
  496. sample_operation_select_field=sample_operation_select_field,
  497. libraries_select_field=libraries_select_field,
  498. folders_select_field=folders_select_field,
  499. sample_state_id_select_field=sample_state_id_select_field,
  500. status=status,
  501. message=message )
  502. @web.expose
  503. def update_sample_state(self, trans, cntrller, sample_ids, new_state, comment=None ):
  504. for sample_id in sample_ids:
  505. try:
  506. sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
  507. except:
  508. if cntrller == 'api':
  509. trans.response.status = 400
  510. return "Invalid sample id ( %s ) specified, unable to decode." % str( sample_id )
  511. else:
  512. return invalid_id_redirect( trans, cntrller, sample_id, 'sample' )
  513. if comment is None:
  514. comment = 'Sample state set to %s' % str( new_state )
  515. event = trans.model.SampleEvent( sample, new_state, comment )
  516. trans.sa_session.add( event )
  517. trans.sa_session.flush()
  518. if cntrller == 'api':
  519. return 200, 'Done'
  520. @web.expose
  521. @web.require_login( "delete sequencing requests" )
  522. def delete_request( self, trans, cntrller, **kwd ):
  523. params = util.Params( kwd )
  524. id_list = util.listify( kwd.get( 'id', '' ) )
  525. message = util.restore_text( params.get( 'message', '' ) )
  526. status = util.restore_text( params.get( 'status', 'done' ) )
  527. is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
  528. num_deleted = 0
  529. not_deleted = []
  530. for id in id_list:
  531. ok_for_now = True
  532. try:
  533. # This block will handle bots that do not send valid request ids.
  534. request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( id ) )
  535. except:
  536. ok_for_now = False
  537. if ok_for_now:
  538. # We will only allow the request to be deleted by a non-admin user if not request.submitted
  539. if is_admin or not request.is_submitted:
  540. request.deleted = True
  541. trans.sa_session.add( request )
  542. # Delete all the samples belonging to this request
  543. for s in request.samples:
  544. s.deleted = True
  545. trans.sa_session.add( s )
  546. comment = "Sequencing request marked deleted by %s." % trans.user.email
  547. # There is no DELETED state for a request, so keep the current request state
  548. event = trans.model.RequestEvent( request, request.state, comment )
  549. trans.sa_session.add( event )
  550. trans.sa_session.flush()
  551. num_deleted += 1
  552. else:
  553. not_deleted.append( request )
  554. message += '%i requests have been deleted.' % num_deleted
  555. if not_deleted:
  556. message += ' Contact the administrator to delete the following submitted requests: '
  557. for request in not_deleted:
  558. message += '%s, ' % request.name
  559. message = message.rstrip( ', ' )
  560. return trans.response.send_redirect( web.url_for( controller=cntrller,
  561. action='browse_requests',
  562. status=status,
  563. message=message ) )
  564. @web.expose
  565. @web.require_login( "undelete sequencing requests" )
  566. def undelete_request( self, trans, cntrller, **kwd ):
  567. params = util.Params( kwd )
  568. id_list = util.listify( kwd.get( 'id', '' ) )
  569. message = util.restore_text( params.get( 'message', '' ) )
  570. status = util.restore_text( params.get( 'status', 'done' ) )
  571. num_undeleted = 0
  572. for id in id_list:
  573. ok_for_now = True
  574. try:
  575. # This block will handle bots that do not send valid request ids.
  576. request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( id ) )
  577. except:
  578. ok_for_now = False
  579. if ok_for_now:
  580. request.deleted = False
  581. trans.sa_session.add( request )
  582. # Undelete all the samples belonging to this request
  583. for s in request.samples:
  584. s.deleted = False
  585. trans.sa_session.add( s )
  586. comment = "Sequencing request marked undeleted by %s." % trans.user.email
  587. event = trans.model.RequestEvent( request, request.state, comment )
  588. trans.sa_session.add( event )
  589. trans.sa_session.flush()
  590. num_undeleted += 1
  591. message += '%i requests have been undeleted.' % num_undeleted
  592. return trans.response.send_redirect( web.url_for( controller=cntrller,
  593. action='browse_requests',
  594. status=status,
  595. message=message ) )
  596. @web.expose
  597. @web.require_login( "sequencing request history" )
  598. def view_request_history( self, trans, cntrller, **kwd ):
  599. params = util.Params( kwd )
  600. request_id = params.get( 'id', None )
  601. try:
  602. request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
  603. except:
  604. return invalid_id_redirect( trans, cntrller, request_id )
  605. return trans.fill_template( '/requests/common/view_request_history.mako',
  606. cntrller=cntrller,
  607. request=request )
  608. @web.expose
  609. @web.require_login( "edit email notification settings" )
  610. def edit_email_settings( self, trans, cntrller, **kwd ):
  611. """
  612. Allow for changing the email notification settings where email is sent to a list of users
  613. whenever the request state changes to one selected for notification.
  614. """
  615. params = util.Params( kwd )
  616. message = util.restore_text( params.get( 'message', '' ) )
  617. status = params.get( 'status', 'done' )
  618. request_id = params.get( 'id', None )
  619. try:
  620. request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
  621. except:
  622. return invalid_id_redirect( trans, cntrller, request_id )
  623. email_address = CheckboxField.is_checked( params.get( 'email_address', '' ) )
  624. additional_email_addresses = params.get( 'additional_email_addresses', '' )
  625. # Get the list of checked sample state CheckBoxFields
  626. checked_sample_states = []
  627. for index, sample_state in enumerate( request.type.states ):
  628. if CheckboxField.is_checked( params.get( 'sample_state_%i' % sample_state.id, '' ) ):
  629. checked_sample_states.append( sample_state.id )
  630. if additional_email_addresses:
  631. additional_email_addresses = additional_email_addresses.split( '\r\n' )
  632. if email_address or additional_email_addresses:
  633. # The user added 1 or more email addresses
  634. email_addresses = []
  635. if email_address:
  636. email_addresses.append( request.user.email )
  637. for email_address in additional_email_addresses:
  638. email_addresses.append( util.restore_text( email_address ) )
  639. # Make sure email addresses are valid
  640. err_msg = ''
  641. for email_address in email_addresses:
  642. err_msg += validate_email( trans, email_address, check_dup=False )
  643. if err_msg:
  644. status = 'error'
  645. message += err_msg
  646. else:
  647. request.notification = dict( email=email_addresses,
  648. sample_states=checked_sample_states,
  649. body='',
  650. subject='' )
  651. else:
  652. # The user may have eliminated email addresses that were previously set
  653. request.notification = None
  654. if checked_sample_states:
  655. message = 'All sample states have been unchecked since no email addresses have been selected or entered. '
  656. trans.sa_session.add( request )
  657. trans.sa_session.flush()
  658. trans.sa_session.refresh( request )
  659. message += 'The changes made to the email notification settings have been saved.'
  660. return trans.response.send_redirect( web.url_for( controller='requests_common',
  661. action='edit_basic_request_info',
  662. cntrller=cntrller,
  663. id=request_id,
  664. message=message ,
  665. status=status ) )
  666. @web.expose
  667. @web.require_login( "update sequencing request state" )
  668. def update_request_state( self, trans, cntrller, **kwd ):
  669. params = util.Params( kwd )
  670. message = params.get( 'message', '' )
  671. status = params.get( 'status', 'done' )
  672. request_id = params.get( 'request_id', None )
  673. try:
  674. request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
  675. except:
  676. return invalid_id_redirect( trans, cntrller, request_id )
  677. # Make sure all the samples of the current request have the same state
  678. common_state = request.samples_have_common_state
  679. if not common_state:
  680. # If the current request state is complete and one of its samples moved from
  681. # the final sample state, then move the request state to In-progress
  682. if request.is_complete:
  683. message = "At least 1 sample state moved from the final sample state, so now the request's state is (%s)" % request.states.SUBMITTED
  684. event = trans.model.RequestEvent( request, request.states.SUBMITTED, message )
  685. trans.sa_session.add( event )
  686. trans.sa_session.flush()
  687. if cntrller == 'api':
  688. return 200, message
  689. else:
  690. final_state = False
  691. request_type_state = request.type.final_sample_state
  692. if common_state.id == request_type_state.id:
  693. # since all the samples are in the final state, change the request state to 'Complete'
  694. comment = "All samples of this sequencing request are in the final sample state (%s). " % request_type_state.name
  695. state = request.states.COMPLETE
  696. final_state = True
  697. else:
  698. comment = "All samples of this sequencing request are in the (%s) sample state. " % common_state.name
  699. state = request.states.SUBMITTED
  700. event = trans.model.RequestEvent( request, state, comment )
  701. trans.sa_session.add( event )
  702. trans.sa_session.flush()
  703. # See if an email notification is configured to be sent when the samples are in this state.
  704. retval = request.send_email_notification( trans, common_state, final_state )
  705. if retval:
  706. message = comment + retval
  707. else:
  708. message = comment
  709. if cntrller == 'api':
  710. return 200, message
  711. return trans.response.send_redirect( web.url_for( controller='requests_common',
  712. action='edit_samples',
  713. cntrller=cntrller,
  714. id=request_id,
  715. status=status,
  716. message=message ) )
  717. @web.expose
  718. @web.require_login( "find samples" )
  719. def find_samples( self, trans, cntrller, **kwd ):
  720. params = util.Params( kwd )
  721. message = util.restore_text( params.get( 'message', '' ) )
  722. status = params.get( 'status', 'done' )
  723. is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
  724. samples_list = []
  725. results = ''
  726. if params.get( 'find_samples_button', False ):
  727. search_string = kwd.get( 'search_box', '' )
  728. search_type = params.get( 'search_type', '' )
  729. request_states = util.listify( params.get( 'request_states', '' ) )
  730. samples = []
  731. if search_type == 'bar_code':
  732. samples = trans.sa_session.query( trans.model.Sample ) \
  733. .filter( and_( trans.model.Sample.table.c.deleted==False,
  734. func.lower( trans.model.Sample.table.c.bar_code ).like( "%" + search_string.lower() + "%" ) ) ) \
  735. .order_by( trans.model.Sample.table.c.create_time.desc() )
  736. elif search_type == 'sample name':
  737. samples = trans.sa_session.query( trans.model.Sample ) \
  738. .filter( and_( trans.model.Sample.table.c.deleted==False,
  739. func.lower( trans.model.Sample.table.c.name ).like( "%" + search_string.lower() + "%" ) ) ) \
  740. .order_by( trans.model.Sample.table.c.create_time.desc() )
  741. elif search_type == 'dataset':
  742. samples = trans.sa_session.query( trans.model.Sample ) \
  743. .filter( and_( trans.model.Sample.table.c.deleted==False,
  744. trans.model.SampleDataset.table.c.sample_id==trans.model.Sample.table.c.id,
  745. func.lower( trans.model.SampleDataset.table.c.name ).like( "%" + search_string.lower() + "%" ) ) ) \
  746. .order_by( trans.model.Sample.table.c.create_time.desc() )
  747. elif search_type == 'form value':
  748. samples = []
  749. if search_string.find('=') != -1:
  750. field_label, field_value = search_string.split('=')
  751. all_samples = trans.sa_session.query( trans.model.Sample ) \
  752. .filter( trans.model.Sample.table.c.deleted==False ) \
  753. .order_by( trans.model.Sample.table.c.create_time.desc() )
  754. for sample in all_samples:
  755. # find the field in the sample form with the given label
  756. for field in sample.request.type.sample_form.fields:
  757. if field_label == field['label']:
  758. # check if the value is equal to the value in the search string
  759. if sample.values.content[ field['name'] ] == field_value:
  760. samples.append( sample )
  761. if is_admin:
  762. for s in samples:
  763. if not s.request.deleted and s.request.state in request_states:
  764. samples_list.append( s )
  765. else:
  766. for s in samples:
  767. if s.request.user.id == trans.user.id and s.request.state in request_states and not s.request.deleted:
  768. samples_list.append( s )
  769. results = 'There are %i samples matching the search parameters.' % len( samples_list )
  770. # Build the request_states SelectField
  771. selected_value = kwd.get( 'request_states', trans.model.Request.states.SUBMITTED )
  772. states = [ v for k, v in trans.model.Request.states.items() ]
  773. request_states = build_select_field( trans,
  774. states,
  775. 'self',
  776. 'request_states',
  777. selected_value=selected_value,
  778. refresh_on_change=False,
  779. multiple=True,
  780. display='checkboxes' )
  781. # Build the search_type SelectField
  782. selected_value = kwd.get( 'search_type', 'sample name' )
  783. types = [ 'sample name', 'bar_code', 'dataset', 'form value' ]
  784. search_type = build_select_field( trans, types, 'self', 'search_type', selected_value=selected_value, refresh_on_change=False )
  785. # Build the search_box TextField
  786. search_box = TextField( 'search_box', 50, kwd.get('search_box', '' ) )
  787. return trans.fill_template( '/requests/common/find_samples.mako',
  788. cntrller=cntrller,
  789. request_states=request_states,
  790. samples=samples_list,
  791. search_type=search_type,
  792. results=results,
  793. search_box=search_box )
  794. @web.expose
  795. @web.require_login( "sample events" )
  796. def view_sample_history( self, trans, cntrller, **kwd ):
  797. params = util.Params( kwd )
  798. status = params.get( 'status', 'done' )
  799. message = util.restore_text( params.get( 'message', '' ) )
  800. sample_id = params.get( 'sample_id', None )
  801. try:
  802. sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
  803. except:
  804. return invalid_id_redirect( trans, cntrller, sample_id, 'sample' )
  805. return trans.fill_template( '/requests/common/view_sample_history.mako',
  806. cntrller=cntrller,
  807. sample=sample )
  808. @web.expose
  809. @web.require_login( "add samples" )
  810. def add_samples( self, trans, cntrller, **kwd ):
  811. params = util.Params( kwd )
  812. is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
  813. message = util.restore_text( params.get( 'message', '' ) )
  814. status = params.get( 'status', 'done' )
  815. request_id = params.get( 'id', None )
  816. try:
  817. request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
  818. except:
  819. return invalid_id_redirect( trans, cntrller, request_id )
  820. libraries = trans.app.security_agent.get_accessible_libraries( trans, request.user )
  821. # Build a list of sample widgets (based on the attributes of each sample) for display.
  822. displayable_sample_widgets = self.__get_sample_widgets( trans, request, request.samples, **kwd )
  823. if params.get( 'import_samples_button', False ):
  824. # Import sample field values from a csv file
  825. # TODO: should this be a mapper?
  826. workflows = [ w.latest_workflow for w in trans.user.stored_workflows if not w.deleted ]
  827. return self.__import_samples( trans, cntrller, request, displayable_sample_widgets, libraries, workflows, **kwd )
  828. elif params.get( 'add_sample_button', False ):
  829. return self.add_sample( trans, cntrller, request_id, **kwd )
  830. elif params.get( 'save_samples_button', False ):
  831. return self.__save_samples( trans, cntrller, request, displayable_sample_widgets, saving_new_samples=True, **kwd )
  832. request_widgets = self.__get_request_widgets( trans, request.id )
  833. sample_copy_select_field = self.__build_copy_sample_select_field( trans, displayable_sample_widgets )
  834. libraries_select_field, folders_select_field = self.__build_library_and_folder_select_fields( trans,
  835. request.user,
  836. 'sample_operation',
  837. libraries,
  838. None,
  839. **kwd )
  840. return trans.fill_template( '/requests/common/add_samples.mako',
  841. cntrller=cntrller,
  842. request=request,
  843. request_widgets=request_widgets,
  844. displayable_sample_widgets=displayable_sample_widgets,
  845. sample_copy_select_field=sample_copy_select_field,
  846. libraries=libraries,
  847. libraries_select_field=libraries_select_field,
  848. folders_select_field=folders_select_field,
  849. status=status,
  850. message=message )
  851. @web.expose
  852. @web.require_login( "add sample" )
  853. def add_sample( self, trans, cntrller, request_id, **kwd ):
  854. params = util.Params( kwd )
  855. message = util.restore_text( params.get( 'message', '' ) )
  856. status = params.get( 'status', 'done' )
  857. try:
  858. request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
  859. except:
  860. return invalid_id_redirect( trans, cntrller, request_id )
  861. is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
  862. # Get the widgets for rendering the request form
  863. request_widgets = self.__get_request_widgets( trans, request.id )
  864. displayable_sample_widgets = self.__get_sample_widgets( trans, request, request.samples, **kwd )
  865. if not displayable_sample_widgets:
  866. # Form field names are zero-based.
  867. sample_index = 0
  868. else:
  869. sample_index = len( displayable_sample_widgets )
  870. if params.get( 'add_sample_button', False ):
  871. libraries = trans.app.security_agent.get_accessible_libraries( trans, request.user )
  872. num_samples_to_add = int( params.get( 'num_sample_to_copy', 1 ) )
  873. # See if the user has selected a sample to copy.
  874. copy_sample_index = int( params.get( 'copy_sample_index', -1 ) )
  875. for index in range( num_samples_to_add ):
  876. field_values = {}
  877. if copy_sample_index != -1:
  878. # The user has selected a sample to copy.
  879. library_id = displayable_sample_widgets[ copy_sample_index][ 'library_select_field' ].get_selected( return_value=True )
  880. folder_id = displayable_sample_widgets[ copy_sample_index ][ 'folder_select_field' ].get_selected( return_value=True )
  881. name = displayable_sample_widgets[ copy_sample_index ][ 'name' ] + '_%i' % ( len( displayable_sample_widgets ) + 1 )
  882. history_id = displayable_sample_widgets[ copy_sample_index ][ 'history_select_field' ].get_selected( return_value=True )
  883. workflow_id = displayable_sample_widgets[ copy_sample_index ][ 'workflow_select_field' ][0].get_selected( return_value=True )
  884. # DBTODO Do something nicer with the workflow fieldset. Remove [0] indexing and copy mappings as well.
  885. for field_name in displayable_sample_widgets[ copy_sample_index ][ 'field_values' ]:
  886. field_values[ field_name ] = ''
  887. else:
  888. # The user has not selected a sample to copy, just adding a new generic sample.
  889. library_id = None
  890. folder_id = None
  891. history_id = None
  892. workflow_id = None
  893. name = 'Sample_%i' % ( len( displayable_sample_widgets ) + 1 )
  894. for field in request.type.sample_form.fields:
  895. field_values[ field[ 'name' ] ] = ''
  896. # Build the library_select_field and folder_select_field for the new sample being added.
  897. library_select_field, folder_select_field = self.__build_library_and_folder_select_fields( trans,
  898. user=request.user,
  899. sample_index=len( displayable_sample_widgets ),
  900. libraries=libraries,
  901. sample=None,
  902. library_id=library_id,
  903. folder_id=folder_id,
  904. **kwd )
  905. history_select_field = self.__build_history_select_field( trans=trans,
  906. user=request.user,
  907. sample_index=len( displayable_sample_widgets ),
  908. history_id=history_id,
  909. **kwd )
  910. workflow_select_field = self.__build_workflow_select_field( trans=trans,
  911. user=request.user,
  912. request=request,
  913. sample_index=len( displayable_sample_widgets ),
  914. workflow_id=workflow_id,
  915. history_id=history_id,
  916. **kwd )
  917. # Append the new sample to the current list of samples for the request
  918. displayable_sample_widgets.append( dict( id=None,
  919. name=name,
  920. bar_code='',
  921. library=None,
  922. library_id=library_id,
  923. history=None,
  924. workflow=None,
  925. history_select_field=history_select_field,
  926. workflow_select_field=workflow_select_field,
  927. folder=None,
  928. folder_id=folder_id,
  929. field_values=field_values,
  930. library_select_field=library_select_field,
  931. folder_select_field=folder_select_field ) )
  932. sample_copy_select_field = self.__build_copy_sample_select_field( trans, displayable_sample_widgets )
  933. return trans.fill_template( '/requests/common/add_samples.mako',
  934. cntrller=cntrller,
  935. request=request,
  936. request_widgets=request_widgets,
  937. displayable_sample_widgets=displayable_sample_widgets,
  938. sample_copy_select_field=sample_copy_select_field,
  939. message=message,
  940. status=status )
  941. @web.expose
  942. @web.require_login( "view request" )
  943. def view_sample( self, trans, cntrller, **kwd ):
  944. params = util.Params( kwd )
  945. is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
  946. message = util.restore_text( params.get( 'message', '' ) )
  947. status = params.get( 'status', 'done' )
  948. sample_id = params.get( 'id', None )
  949. try:
  950. sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
  951. except:
  952. return invalid_id_redirect( trans, cntrller, sample_id, 'sample' )
  953. # See if we have any associated templates
  954. widgets = sample.get_template_widgets( trans )
  955. widget_fields_have_contents = self.widget_fields_have_contents( widgets )
  956. if is_admin:
  957. external_services = sample.populate_external_services( trans = trans )
  958. else:
  959. external_services = None
  960. return trans.fill_template( '/requests/common/view_sample.mako',
  961. cntrller=cntrller,
  962. sample=sample,
  963. widgets=widgets,
  964. widget_fields_have_contents=widget_fields_have_contents,
  965. status=status,
  966. message=message,
  967. external_services=external_services )
  968. @web.expose
  969. @web.require_login( "delete sample from sequencing request" )
  970. def delete_sample( self, trans, cntrller, **kwd ):
  971. params = util.Params( kwd )
  972. status = params.get( 'status', 'done' )
  973. message = util.restore_text( params.get( 'message', '' ) )
  974. request_id = params.get( 'request_id', None )
  975. try:
  976. request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
  977. except:
  978. return invalid_id_redirect( trans, cntrller, request_id )
  979. displayable_sample_widgets = self.__get_sample_widgets( trans, request, request.samples, **kwd )
  980. sample_index = int( params.get( 'sample_id', 0 ) )
  981. sample_name = displayable_sample_widgets[sample_index]['name']
  982. sample = request.get_sample( sample_name )
  983. if sample:
  984. trans.sa_session.delete( sample.values )
  985. trans.sa_session.delete( sample )
  986. trans.sa_session.flush()
  987. message = 'Sample (%s) has been deleted.' % sample_name
  988. return trans.response.send_redirect( web.url_for( controller='requests_common',
  989. action='edit_samples',
  990. cntrller=cntrller,
  991. id=trans.security.encode_id( request.id ),
  992. status=status,
  993. message=message ) )
  994. @web.expose
  995. @web.require_login( "view data transfer page" )
  996. def view_sample_datasets( self, trans, cntrller, **kwd ):
  997. # The link on the number of selected datasets will only appear if there is at least 1 selected dataset.
  998. # If there are 0 selected datasets, there is no link, so this method will only be reached from the requests
  999. # controller if there are selected datasets.
  1000. params = util.Params( kwd )
  1001. message = util.restore_text( params.get( 'message', '' ) )
  1002. status = params.get( 'status', 'done' )
  1003. is_admin = cntrller == 'requests_admin' and trans.user_is_admin()
  1004. sample_id = params.get( 'sample_id', None )
  1005. try:
  1006. sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
  1007. except:
  1008. return invalid_id_redirect( trans, cntrller, sample_id, 'sample' )
  1009. # See if a library and folder have been set for this sample.
  1010. if is_admin and not sample.library or not sample.folder:
  1011. status = 'error'
  1012. message = "Select a target data library and folder for the sample before selecting the datasets."
  1013. return trans.response.send_redirect( web.url_for( controller='requests_common',
  1014. action='edit_samples',
  1015. cntrller=cntrller,
  1016. id=trans.security.encode_id( sample.request.id ),
  1017. status=status,
  1018. message=message ) )
  1019. transfer_status = params.get( 'transfer_status', None )
  1020. if transfer_status in [ None, 'None' ]:
  1021. title = 'All selected datasets for "%s"' % sample.name
  1022. sample_datasets = sample.datasets
  1023. elif transfer_status == trans.model.SampleDataset.transfer_status.IN_QUEUE:
  1024. title = 'Datasets of "%s" that are in the transfer queue' % sample.name
  1025. sample_datasets = sample.queued_dataset_files
  1026. elif transfer_status == trans.model.SampleDataset.transfer_status.TRANSFERRING:
  1027. title = 'Datasets of "%s" that are being transferred' % sample.name
  1028. sample_datasets = sample.transferring_dataset_files
  1029. elif transfer_status == trans.model.SampleDataset.transfer_status.ADD_TO_LIBRARY:
  1030. title = 'Datasets of "%s" that are being added to the target data library' % sample.name
  1031. sample_datasets = sample.adding_to_library_dataset_files
  1032. elif transfer_status == trans.model.SampleDataset.transfer_status.COMPLETE:
  1033. title = 'Datasets of "%s" that are available in the target data library' % sample.name
  1034. sample_datasets = sample.transferred_dataset_files
  1035. elif transfer_status == trans.model.SampleDataset.transfer_status.ERROR:
  1036. title = 'Datasets of "%s" that resulted in a transfer error' % sample.name
  1037. sample_datasets = sample.transfer_error_dataset_files
  1038. return trans.fill_template( '/requests/common/view_sample_datasets.mako',
  1039. cntrller=cntrller,
  1040. title=title,
  1041. sample=sample,
  1042. sample_datasets=sample_datasets,
  1043. transfer_status=transfer_status,
  1044. message=message,
  1045. status=status )
  1046. def __import_samples( self, trans, cntrller, request, displayable