1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/a73c02cd503e/ Changeset: a73c02cd503e User: martenson Date: 2014-05-07 20:10:30 Summary: data libraries: API pep8, cleanup, repaired yet another encoding bug Affected #: 2 files diff -r 9eb2b42e21fee519b1ea5e9c791bb6b8989f6b5c -r a73c02cd503ef2bec4819013517530df7045127e lib/galaxy/webapps/galaxy/api/folder_contents.py --- a/lib/galaxy/webapps/galaxy/api/folder_contents.py +++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py @@ -13,6 +13,7 @@ import logging log = logging.getLogger( __name__ ) + class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems, UsesHistoryDatasetAssociationMixin ): """ Class controls retrieval, creation and updating of folder contents. @@ -23,14 +24,14 @@ """ GET /api/folders/{encoded_folder_id}/contents - Displays a collection (list) of a folder's contents + Displays a collection (list) of a folder's contents (files and folders). Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it. Full path is provided in - response as a separate object providing data for + response as a separate object providing data for breadcrumb path building. - :param folder_id: encoded ID of the folder which + :param folder_id: encoded ID of the folder which contents should be library_dataset_dict :type folder_id: encoded string @@ -70,7 +71,7 @@ can_add_library_item = trans.user_is_admin() or trans.app.security_agent.can_add_library_item( current_user_roles, folder ) if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ): - if folder.parent_id == None: + if folder.parent_id is None: try: library = trans.sa_session.query( trans.app.model.Library ).filter( trans.app.model.Library.table.c.root_folder_id == decoded_folder_id ).one() except Exception: @@ -82,14 +83,14 @@ log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, decoded_folder_id ) ) else: log.warning( "SECURITY: Anonymous user without proper access rights is trying to load folder with ID of %s" % ( decoded_folder_id ) ) - raise exceptions.ObjectNotFound( 'Folder with the id provided ( %s ) was not found' % str( folder_id ) ) + raise exceptions.ObjectNotFound( 'Folder with the id provided ( %s ) was not found' % str( folder_id ) ) else: if trans.user: log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, decoded_folder_id ) ) else: log.warning( "SECURITY: Anonymous user without proper access rights is trying to load folder with ID of %s" % ( decoded_folder_id ) ) raise exceptions.ObjectNotFound( 'Folder with the id provided ( %s ) was not found' % str( folder_id ) ) - + def build_path( folder ): """ Search the path upwards recursively and load the whole route of @@ -111,50 +112,45 @@ upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id ) path_to_root.extend( build_path( upper_folder ) ) return path_to_root - + # Return the reversed path so it starts with the library node. full_path = build_path( folder )[::-1] folder_contents = [] update_time = '' create_time = '' - # Go through every accessible item in the folder and include its meta-data. + # Go through every accessible item (folders, datasets) in the folder and include its meta-data. for content_item in self._load_folder_contents( trans, folder, deleted ): - can_access = trans.app.security_agent.can_access_library_item( current_user_roles, content_item, trans.user ) - if ( can_access or ( content_item.api_type == 'folder' and trans.app.security_agent.folder_is_unrestricted( content_item ) ) ): - return_item = {} - encoded_id = trans.security.encode_id( content_item.id ) - update_time = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" ) - create_time = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" ) + return_item = {} + encoded_id = trans.security.encode_id( content_item.id ) + update_time = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" ) + create_time = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" ) - # For folder return also hierarchy values - if content_item.api_type == 'folder': - encoded_id = 'F' + encoded_id + if content_item.api_type == 'folder': + encoded_id = 'F' + encoded_id - # this is commented for now as it includes the items that might not be accessible - # item_count = content_item.item_count if can_access else 0 - # return_item.update ( dict ( item_count = item_count ) ) + if content_item.api_type == 'file': + library_dataset_dict = content_item.to_dict() + library_dataset_dict[ 'is_unrestricted' ] = trans.app.security_agent.dataset_is_public( content_item.library_dataset_dataset_association.dataset ) + library_dataset_dict[ 'is_private' ] = trans.app.security_agent.dataset_is_private_to_user( trans, content_item ) - if content_item.api_type == 'file': - library_dataset_dict = content_item.to_dict() - library_dataset_dict['data_type'] - library_dataset_dict['file_size'] - library_dataset_dict['date_uploaded'] - return_item.update ( dict ( data_type = library_dataset_dict['data_type'], - file_size = library_dataset_dict['file_size'], - date_uploaded = library_dataset_dict['date_uploaded'] ) ) + return_item.update( dict( data_type=library_dataset_dict[ 'data_type' ], + file_size=library_dataset_dict[ 'file_size' ], + date_uploaded=library_dataset_dict[ 'date_uploaded' ], + is_unrestricted=library_dataset_dict[ 'is_unrestricted' ], + is_private=library_dataset_dict[ 'is_private' ] ) ) - # For every item return also the default meta-data - return_item.update( dict( id = encoded_id, - type = content_item.api_type, - name = content_item.name, - update_time = update_time, - create_time = create_time, - deleted = content_item.deleted - ) ) - folder_contents.append( return_item ) + # For every item include the default meta-data + return_item.update( dict( id=encoded_id, + type=content_item.api_type, + name=content_item.name, + update_time=update_time, + create_time=create_time, + deleted=content_item.deleted + ) ) + folder_contents.append( return_item ) - return { 'metadata' : { 'full_path' : full_path, 'can_add_library_item': can_add_library_item, 'folder_name': folder.name }, 'folder_contents' : folder_contents } + return { 'metadata': { 'full_path': full_path, 'can_add_library_item': can_add_library_item, 'folder_name': folder.name }, 'folder_contents': folder_contents } def _load_folder_contents( self, trans, folder, include_deleted ): """ @@ -166,7 +162,7 @@ :type folder: Galaxy LibraryFolder :param include_deleted: flag, when true the items that are deleted - and can be undeleted by current user are shown + and can be undeleted by current user are shown :type include_deleted: boolean :returns: a list containing the requested items @@ -175,34 +171,42 @@ current_user_roles = trans.get_current_user_roles() is_admin = trans.user_is_admin() content_items = [] - for subfolder in folder.active_folders: + for subfolder in folder.folders: if subfolder.deleted: if include_deleted: if is_admin: + # Admins can see all deleted folders. subfolder.api_type = 'folder' content_items.append( subfolder ) else: + # Users with MODIFY permissions can see deleted folders. can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, subfolder ) if can_modify: subfolder.api_type = 'folder' content_items.append( subfolder ) else: - if is_admin: - subfolder.api_type = 'folder' - content_items.append( subfolder ) - else: - can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder ) - if can_access: - subfolder.api_type = 'folder' - content_items.append( subfolder ) + # Undeleted folders are non-restricted for now. The contents are not. + # TODO decide on restrictions + subfolder.api_type = 'folder' + content_items.append( subfolder ) + # if is_admin: + # subfolder.api_type = 'folder' + # content_items.append( subfolder ) + # else: + # can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder ) + # if can_access: + # subfolder.api_type = 'folder' + # content_items.append( subfolder ) for dataset in folder.datasets: if dataset.deleted: if include_deleted: if is_admin: + # Admins can see all deleted datasets. dataset.api_type = 'file' content_items.append( dataset ) else: + # Users with MODIFY permissions on the item can see the deleted item. can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, dataset ) if can_modify: dataset.api_type = 'file' @@ -230,19 +234,19 @@ :type payload: dict * folder_id: the parent folder of the new item - * from_hda_id: (optional) the id of an accessible HDA to copy + * from_hda_id: (optional) the id of an accessible HDA to copy into the library * ldda_message: (optional) the new message attribute of the LDDA created - * extended_metadata: (optional) dub-dictionary containing any + * extended_metadata: (optional) dub-dictionary containing any extended metadata to associate with the item - :returns: a dictionary containing the id, name, + :returns: a dictionary containing the id, name, and 'show' url of the new item :rtype: dict - :raises: ObjectAttributeInvalidException, - InsufficientPermissionsException, ItemAccessibilityException, + :raises: ObjectAttributeInvalidException, + InsufficientPermissionsException, ItemAccessibilityException, InternalServerError """ encoded_folder_id_16 = self.__decode_library_content_id( trans, encoded_folder_id ) @@ -287,13 +291,13 @@ :param encoded_folder_id: encoded id of Galaxy LibraryFolder :type encoded_folder_id: encoded string - :returns: last 16 chars of the encoded id in case it was Folder + :returns: last 16 chars of the encoded id in case it was Folder (had 'F' prepended) :type: string :raises: MalformedId """ - if ( len( encoded_folder_id ) == 17 and encoded_folder_id.startswith( 'F' )): + if ( len( encoded_folder_id ) == 17 and encoded_folder_id.startswith( 'F' )): return encoded_folder_id[1:] else: raise exceptions.MalformedId( 'Malformed folder id ( %s ) specified, unable to decode.' % str( encoded_folder_id ) ) diff -r 9eb2b42e21fee519b1ea5e9c791bb6b8989f6b5c -r a73c02cd503ef2bec4819013517530df7045127e lib/galaxy/webapps/galaxy/api/folders.py --- a/lib/galaxy/webapps/galaxy/api/folders.py +++ b/lib/galaxy/webapps/galaxy/api/folders.py @@ -1,18 +1,24 @@ """ API operations on library folders """ -import os, string, shutil, urllib, re, socket, traceback -from galaxy import datatypes, jobs, web, security +# import os +# import shutil +# import urllib +# import re +# import socket +# import traceback +# import string +from galaxy import web from galaxy import exceptions from galaxy.web import _future_expose_api as expose_api -from galaxy.web import _future_expose_api_anonymous as expose_api_anonymous -from galaxy.web.base.controller import BaseAPIController,UsesLibraryMixin,UsesLibraryMixinItems +from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems from sqlalchemy.orm.exc import MultipleResultsFound from sqlalchemy.orm.exc import NoResultFound import logging log = logging.getLogger( __name__ ) + class FoldersController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ): @web.expose_api @@ -32,18 +38,19 @@ Displays information about a folder. - :param encoded_parent_folder_id: the parent folder's id (required) - :type encoded_parent_folder_id: an encoded id string (should be prefixed by 'F') + :param id: the folder's encoded id (required) + :type id: an encoded id string (has to be prefixed by 'F') + + :returns: dictionary including details of the folder + :rtype: dict """ - # Eliminate any 'F' in front of the folder id. Just take the - # last 16 characters: - if ( len( id ) >= 17 ): - id = id[-16:] - # Retrieve the folder and return its contents encoded. Note that the - # check_ownership=false since we are only displaying it. - content = self.get_library_folder( trans, id, check_ownership=False, - check_accessible=True ) - return self.encode_all_ids( trans, content.to_dict( view='element' ) ) + folder_id_without_prefix = self.__cut_the_prefix( id ) + content = self.get_library_folder( trans, folder_id_without_prefix, check_ownership=False, check_accessible=True ) + return_dict = self.encode_all_ids( trans, content.to_dict( view='element' ) ) + return_dict[ 'id' ] = 'F' + return_dict[ 'id' ] + if return_dict[ 'parent_id' ] is not None: + return_dict[ 'parent_id' ] = 'F' + return_dict[ 'parent_id' ] + return return_dict @expose_api def create( self, trans, encoded_parent_folder_id, **kwd ): @@ -53,13 +60,13 @@ *POST /api/folders/{encoded_parent_folder_id} Create a new folder object underneath the one specified in the parameters. - + :param encoded_parent_folder_id: the parent folder's id (required) - :type encoded_parent_folder_id: an encoded id string (should be prefixed by 'F') + :type encoded_parent_folder_id: an encoded id string (should be prefixed by 'F') :param name: the name of the new folder (required) - :type name: str - + :type name: str + :param description: the description of the new folder :type description: str @@ -70,16 +77,16 @@ """ payload = kwd.get( 'payload', None ) - if payload == None: + if payload is None: raise exceptions.RequestParameterMissingException( "Missing required parameters 'encoded_parent_folder_id' and 'name'." ) name = payload.get( 'name', None ) description = payload.get( 'description', '' ) - if encoded_parent_folder_id == None: + if encoded_parent_folder_id is None: raise exceptions.RequestParameterMissingException( "Missing required parameter 'encoded_parent_folder_id'." ) - elif name == None: + elif name is None: raise exceptions.RequestParameterMissingException( "Missing required parameter 'name'." ) - # encoded_parent_folder_id may be prefixed by 'F' + # encoded_parent_folder_id should be prefixed by 'F' encoded_parent_folder_id = self.__cut_the_prefix( encoded_parent_folder_id ) try: decoded_parent_folder_id = trans.security.decode_id( encoded_parent_folder_id ) @@ -93,7 +100,7 @@ except NoResultFound: raise exceptions.RequestParameterInvalidException( 'No folder found with the id provided.' ) except Exception, e: - raise exceptions.InternalServerError( 'Error loading from the database.' + str(e)) + raise exceptions.InternalServerError( 'Error loading from the database.' + str( e ) ) library = parent_folder.parent_library if library.deleted: @@ -108,29 +115,28 @@ try: folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( v.id ) except Exception, e: - raise exceptions.InternalServerError( 'Error loading from the database.' + str( e )) + raise exceptions.InternalServerError( 'Error loading from the database.' + str( e )) if folder: - return_dict = folder.to_dict( view='element' ) - return_dict[ 'parent_library_id' ] = trans.security.encode_id( return_dict[ 'parent_library_id' ] ) - return_dict[ 'parent_id' ] = 'F' + trans.security.encode_id( return_dict[ 'parent_id' ] ) - return_dict[ 'id' ] = 'F' + trans.security.encode_id( return_dict[ 'id' ] ) + update_time = folder.update_time.strftime( "%Y-%m-%d %I:%M %p" ) + return_dict = self.encode_all_ids( trans, folder.to_dict( view='element' ) ) + return_dict[ 'update_time' ] = update_time + return_dict[ 'parent_id' ] = 'F' + return_dict[ 'parent_id' ] + return_dict[ 'id' ] = 'F' + return_dict[ 'id' ] return return_dict else: - raise exceptions.InternalServerError( 'Error while creating a folder.' + str(e)) + raise exceptions.InternalServerError( 'Error while creating a folder.' + str( e ) ) @web.expose_api def update( self, trans, id, library_id, payload, **kwd ): """ PUT /api/folders/{encoded_folder_id} - For now this does nothing. There are no semantics for folders that - indicates that an update operation is needed; the existing - library_contents folder does not allow for update, either. + """ - pass + raise exceptions.NotImplemented( 'Updating folder through this endpoint is not implemented yet.' ) def __cut_the_prefix(self, encoded_id): - if ( len( encoded_id ) == 17 and encoded_id.startswith( 'F' ) ): - return encoded_id[-16:] + + if ( ( len( encoded_id ) % 2 == 1 ) and encoded_id.startswith( 'F' ) ): + return encoded_id[ 1: ] else: - return encoded_id - + raise exceptions.MalformedId( 'Malformed folder id ( %s ) specified, unable to decode.' % str( encoded_id ) ) Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.