| 1 | import sys, os, shutil, logging, urllib2 | 
|---|
| 2 | from galaxy.web.base.controller import * | 
|---|
| 3 | from galaxy.web.framework.helpers import time_ago, iff, grids | 
|---|
| 4 | from galaxy.model.orm import * | 
|---|
| 5 | from galaxy.web.form_builder import SelectField | 
|---|
| 6 | from galaxy.webapps.community import datatypes | 
|---|
| 7 | from common import get_categories, get_category, get_versions | 
|---|
| 8 |  | 
|---|
| 9 | log = logging.getLogger( __name__ ) | 
|---|
| 10 |  | 
|---|
| 11 | # States for passing messages | 
|---|
| 12 | SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error" | 
|---|
| 13 |  | 
|---|
| 14 | class UploadError( Exception ): | 
|---|
| 15 |     pass | 
|---|
| 16 |  | 
|---|
| 17 | class UploadController( BaseController ): | 
|---|
| 18 |      | 
|---|
| 19 |     @web.expose | 
|---|
| 20 |     @web.require_login( 'upload', use_panels=True, webapp='community' ) | 
|---|
| 21 |     def upload( self, trans, **kwd ): | 
|---|
| 22 |         params = util.Params( kwd ) | 
|---|
| 23 |         message = util.restore_text( params.get( 'message', ''  ) ) | 
|---|
| 24 |         status = params.get( 'status', 'done' ) | 
|---|
| 25 |         category_ids = util.listify( params.get( 'category_id', '' ) ) | 
|---|
| 26 |         replace_id = params.get( 'replace_id', None ) | 
|---|
| 27 |         replace_version = None | 
|---|
| 28 |         uploaded_file = None | 
|---|
| 29 |         upload_type = params.get( 'upload_type', 'tool' ) | 
|---|
| 30 |         categories = get_categories( trans ) | 
|---|
| 31 |         if not categories: | 
|---|
| 32 |             return trans.response.send_redirect( web.url_for( controller='tool', | 
|---|
| 33 |                                                               action='browse_tools', | 
|---|
| 34 |                                                               cntrller='tool', | 
|---|
| 35 |                                                               message='No categories have been configured in this instance of the Galaxy Tool Shed.  An administrator needs to create some via the Administrator control panel before anything can be uploaded', | 
|---|
| 36 |                                                               status='error' ) ) | 
|---|
| 37 |         if params.get( 'upload_button', False ): | 
|---|
| 38 |             url_paste = params.get( 'url', '' ).strip() | 
|---|
| 39 |             file_data = params.get( 'file_data', '' ) | 
|---|
| 40 |             if file_data == '' and url_paste == '': | 
|---|
| 41 |                 message = 'No files were entered on the upload form.' | 
|---|
| 42 |                 status = 'error' | 
|---|
| 43 |             elif file_data == '': | 
|---|
| 44 |                 try: | 
|---|
| 45 |                     uploaded_file = urllib2.urlopen( url_paste ) | 
|---|
| 46 |                 except ( ValueError, urllib2.HTTPError ), e: | 
|---|
| 47 |                     message = 'An error occurred trying to retrieve the URL entered on the upload form: %s' % e | 
|---|
| 48 |                     status = 'error' | 
|---|
| 49 |                 except urllib2.URLError, e: | 
|---|
| 50 |                     message = 'An error occurred trying to retrieve the URL entered on the upload form: %s' % e.reason | 
|---|
| 51 |                     status = 'error' | 
|---|
| 52 |             elif file_data not in ( '', None ): | 
|---|
| 53 |                 uploaded_file = file_data.file | 
|---|
| 54 |             if uploaded_file: | 
|---|
| 55 |                 datatype = trans.app.datatypes_registry.get_datatype_by_extension( upload_type ) | 
|---|
| 56 |                 if datatype is None: | 
|---|
| 57 |                     message = 'An unknown file type was selected.  This should not be possible, please report the error.' | 
|---|
| 58 |                     status = 'error' | 
|---|
| 59 |                 else: | 
|---|
| 60 |                     try: | 
|---|
| 61 |                         # Initialize the tool object | 
|---|
| 62 |                         meta = datatype.verify( uploaded_file ) | 
|---|
| 63 |                         meta.user = trans.user | 
|---|
| 64 |                         meta.guid = trans.app.security.get_new_guid() | 
|---|
| 65 |                         meta.suite = upload_type == 'toolsuite' | 
|---|
| 66 |                         obj = datatype.create_model_object( meta ) | 
|---|
| 67 |                         trans.sa_session.add( obj ) | 
|---|
| 68 |                         if isinstance( obj, trans.app.model.Tool ): | 
|---|
| 69 |                             existing = trans.sa_session.query( trans.app.model.Tool ) \ | 
|---|
| 70 |                                                        .filter_by( tool_id = meta.id ) \ | 
|---|
| 71 |                                                        .first() | 
|---|
| 72 |                             if replace_id: | 
|---|
| 73 |                                 replace_version = trans.sa_session.query( trans.app.model.Tool ).get( trans.security.decode_id( replace_id ) ) | 
|---|
| 74 |                             if existing and not replace_id: | 
|---|
| 75 |                                 raise UploadError( 'A %s with the same Id already exists.  If you are trying to update this %s to a new version, use the upload form on the "Edit Tool" page.  Otherwise, change the Id in the %s config.' % \ | 
|---|
| 76 |                                                    ( obj.label, obj.label, obj.label ) ) | 
|---|
| 77 |                             elif replace_id and not existing: | 
|---|
| 78 |                                 raise UploadError( 'The new %s id (%s) does not match the old %s id (%s).  Check the %s config files.' % \ | 
|---|
| 79 |                                                    ( obj.label, str( meta.id ), obj.label, str( replace_version.tool_id ), obj.label ) ) | 
|---|
| 80 |                             elif existing and replace_id: | 
|---|
| 81 |                                 if replace_version.newer_version: | 
|---|
| 82 |                                     # If the user has picked an old version, switch to the newest version | 
|---|
| 83 |                                     replace_version = get_versions( replace_version )[0] | 
|---|
| 84 |                                 if replace_version.tool_id != meta.id: | 
|---|
| 85 |                                     raise UploadError( 'The new %s id (%s) does not match the old %s id (%s).  Check the %s config files.' % \ | 
|---|
| 86 |                                                    ( obj.label, str( meta.id ), obj.label, str( replace_version.tool_id ), obj.label ) ) | 
|---|
| 87 |                                 for old_version in get_versions( replace_version ): | 
|---|
| 88 |                                     if old_version.version == meta.version: | 
|---|
| 89 |                                         raise UploadError( 'The new version (%s) matches an old version.  Check your version in the %s config file.' % \ | 
|---|
| 90 |                                                            ( str( meta.version ), obj.label ) ) | 
|---|
| 91 |                                     if old_version.is_new: | 
|---|
| 92 |                                         raise UploadError( 'There is an existing version of this %s which has not yet been submitted for approval, so either <a href="%s">submit it or delete it</a> before uploading a new version.' % \ | 
|---|
| 93 |                                                            ( obj.label, | 
|---|
| 94 |                                                              url_for( controller='common', | 
|---|
| 95 |                                                                       action='view_tool', | 
|---|
| 96 |                                                                       cntrller='tool', | 
|---|
| 97 |                                                                       id=trans.security.encode_id( old_version.id ) ) ) ) | 
|---|
| 98 |                                     if old_version.is_waiting: | 
|---|
| 99 |                                         raise UploadError( 'There is an existing version of this %s which is waiting for administrative approval, so contact an administrator for help.' % \ | 
|---|
| 100 |                                                            obj.label ) | 
|---|
| 101 |                                     # Defer setting the id since the newer version id doesn't exist until the new Tool object is flushed | 
|---|
| 102 |                             if category_ids: | 
|---|
| 103 |                                 for category_id in category_ids: | 
|---|
| 104 |                                     category = trans.app.model.Category.get( trans.security.decode_id( category_id ) ) | 
|---|
| 105 |                                     # Initialize the tool category | 
|---|
| 106 |                                     tca = trans.app.model.ToolCategoryAssociation( obj, category ) | 
|---|
| 107 |                                     trans.sa_session.add( tca ) | 
|---|
| 108 |                             # Initialize the tool event | 
|---|
| 109 |                             event = trans.app.model.Event( state=trans.app.model.Tool.states.NEW ) | 
|---|
| 110 |                             # Flush to get an event id | 
|---|
| 111 |                             trans.sa_session.add( event ) | 
|---|
| 112 |                             trans.sa_session.flush() | 
|---|
| 113 |                             tea = trans.app.model.ToolEventAssociation( obj, event ) | 
|---|
| 114 |                             trans.sa_session.add( tea ) | 
|---|
| 115 |                         if replace_version and replace_id: | 
|---|
| 116 |                             replace_version.newer_version_id = obj.id | 
|---|
| 117 |                             trans.sa_session.add( replace_version ) | 
|---|
| 118 |                         trans.sa_session.flush() | 
|---|
| 119 |                         try: | 
|---|
| 120 |                             os.link( uploaded_file.name, obj.file_name ) | 
|---|
| 121 |                         except OSError: | 
|---|
| 122 |                             shutil.copy( uploaded_file.name, obj.file_name ) | 
|---|
| 123 |                         # We're setting cntrller to 'tool' since that is the only controller from which we can upload | 
|---|
| 124 |                         # TODO: this will need tweaking when we can upload histories or workflows | 
|---|
| 125 |                         return trans.response.send_redirect( web.url_for( controller='common', | 
|---|
| 126 |                                                                           action='edit_tool', | 
|---|
| 127 |                                                                           cntrller='tool', | 
|---|
| 128 |                                                                           id=trans.app.security.encode_id( obj.id ), | 
|---|
| 129 |                                                                           message='Uploaded %s' % meta.message, | 
|---|
| 130 |                                                                           status='done' ) ) | 
|---|
| 131 |                     except ( datatypes.DatatypeVerificationError, UploadError ), e: | 
|---|
| 132 |                         message = str( e ) | 
|---|
| 133 |                         status = 'error' | 
|---|
| 134 |                     uploaded_file.close() | 
|---|
| 135 |             elif replace_id is not None: | 
|---|
| 136 |                 replace_version = trans.sa_session.query( trans.app.model.Tool ).get( int( trans.app.security.decode_id( replace_id ) ) ) | 
|---|
| 137 |                 old_version = None | 
|---|
| 138 |                 for old_version in get_versions( replace_version ): | 
|---|
| 139 |                     if old_version.is_new: | 
|---|
| 140 |                         message = 'There is an existing version of this tool which has not been submitted for approval, so either submit or delete it before uploading a new version.' | 
|---|
| 141 |                         break | 
|---|
| 142 |                     if old_version.is_waiting: | 
|---|
| 143 |                         message = 'There is an existing version of this tool which is waiting for administrative approval, so contact an administrator for help.' | 
|---|
| 144 |                         break | 
|---|
| 145 |                 else: | 
|---|
| 146 |                     old_version = None | 
|---|
| 147 |                 if old_version is not None: | 
|---|
| 148 |                     return trans.response.send_redirect( web.url_for( controller='common', | 
|---|
| 149 |                                                                       action='view_tool', | 
|---|
| 150 |                                                                       cntrller='tool', | 
|---|
| 151 |                                                                       id=trans.app.security.encode_id( old_version.id ), | 
|---|
| 152 |                                                                       message=message, | 
|---|
| 153 |                                                                       status='error' ) ) | 
|---|
| 154 |         selected_categories = [ trans.security.decode_id( id ) for id in category_ids ] | 
|---|
| 155 |         datatype_labels=trans.app.datatypes_registry.get_datatype_labels() | 
|---|
| 156 |         type_ids = [ tup[0] for tup in datatype_labels ] | 
|---|
| 157 |         upload_type_select_list = SelectField( 'upload_type',  | 
|---|
| 158 |                                                refresh_on_change=True,  | 
|---|
| 159 |                                                refresh_on_change_values=type_ids ) | 
|---|
| 160 |         for type_id, type_label in datatype_labels: | 
|---|
| 161 |             if type_id == upload_type: | 
|---|
| 162 |                 upload_type_select_list.add_option( type_label, type_id, selected = True ) | 
|---|
| 163 |             else: | 
|---|
| 164 |                 upload_type_select_list.add_option( type_label, type_id, selected = False ) | 
|---|
| 165 |         return trans.fill_template( '/webapps/community/upload/upload.mako', | 
|---|
| 166 |                                     message=message, | 
|---|
| 167 |                                     status=status, | 
|---|
| 168 |                                     selected_upload_type=upload_type, | 
|---|
| 169 |                                     upload_type_select_list=upload_type_select_list, | 
|---|
| 170 |                                     datatype_labels=trans.app.datatypes_registry.get_datatype_labels(), | 
|---|
| 171 |                                     replace_id=replace_id, | 
|---|
| 172 |                                     selected_categories=selected_categories, | 
|---|
| 173 |                                     categories=get_categories( trans ) ) | 
|---|