root/galaxy-central/lib/galaxy/web/controllers/requests_admin.py @ 2

リビジョン 2, 54.6 KB (コミッタ: hatakeyama, 14 年 前)

import galaxy-central

行番号 
1from galaxy.web.base.controller import *
2from galaxy.web.framework.helpers import time_ago, iff, grids
3from galaxy.model.orm import *
4from galaxy import model, util
5from galaxy.web.form_builder import *
6from galaxy.web.controllers.requests_common import RequestsGrid, invalid_id_redirect
7from amqplib import client_0_8 as amqp
8import logging, os, pexpect, ConfigParser
9
10log = logging.getLogger( __name__ )
11
12
13
14class AdminRequestsGrid( RequestsGrid ):
15    class UserColumn( grids.TextColumn ):
16        def get_value( self, trans, grid, request ):
17            return request.user.email
18    # Grid definition
19    columns = [ col for col in RequestsGrid.columns ]
20    columns.append( UserColumn( "User",
21                                model_class=model.User,
22                                key='username' ) )
23    operations = [ operation for operation in RequestsGrid.operations ]
24    operations.append( grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ) )
25    operations.append( grids.GridOperation( "Reject", allow_multiple=False, condition=( lambda item: not item.deleted and item.is_submitted ) ) )
26    operations.append( grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted ) ) )
27    operations.append( grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ) )
28    operations.append( grids.GridOperation( "Purge",
29                                            allow_multiple=False,
30                                            confirm="This will permanently delete this sequencing request. Click OK to proceed.",
31                                            condition=( lambda item: item.deleted ) ) )
32    global_actions = [
33        grids.GridAction( "Create new request", dict( controller='requests_common',
34                                                      action='create_request',
35                                                      cntrller='requests_admin' ) )
36    ]
37
38class RequestTypeGrid( grids.Grid ):
39    # Custom column types
40    class NameColumn( grids.TextColumn ):
41        def get_value(self, trans, grid, request_type):
42            return request_type.name
43    class DescriptionColumn( grids.TextColumn ):
44        def get_value(self, trans, grid, request_type):
45            return request_type.desc
46    class RequestFormColumn( grids.TextColumn ):
47        def get_value(self, trans, grid, request_type):
48            return request_type.request_form.name
49    class SampleFormColumn( grids.TextColumn ):
50        def get_value(self, trans, grid, request_type):
51            return request_type.sample_form.name
52
53    # Grid definition
54    title = "Sequencer Configurations"
55    template = "admin/requests/grid.mako"
56    model_class = model.RequestType
57    default_sort_key = "-create_time"
58    num_rows_per_page = 50
59    preserve_state = True
60    use_paging = True
61    default_filter = dict( deleted="False" )
62    columns = [
63        NameColumn( "Name",
64                    key="name",
65                    link=( lambda item: iff( item.deleted, None, dict( operation="view", id=item.id ) ) ),
66                    attach_popup=True,
67                    filterable="advanced" ),
68        DescriptionColumn( "Description",
69                           key='desc',
70                           filterable="advanced" ),
71        RequestFormColumn( "Request Form",
72                           link=( lambda item: iff( item.deleted, None, dict( operation="view_form_definition", id=item.request_form.id ) ) ) ),
73        SampleFormColumn( "Sample Form",
74                           link=( lambda item: iff( item.deleted, None, dict( operation="view_form_definition", id=item.sample_form.id ) ) ) ),
75        grids.DeletedColumn( "Deleted",
76                             key="deleted",
77                             visible=False,
78                             filterable="advanced" )
79    ]
80    columns.append( grids.MulticolFilterColumn( "Search",
81                                                cols_to_filter=[ columns[0], columns[1] ],
82                                                key="free-text-search",
83                                                visible=False,
84                                                filterable="standard" ) )
85    operations = [
86        grids.GridOperation( "Permissions", allow_multiple=False, condition=( lambda item: not item.deleted  )  ),
87        grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted  )  ),
88        grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ),   
89    ]
90    global_actions = [
91        grids.GridAction( "Create new sequencer configuration", dict( controller='requests_admin', action='create_request_type' ) )
92    ]
93
94class DataTransferGrid( grids.Grid ):
95    # Custom column types
96    class NameColumn( grids.TextColumn ):
97        def get_value( self, trans, grid, sample_dataset ):
98            return sample_dataset.name
99    class SizeColumn( grids.TextColumn ):
100        def get_value( self, trans, grid, sample_dataset ):
101            return sample_dataset.size
102    class StatusColumn( grids.TextColumn ):
103        def get_value( self, trans, grid, sample_dataset ):
104            return sample_dataset.status
105    # Grid definition
106    title = "Sample Datasets"
107    template = "admin/requests/grid.mako"
108    model_class = model.SampleDataset
109    default_sort_key = "-create_time"
110    num_rows_per_page = 50
111    preserve_state = True
112    use_paging = True
113    columns = [
114        NameColumn( "Name",
115                    link=( lambda item: dict( operation="view", id=item.id ) ),
116                    attach_popup=True,
117                    filterable="advanced" ),
118        SizeColumn( "Size",
119                    filterable="advanced" ),
120        grids.GridColumn( "Last Updated",
121                          key="update_time",
122                          format=time_ago ),
123        StatusColumn( "Status",
124                      filterable="advanced" ),
125    ]
126    columns.append( grids.MulticolFilterColumn( "Search",
127                                                cols_to_filter=[ columns[0] ],
128                                                key="free-text-search",
129                                                visible=False,
130                                                filterable="standard" ) )
131    operations = [
132        grids.GridOperation( "Start Transfer", allow_multiple=True, condition=( lambda item: item.status in [ model.Sample.transfer_status.NOT_STARTED ] ) ),
133        grids.GridOperation( "Rename", allow_multiple=True, allow_popup=False, condition=( lambda item: item.status in [ model.Sample.transfer_status.NOT_STARTED ] ) ),
134        grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.status in [ model.Sample.transfer_status.NOT_STARTED ] )  ),
135    ]
136    def apply_query_filter( self, trans, query, **kwd ):
137        sample_id = kwd.get( 'sample_id', None )
138        if not sample_id:
139            return query
140        return query.filter_by( sample_id=trans.security.decode_id( sample_id ) )
141
142class RequestsAdmin( BaseController, UsesFormDefinitionWidgets ):
143    request_grid = AdminRequestsGrid()
144    requesttype_grid = RequestTypeGrid()
145    datatx_grid = DataTransferGrid()
146
147    @web.expose
148    @web.require_admin
149    def index( self, trans ):
150        return trans.fill_template( "/admin/requests/index.mako" )
151    @web.expose
152    @web.require_admin
153    def browse_requests( self, trans, **kwd ):
154        if 'operation' in kwd:
155            operation = kwd['operation'].lower()
156            if operation == "edit":
157                return trans.response.send_redirect( web.url_for( controller='requests_common',
158                                                                  action='edit_basic_request_info',
159                                                                  cntrller='requests_admin',
160                                                                  **kwd ) )
161            if operation == "manage_request":
162                return trans.response.send_redirect( web.url_for( controller='requests_common',
163                                                                  action='manage_request',
164                                                                  cntrller='requests_admin',
165                                                                  **kwd ) )
166            if operation == "request_events":
167                return trans.response.send_redirect( web.url_for( controller='requests_common',
168                                                                  action='request_events',
169                                                                  cntrller='requests_admin',
170                                                                  **kwd ) )
171            if operation == "reject":
172                return self.reject_request( trans, **kwd )
173            if operation == "view_type":
174                return self.view_request_type( trans, **kwd )
175            if operation == "delete":
176                return trans.response.send_redirect( web.url_for( controller='requests_common',
177                                                                  action='delete_request',
178                                                                  cntrller='requests_admin',
179                                                                  **kwd ) )
180            if operation == "undelete":
181                return trans.response.send_redirect( web.url_for( controller='requests_common',
182                                                                  action='undelete_request',
183                                                                  cntrller='requests_admin',
184                                                                  **kwd ) )
185        # Render the list view
186        return self.request_grid( trans, **kwd )
187    @web.expose
188    @web.require_admin
189    def reject( self, trans, **kwd ):
190        params = util.Params( kwd )
191        request_id = params.get( 'id', '' )
192        status = params.get( 'status', 'done' )
193        message = params.get( 'message', 'done' )
194        if params.get( 'cancel_reject_button', False ):
195            return trans.response.send_redirect( web.url_for( controller='requests_common',
196                                                              action='manage_request',
197                                                              cntrller='requests_admin',
198                                                              id=request_id ) )
199        try:
200            request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
201        except:
202            return invalid_id_redirect( trans, 'requests_admin', request_id )
203        # Validate
204        comment = util.restore_text( params.get( 'comment', '' ) )
205        if not comment:
206            status='error'
207            message='A reason for rejecting the request is required.'
208            return trans.fill_template( '/admin/requests/reject.mako',
209                                        cntrller='requests_admin',
210                                        request=request,
211                                        status=status,
212                                        message=message )
213        # Create an event with state 'Rejected' for this request
214        event = trans.model.RequestEvent( request, request.states.REJECTED, comment )
215        trans.sa_session.add( event )
216        trans.sa_session.flush()
217        message='Request (%s) has been rejected.' % request.name
218        return trans.response.send_redirect( web.url_for( controller='requests_admin',
219                                                          action='browse_requests',
220                                                          status=status,
221                                                          message=message,
222                                                          **kwd ) )
223    # Data transfer from sequencer
224    @web.expose
225    @web.require_admin
226    def manage_datasets( self, trans, **kwd ):
227        params = util.Params( kwd )
228        message = util.restore_text( params.get( 'message', ''  ) )
229        status = params.get( 'status', 'done' )
230        if 'operation' in kwd:
231            operation = kwd[ 'operation' ].lower()
232            sample_dataset_id = params.get( 'id', None )
233            if not sample_dataset_id:
234                return invalid_id_redirect( trans, 'requests_admin', sample_dataset_id )
235            id_list = util.listify( sample_dataset_id )
236            selected_sample_datasets = []
237            for sample_dataset_id in id_list:
238                try:
239                    selected_sample_datasets.append( trans.sa_session.query( trans.model.SampleDataset ).get( trans.security.decode_id( sample_dataset_id ) ) )
240                except:
241                    return invalid_id_redirect( trans, 'requests_admin', sample_dataset_id )
242            if operation == "view":
243                return trans.fill_template( '/admin/requests/dataset.mako',
244                                            sample_dataset=selected_sample_datasets[0] )
245            elif operation == "delete":
246                not_deleted = []
247                for sample_dataset in selected_sample_datasets:
248                    # Make sure the dataset has been transferred before deleting it.
249                    if sample_dataset in sample_dataset.sample.untransferred_dataset_files:
250                        # save the sample to which these datasets belong to
251                        sample = sample_dataset.sample
252                        trans.sa_session.delete( sample_dataset )
253                        trans.sa_session.flush()
254                    else:
255                        not_deleted.append( sample_dataset.name )
256                message = '%i datasets have been deleted.' % ( len( id_list ) - len( not_deleted ) )
257                if not_deleted:
258                    status = 'warning'
259                    message = message + '  %s could not be deleted because their transfer status is not "Not Started". ' % str( not_deleted )
260                return trans.response.send_redirect( web.url_for( controller='requests_admin',
261                                                                  action='manage_datasets',
262                                                                  sample_id=trans.security.encode_id( sample.id ),
263                                                                  status=status,
264                                                                  message=message ) )
265            elif operation == "rename":
266                # If one of the selected sample datasets is in the NOT_STARTED state,
267                # then display an error message.  A NOT_STARTED state implies the dataset
268                # has not yet been transferred.
269                no_datasets_transferred = True
270                for selected_sample_dataset in selected_sample_datasets:
271                    if selected_sample_dataset in selected_sample_dataset.sample.untransferred_dataset_files:
272                        no_datasets_transferred = False
273                        break
274                if no_datasets_transferred:
275                    status = 'error'
276                    message = 'A dataset can be renamed only if it is in the "Not Started" state.'
277                    return trans.response.send_redirect( web.url_for( controller='requests_admin',
278                                                                      action='manage_datasets',
279                                                                      sample_id=trans.security.encode_id( selected_sample_datasets[0].sample.id ),
280                                                                      status=status,
281                                                                      message=message ) )
282                return trans.fill_template( '/admin/requests/rename_datasets.mako',
283                                            sample=selected_sample_datasets[0].sample,
284                                            id_list=id_list )
285            elif operation == "start transfer":
286                self.__start_datatx( trans, selected_sample_datasets[0].sample, selected_sample_datasets )
287        # Render the grid view
288        sample_id = params.get( 'sample_id', None )
289        try:
290            sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id ( sample_id ) )
291        except:
292            return invalid_id_redirect( trans, 'requests_admin', sample_id )
293        request_id = trans.security.encode_id( sample.request.id )
294        library_id = trans.security.encode_id( sample.library.id )
295        self.datatx_grid.title = 'Datasets of sample "%s"' % sample.name
296        self.datatx_grid.global_actions = [ grids.GridAction( "Refresh",
297                                                              dict( controller='requests_admin',
298                                                                    action='manage_datasets',
299                                                                    sample_id=sample_id ) ),
300                                            grids.GridAction( "Select datasets",
301                                                              dict( controller='requests_admin',
302                                                                    action='get_data',
303                                                                    request_id=request_id,
304                                                                    folder_path=sample.request.type.datatx_info[ 'data_dir' ],
305                                                                    sample_id=sample_id ) ),
306                                            grids.GridAction( 'Data library "%s"' % sample.library.name,
307                                                              dict( controller='library_common',
308                                                                    action='browse_library',
309                                                                    cntrller='library_admin',
310                                                                    id=library_id ) ),
311                                            grids.GridAction( "Browse this request",
312                                                              dict( controller='requests_common',
313                                                                    action='manage_request',
314                                                                    cntrller='requests_admin',
315                                                                    id=request_id ) ) ]
316        return self.datatx_grid( trans, **kwd )
317    @web.expose
318    @web.require_admin
319    def rename_datasets( self, trans, **kwd ):
320        # This method is called from the DataTransferGrid when a user is renaming 1 or more
321        # SampleDatasets.
322        params = util.Params( kwd )
323        message = util.restore_text( params.get( 'message', ''  ) )
324        status = params.get( 'status', 'done' )
325        sample_id = kwd.get( 'sample_id', None )
326        try:
327            sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( sample_id ) )
328        except:
329            return invalid_id_redirect( trans, 'requests_admin', sample_id )
330        # id_list is list of SampleDataset ids, which is a subset of all
331        # of the SampleDatasets associated with the Sample.  The user may
332        # or may not have selected all of the SampleDatasets for renaming.
333        id_list = util.listify( kwd.get( 'id_list', [] ) )
334        # Get all of the SampleDatasets
335        sample_datasets = []
336        for sample_dataset_id in id_list:
337            sample_dataset = trans.sa_session.query( trans.app.model.SampleDataset ).get( trans.security.decode_id( sample_dataset_id ) )
338            sample_datasets.append( sample_dataset )
339        if params.get( 'rename_datasets_button', False ):
340            for sample_dataset in sample_datasets:
341                encoded_id = trans.security.encode_id( sample_dataset.id )
342                selected_option = util.restore_text( params.get( 'rename_datasets_for_sample_%s' % encoded_id, '' ) )
343                new_name = util.restore_text( params.get( 'new_name_%s' % encoded_id, '' ) )
344                if selected_option == 'none':
345                    sample_dataset.name = new_name
346                else:
347                    sample_dataset.name = '%s_%s' % ( selected_option, new_name )
348                trans.sa_session.add( sample_dataset )
349                trans.sa_session.flush()
350            message = 'Changes saved successfully.'
351            return trans.fill_template( '/admin/requests/rename_datasets.mako',
352                                        sample=sample,
353                                        id_list=id_list,
354                                        message=message,
355                                        status=status )
356        return trans.response.send_redirect( web.url_for( controller='requests_admin',
357                                                          action='manage_datasets',
358                                                          sample_id=sample_id ) )
359    @web.expose
360    @web.require_admin
361    def get_data( self, trans, **kwd ):
362        params = util.Params( kwd )
363        message = util.restore_text( params.get( 'message', '' ) )
364        status = params.get( 'status', 'done' )
365        request_id = kwd.get( 'request_id', None )
366        files = []
367        try:
368            request = trans.sa_session.query( trans.model.Request ).get( trans.security.decode_id( request_id ) )
369        except:
370            return invalid_id_redirect( trans, 'requests_admin', request_id )
371        selected_files = util.listify( params.get( 'files_list', [] ) )
372        folder_path = util.restore_text( params.get( 'folder_path', request.type.datatx_info[ 'data_dir' ] ) )
373        selected_sample_id = kwd.get( 'sample_id', 'none' )
374        sample_id_select_field = self.__build_sample_id_select_field( trans, request, selected_sample_id )
375        # The __get_files() method redirects here with a status of 'error' and a message if there
376        # was a problem retrieving the files.
377        if folder_path and status != 'error':
378            folder_path = self.__check_path( folder_path )
379            if params.get( 'folder_up', False ):
380                if folder_path[-1] == os.sep:
381                    folder_path = os.path.dirname( folder_path[:-1] )
382                folder_path = self.__check_path( folder_path )
383            elif params.get( 'open_folder', False ):
384                if len(selected_files) == 1:
385                    folder_path = os.path.join(folder_path, selected_files[0])
386                folder_path = self.__check_path( folder_path )
387            elif params.get( 'select_show_datasets_button', False ) or params.get( 'select_more_button', False ):
388                # get the sample these datasets are associated with
389                try:
390                    sample = trans.sa_session.query( trans.model.Sample ).get( trans.security.decode_id( selected_sample_id ) )
391                except:
392                    return invalid_id_redirect( trans, 'requests_admin', selected_sample_id )
393                if sample in sample.request.samples_without_library_destinations:
394                    # Display an error if a sample has been selected that
395                    # has not yet been associated with a destination library.
396                    status = 'error'
397                    message = 'Select a sample with associated data library and folder before selecting the datasets.'
398                    return trans.response.send_redirect( web.url_for( controller='requests_admin',
399                                                                      action='get_data',
400                                                                      request_id=request_id,
401                                                                      folder_path=folder_path,
402                                                                      status=status,
403                                                                      message=message ) )
404                # Save the sample datasets
405                sample_dataset_file_names = self.__save_sample_datasets( trans, sample, selected_files, folder_path )
406                if sample_dataset_file_names:
407                    message = 'Datasets (%s) have been selected for sample (%s)' % \
408                        ( str( sample_dataset_file_names )[1:-1].replace( "'", "" ), sample.name )
409                if params.get( 'select_show_datasets_button', False ):
410                    return trans.response.send_redirect( web.url_for( controller='requests_admin',
411                                                                      action='manage_datasets',
412                                                                      request_id=request_id,
413                                                                      sample_id=selected_sample_id,
414                                                                      message=message,
415                                                                      status=status ) )
416                else: # 'select_more_button' was clicked
417                    return trans.response.send_redirect( web.url_for( controller='requests_admin',
418                                                                      action='get_data',
419                                                                      request_id=request_id,
420                                                                      folder_path=folder_path,
421                                                                      sample_id=sample.id,
422                                                                      message=message,
423                                                                      status=status ) )
424            # Get the filenames from the remote host
425            files = self.__get_files( trans, request, folder_path )
426        return trans.fill_template( '/admin/requests/get_data.mako',
427                                    cntrller='requests_admin',
428                                    request=request,
429                                    sample_id_select_field=sample_id_select_field,
430                                    files=files,
431                                    folder_path=folder_path,
432                                    status=status,
433                                    message=message )
434    @web.json
435    def get_file_details( self, trans, id, folder_path ):
436        def print_ticks( d ):
437            # pexpect timeout method
438            pass
439        # Avoid caching
440        trans.response.headers['Pragma'] = 'no-cache'
441        trans.response.headers['Expires'] = '0'
442        request = trans.sa_session.query( trans.model.Request ).get( int( id ) )
443        datatx_info = request.type.datatx_info
444        cmd  = 'ssh %s@%s "ls -oghp \'%s\'"' % ( datatx_info['username'],
445                                                 datatx_info['host'],
446                                                 folder_path )
447        output = pexpect.run( cmd,
448                              events={ '.ssword:*' : datatx_info[ 'password'] + '\r\n', pexpect.TIMEOUT : print_ticks },
449                              timeout=10 )
450        return unicode( output.replace( '\n', '<br/>' ) )
451    @web.json
452    def open_folder( self, trans, id, folder_path ):
453        def print_ticks( d ):
454            # pexpect timeout method
455            pass
456        # Avoid caching
457        trans.response.headers['Pragma'] = 'no-cache'
458        trans.response.headers['Expires'] = '0'
459        request = trans.sa_session.query( trans.model.Request ).get( int( id ) )
460        return self.__get_files( trans, request, folder_path )
461    def __get_files( self, trans, request, folder_path ):
462        # Retrieves the filenames to be transferred from the remote host.
463        ok = True
464        datatx_info = request.type.datatx_info
465        if not datatx_info[ 'host' ] or not datatx_info[ 'username' ] or not datatx_info[ 'password' ]:
466            status = 'error'
467            message = "Error in sequencer login information."
468            ok = False
469        def print_ticks( d ):
470            pass
471        cmd  = 'ssh %s@%s "ls -p \'%s\'"' % ( datatx_info['username'], datatx_info['host'], folder_path )
472        output = pexpect.run( cmd,
473                              events={ '.ssword:*' : datatx_info['password'] + '\r\n', pexpect.TIMEOUT : print_ticks },
474                              timeout=10 )
475        if 'No such file or directory' in output:
476            status = 'error'
477            message = "No folder named (%s) exists on the sequencer." % folder_path
478            ok = False
479        if ok:
480            return output.splitlines()
481        return trans.response.send_redirect( web.url_for( controller='requests_admin',
482                                                          action='get_data',
483                                                          request_id=trans.security.encode_id( request.id ),
484                                                          folder_path=folder_path,
485                                                          status=status,
486                                                          message=message ) )
487    def __check_path( self, a_path ):
488        # Return a valid folder_path
489        if a_path and not a_path.endswith( os.sep ):
490            a_path += os.sep
491        return a_path
492    def __save_sample_datasets( self, trans, sample, selected_files, folder_path ):
493        sample_dataset_file_names = []
494        if selected_files:
495            for f in selected_files:
496                filepath = os.path.join( folder_path, f )
497                if f[-1] == os.sep:
498                    # FIXME: The selected item is a folder so transfer all the folder contents
499                    request_id = trans.security.ecnode_id( sample.request.id )
500                    return trans.response.send_redirect( web.url_for( controller='requests_admin',
501                                                                      action='get_data',
502                                                                      request_id=request_id,
503                                                                      folder_path=folder_path,
504                                                                      open_folder=True ) )
505                else:
506                    name = self.__dataset_name( sample, filepath.split( '/' )[-1] )
507                    sample_dataset = trans.model.SampleDataset( sample=sample,
508                                                                file_path=filepath,
509                                                                status=sample.transfer_status.NOT_STARTED,
510                                                                name=name,
511                                                                error_msg='',
512                                                                size=sample.dataset_size( filepath ) )
513                    trans.sa_session.add( sample_dataset )
514                    trans.sa_session.flush()
515                    sample_dataset_file_names.append( str( sample_dataset.name ) )
516        return sample_dataset_file_names
517    def __dataset_name( self, sample, filepath ):
518        name = filepath.split( '/' )[-1]
519        options = sample.request.type.rename_dataset_options
520        option = sample.request.type.datatx_info.get( 'rename_dataset', options.NO )
521        if option == options.NO:
522            return name
523        if option == options.SAMPLE_NAME:
524            return sample.name + '_' + name
525        if option == options.EXPERIMENT_AND_SAMPLE_NAME:
526            return sample.request.name + '_' + sample.name + '_' + name
527        if opt == options.EXPERIMENT_NAME:
528            return sample.request.name + '_' + name
529    def __setup_datatx_user( self, trans, library, folder ):
530        """
531        Sets up the datatx user:
532        - Checks if the user exists, if not creates them.
533        - Checks if the user had ADD_LIBRARY permission on the target library
534          and the target folder, if not sets up the permissions.
535        """
536        # Retrieve the upload user login information from the config file
537        config = ConfigParser.ConfigParser()
538        config.read( 'transfer_datasets.ini' )
539        email = config.get( "data_transfer_user_login_info", "email" )
540        password = config.get( "data_transfer_user_login_info", "password" )
541        # check if the user already exists
542        datatx_user = trans.sa_session.query( trans.model.User ) \
543                                      .filter( trans.model.User.table.c.email==email ) \
544                                      .first()
545        if not datatx_user:
546            # if not create the user
547            datatx_user = trans.model.User( email=email, password=passsword )
548            if trans.app.config.use_remote_user:
549                datatx_user.external = True
550            trans.sa_session.add( datatx_user )
551            trans.sa_session.flush()
552            trans.app.security_agent.create_private_user_role( datatx_user )
553            trans.app.security_agent.user_set_default_permissions( datatx_user, history=False, dataset=False )
554        datatx_user_roles = datatx_user.all_roles()
555        datatx_user_private_role = trans.app.security_agent.get_private_user_role( datatx_user )
556        # Make sure this user has LIBRARY_ADD permissions on the target library and folder.
557        # If not, give them permission.
558        if not trans.app.security_agent.can_add_library_item( datatx_user_roles, library ):
559            lp = trans.model.LibraryPermissions( trans.app.security_agent.permitted_actions.LIBRARY_ADD.action,
560                                                 library,
561                                                 datatx_user_private_role )
562            trans.sa_session.add( lp )
563        if not trans.app.security_agent.can_add_library_item( datatx_user_roles, folder ):
564            lfp = trans.model.LibraryFolderPermissions( trans.app.security_agent.permitted_actions.LIBRARY_ADD.action,
565                                                        folder,
566                                                        datatx_user_private_role )
567            trans.sa_session.add( lfp )
568            trans.sa_session.flush()
569        return datatx_user
570    def __send_message( self, trans, datatx_info, sample, selected_sample_datasets ):
571        """Ceates an xml message and sends it to the rabbitmq server"""
572        # Create the xml message based on the following template
573        xml = \
574            ''' <data_transfer>
575                    <data_host>%(DATA_HOST)s</data_host>
576                    <data_user>%(DATA_USER)s</data_user>
577                    <data_password>%(DATA_PASSWORD)s</data_password>
578                    <sample_id>%(SAMPLE_ID)s</sample_id>
579                    <library_id>%(LIBRARY_ID)s</library_id>
580                    <folder_id>%(FOLDER_ID)s</folder_id>
581                    %(DATASETS)s
582                </data_transfer>'''
583        dataset_xml = \
584            '''<dataset>
585                   <dataset_id>%(ID)s</dataset_id>
586                   <name>%(NAME)s</name>
587                   <file>%(FILE)s</file>
588               </dataset>'''
589        datasets = ''
590        for sample_dataset in selected_sample_datasets:
591            if sample_dataset.status == sample.transfer_status.NOT_STARTED:
592                datasets = datasets + dataset_xml % dict( ID=str( sample_dataset.id ),
593                                                          NAME=sample_dataset.name,
594                                                          FILE=sample_dataset.file_path )
595                sample_dataset.status = sample.transfer_status.IN_QUEUE
596                trans.sa_session.add( sample_dataset )
597                trans.sa_session.flush()
598        data = xml % dict( DATA_HOST=datatx_info['host'],
599                           DATA_USER=datatx_info['username'],
600                           DATA_PASSWORD=datatx_info['password'],
601                           SAMPLE_ID=str(sample.id),
602                           LIBRARY_ID=str(sample.library.id),
603                           FOLDER_ID=str(sample.folder.id),
604                           DATASETS=datasets )
605        # Send the message
606        try:
607            conn = amqp.Connection( host=trans.app.config.amqp['host'] + ":" + trans.app.config.amqp['port'],
608                                    userid=trans.app.config.amqp['userid'],
609                                    password=trans.app.config.amqp['password'],
610                                    virtual_host=trans.app.config.amqp['virtual_host'],
611                                    insist=False )   
612            chan = conn.channel()
613            msg = amqp.Message( data.replace( '\n', '' ).replace( '\r', '' ),
614                                content_type='text/plain',
615                                application_headers={'msg_type': 'data_transfer'} )
616            msg.properties["delivery_mode"] = 2
617            chan.basic_publish( msg,
618                                exchange=trans.app.config.amqp['exchange'],
619                                routing_key=trans.app.config.amqp['routing_key'] )
620            chan.close()
621            conn.close()
622        except Exception, e:
623            message = "Error in sending the data transfer message to the Galaxy AMQP message queue:<br/>%s" % str(e)
624            status = "error"
625            return trans.response.send_redirect( web.url_for( controller='requests_admin',
626                                                              action='manage_datasets',
627                                                              sample_id=trans.security.encode_id( sample.id ),
628                                                              status=status,
629                                                              message=message) )
630
631    def __start_datatx( self, trans, sample, selected_sample_datasets ):
632        datatx_user = self.__setup_datatx_user( trans, sample.library, sample.folder )
633        # Validate sequencer information
634        datatx_info = sample.request.type.datatx_info
635        if not datatx_info['host'] or not datatx_info['username'] or not datatx_info['password']:
636            message = "Error in sequencer login information."
637            status = "error"
638        else:
639            self.__send_message( trans, datatx_info, sample, selected_sample_datasets )
640            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 )
641            status = "done"
642        return trans.response.send_redirect( web.url_for( controller='requests_admin',
643                                                          action='manage_datasets',
644                                                          sample_id=trans.security.encode_id( sample.id ),
645                                                          status=status,
646                                                          message=message) )
647    # Request Type Stuff
648    @web.expose
649    @web.require_admin
650    def browse_request_types( self, trans, **kwd ):
651        if 'operation' in kwd:
652            operation = kwd['operation'].lower()
653            obj_id = kwd.get( 'id', None )
654            if operation == "view_form_definition":
655                return self.view_form_definition( trans, **kwd )
656            elif operation == "view":
657                return self.view_request_type( trans, **kwd )
658            elif operation == "delete":
659                return self.delete_request_type( trans, **kwd )
660            elif operation == "undelete":
661                return self.undelete_request_type( trans, **kwd )
662            elif operation == "permissions":
663                return self.request_type_permissions( trans, **kwd )
664        # Render the grid view
665        return self.requesttype_grid( trans, **kwd )
666    @web.expose
667    @web.require_admin
668    def create_request_type( self, trans, **kwd ):
669        params = util.Params( kwd )
670        message = util.restore_text( params.get( 'message', ''  ) )
671        status = params.get( 'status', 'done' )
672        rt_info_widgets, rt_states_widgets = self.__create_request_type_form( trans, **kwd )
673        rename_dataset_select_field = self.__build_rename_dataset_select_field( trans )
674        if params.get( 'add_state_button', False ):
675            # FIXME: why do we append a tuple of 2 empty strings????
676            rt_states_widgets.append( ( "", "" ) )
677        elif params.get( 'remove_state_button', False ):
678            index = int( params.get( 'remove_state_button', '' ).split(" ")[2] )
679            del rt_states_widgets[ index-1 ]
680        elif params.get( 'save_request_type', False ):
681            request_type, message = self.__save_request_type( trans, **kwd )
682            if not request_type:
683                status='error'
684            else:
685                message = 'Sequencer configuration (%s) has been created' % request_type.name
686                return trans.response.send_redirect( web.url_for( controller='requests_admin',
687                                                                  action='browse_request_types',
688                                                                  message=message,
689                                                                  status=status ) )
690        elif params.get( 'save_changes', False ):
691            request_type_id = params.get( 'rt_id', None )
692            try:
693                request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
694            except:
695                return invalid_id_redirect( trans, 'requests_admin', request_type_id, action='browse_request_types' )
696            # Data transfer info - make sure password is retrieved from kwd rather
697            # than Params since Params may have munged the characters.
698            request_type.datatx_info = dict( host=util.restore_text( params.get( 'host', '' ) ),
699                                             username=util.restore_text( params.get( 'username', '' ) ),
700                                             password=kwd.get( 'password', '' ),
701                                             data_dir=util.restore_text( params.get( 'data_dir', '' ) ),
702                                             rename_dataset=util.restore_text( params.get( 'rename_dataset', False ) ) )
703            data_dir = self.__check_path( request_type.datatx_info[ 'data_dir' ] )
704            request_type.datatx_info[ 'data_dir' ] = data_dir
705            trans.sa_session.add( request_type )
706            trans.sa_session.flush()
707            message = 'Changes made to sequencer configuration <b>%s</b> has been saved' % request_type.name
708            return trans.response.send_redirect( web.url_for( controller='requests_admin',
709                                                              action='view_request_type',
710                                                              id=request_type_id,
711                                                              message=message,
712                                                              status=status ) )
713        return trans.fill_template( '/admin/requests/create_request_type.mako',
714                                    rt_info_widgets=rt_info_widgets,
715                                    rt_states_widgets=rt_states_widgets,
716                                    rename_dataset_select_field=rename_dataset_select_field,
717                                    message=message,
718                                    status=status )
719    def __create_request_type_form( self, trans, **kwd ):
720        request_form_definitions = self.get_all_forms( trans,
721                                                        filter=dict( deleted=False ),
722                                                        form_type=trans.model.FormDefinition.types.REQUEST )
723        sample_form_definitions = self.get_all_forms( trans,
724                                                      filter=dict( deleted=False ),
725                                                      form_type=trans.model.FormDefinition.types.SAMPLE )
726        if not request_form_definitions or not sample_form_definitions:
727            return [],[]
728        params = util.Params( kwd )
729        request_form_id = params.get( 'request_form_id', 'none' )
730        sample_form_id = params.get( 'sample_form_id', 'none' )
731        request_form_id_select_field = build_select_field( trans,
732                                                           objs=request_form_definitions,
733                                                           label_attr='name',
734                                                           select_field_name='request_form_id',
735                                                           selected_value=request_form_id,
736                                                           refresh_on_change=False )
737        sample_form_id_select_field = build_select_field( trans,
738                                                           objs=sample_form_definitions,
739                                                           label_attr='name',
740                                                           select_field_name='sample_form_id',
741                                                           selected_value=sample_form_id,
742                                                           refresh_on_change=False )
743        rt_info_widgets = [ dict( label='Name',
744                                  widget=TextField( 'name', 40, util.restore_text( params.get( 'name', '' ) ) ) ),
745                            dict( label='Description',
746                                  widget=TextField( 'desc', 40, util.restore_text( params.get( 'desc', '' ) ) ) ),
747                            dict( label='Request form',
748                                  widget=request_form_id_select_field ),
749                            dict( label='Sample form',
750                                  widget=sample_form_id_select_field ) ]
751        # Possible sample states
752        rt_states = []
753        i=0
754        while True:
755            if kwd.has_key( 'state_name_%i' % i ):
756                rt_states.append( ( params.get( 'state_name_%i' % i, ''  ),
757                                    params.get( 'state_desc_%i' % i, ''  ) ) )
758                i += 1
759            else:
760                break
761        return rt_info_widgets, rt_states
762    def __save_request_type(self, trans, **kwd):
763        params = util.Params( kwd )
764        name = util.restore_text( params.get( 'name', ''  ) )
765        desc = util.restore_text( params.get( 'desc', '' ) )
766        request_form_id = params.get( 'request_form_id', None )
767        request_form = trans.sa_session.query( trans.model.FormDefinition ).get( trans.security.decode_id( request_form_id ) )
768        sample_form_id = params.get( 'sample_form_id', None )
769        sample_form = trans.sa_session.query( trans.model.FormDefinition ).get( trans.security.decode_id( sample_form_id ) )
770        data_dir = util.restore_text( params.get( 'data_dir', ''  ) )
771        data_dir = self.__check_path( data_dir )
772        # Data transfer info - Make sure password is retrieved from kwd rather than Params
773        # since Params may have munged the characters.
774        datatx_info = dict( host=util.restore_text( params.get( 'host', ''  ) ),
775                            username=util.restore_text( params.get( 'username', ''  ) ),
776                            password=kwd.get( 'password', '' ),
777                            data_dir=data_dir,
778                            rename_dataset=util.restore_text( params.get( 'rename_dataset', '' ) ) )
779        request_type = trans.model.RequestType( name=name, desc=desc, request_form=request_form, sample_form=sample_form, datatx_info=datatx_info )
780        trans.sa_session.add( request_type )
781        trans.sa_session.flush()
782        # set sample states
783        ss_list = trans.sa_session.query( trans.model.SampleState ) \
784                                  .filter( trans.model.SampleState.table.c.request_type_id == request_type.id )
785        for ss in ss_list:
786            trans.sa_session.delete( ss )
787            trans.sa_session.flush()
788        i = 0
789        while True:
790            if kwd.has_key( 'state_name_%i' % i ):
791                name = util.restore_text( params.get( 'state_name_%i' % i, None ) )
792                desc = util.restore_text( params.get( 'state_desc_%i' % i, None ) )
793                ss = trans.model.SampleState( name, desc, request_type )
794                trans.sa_session.add( ss )
795                trans.sa_session.flush()
796                i = i + 1
797            else:
798                break
799        message = "The new sequencer configuration named (%s) with %s states has been created" % ( request_type.name, i )
800        return request_type, message
801    @web.expose
802    @web.require_admin
803    def view_request_type( self, trans, **kwd ):
804        request_type_id = kwd.get( 'id', None )
805        try:
806            request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
807        except:
808            return invalid_id_redirect( trans, 'requests_admin', request_type_id, action='browse_request_types' )
809        forms = self.get_all_forms( trans )
810        rename_dataset_select_field = self.__build_rename_dataset_select_field( trans, request_type )
811        return trans.fill_template( '/admin/requests/view_request_type.mako',
812                                    request_type=request_type,
813                                    forms=forms,
814                                    rename_dataset_select_field=rename_dataset_select_field )
815    @web.expose
816    @web.require_admin
817    def view_form_definition( self, trans, **kwd ):
818        form_definition_id = kwd.get( 'id', None )
819        try:
820            form_definition = trans.sa_session.query( trans.model.FormDefinition ).get( trans.security.decode_id( form_definition_id ) )
821        except:
822            return invalid_id_redirect( trans, 'requests_admin', form_definition_id, action='browse_request_types' )
823        return trans.fill_template( '/admin/forms/show_form_read_only.mako',
824                                    form_definition=form_definition )
825    @web.expose
826    @web.require_admin
827    def delete_request_type( self, trans, **kwd ):
828        rt_id = kwd.get( 'id', '' )
829        rt_id_list = util.listify( rt_id )
830        for rt_id in rt_id_list:
831            try:
832                request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( rt_id ) )
833            except:
834                return invalid_id_redirect( trans, 'requests_admin', rt_id, action='browse_request_types' )
835            request_type.deleted = True
836            trans.sa_session.add( request_type )
837            trans.sa_session.flush()
838        status = 'done'
839        message = '%i sequencer configurations has been deleted' % len( rt_id_list )
840        return trans.response.send_redirect( web.url_for( controller='requests_admin',
841                                                          action='browse_request_types',
842                                                          message=message,
843                                                          status='done' ) )
844    @web.expose
845    @web.require_admin
846    def undelete_request_type( self, trans, **kwd ):
847        rt_id = kwd.get( 'id', '' )
848        rt_id_list = util.listify( rt_id )
849        for rt_id in rt_id_list:
850            try:
851                request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( rt_id ) )
852            except:
853                return invalid_id_redirect( trans, 'requests_admin', rt_id, action='browse_request_types' )
854            request_type.deleted = False
855            trans.sa_session.add( request_type )
856            trans.sa_session.flush()
857        status = 'done'
858        message = '%i sequencer configurations have been undeleted' % len( rt_id_list )
859        return trans.response.send_redirect( web.url_for( controller='requests_admin',
860                                                          action='browse_request_types',
861                                                          message=message,
862                                                          status=status ) )
863    @web.expose
864    @web.require_admin
865    def request_type_permissions( self, trans, **kwd ):
866        params = util.Params( kwd )
867        message = util.restore_text( params.get( 'message', ''  ) )
868        status = params.get( 'status', 'done' )
869        request_type_id = kwd.get( 'id', '' )
870        try:
871            request_type = trans.sa_session.query( trans.model.RequestType ).get( trans.security.decode_id( request_type_id ) )
872        except:
873            return invalid_id_redirect( trans, 'requests_admin', request_type_id, action='browse_request_types' )
874        roles = trans.sa_session.query( trans.model.Role ) \
875                                .filter( trans.model.Role.table.c.deleted==False ) \
876                                .order_by( trans.model.Role.table.c.name )
877        if params.get( 'update_roles_button', False ):
878            permissions = {}
879            for k, v in trans.model.RequestType.permitted_actions.items():
880                in_roles = [ trans.sa_session.query( trans.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ]
881                permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
882            trans.app.security_agent.set_request_type_permissions( request_type, permissions )
883            trans.sa_session.refresh( request_type )
884            message = "Permissions updated for sequencer configuration '%s'" % request_type.name
885        return trans.fill_template( '/admin/requests/request_type_permissions.mako',
886                                    request_type=request_type,
887                                    roles=roles,
888                                    status=status,
889                                    message=message )
890    # ===== Methods for building SelectFields used on various admin_requests forms
891    def __build_sample_id_select_field( self, trans, request, selected_value ):
892        return build_select_field( trans, request.samples, 'name', 'sample_id', selected_value=selected_value, refresh_on_change=False )
893    def __build_rename_dataset_select_field( self, trans, request_type=None ):
894        if request_type:
895            selected_value = request_type.datatx_info.get( 'rename_dataset', trans.model.RequestType.rename_dataset_options.NO )
896        else:
897            selected_value = trans.model.RequestType.rename_dataset_options.NO
898        return build_select_field( trans,
899                                   objs=[ v for k, v in trans.model.RequestType.rename_dataset_options.items() ],
900                                   label_attr='self',
901                                   select_field_name='rename_dataset',
902                                   selected_value=selected_value,
903                                   refresh_on_change=False )
904# ===== Methods for building SelectFields used on various admin_requests forms - used outside this controller =====
905def build_rename_datasets_for_sample_select_field( trans, sample_dataset, selected_value='none' ):
906    options = []
907    for option_index, option in enumerate( sample_dataset.file_path.split( os.sep )[ :-1 ] ):
908        option = option.strip()
909        if option:
910           options.append( option )
911    return build_select_field( trans,
912                               objs=options,
913                               label_attr='self',
914                               select_field_name='rename_datasets_for_sample_%s' % trans.security.encode_id( sample_dataset.id ),
915                               selected_value=selected_value,
916                               refresh_on_change=False )
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。