data libraries: API pep8, cleanup, repaired yet another encoding bug

This commit is contained in:
Martin Cech
2014-05-07 14:10:30 -04:00
parent b0e3dbe304
commit 0e2be0f1c1
2 changed files with 103 additions and 93 deletions
@@ -13,6 +13,7 @@ from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixin, Uses
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 @@ class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrary
"""
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 @@ class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrary
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 @@ class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrary
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 @@ class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrary
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 @@ class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrary
: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 @@ class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrary
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 @@ class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrary
: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 @@ class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrary
: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 ) )
+44 -38
View File
@@ -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 @@ class FoldersController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinIt
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 @@ class FoldersController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinIt
*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 @@ class FoldersController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinIt
"""
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 @@ class FoldersController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinIt
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 @@ class FoldersController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinIt
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:]
else:
return encoded_id
if ( ( len( encoded_id ) % 2 == 1 ) and encoded_id.startswith( 'F' ) ):
return encoded_id[ 1: ]
else:
raise exceptions.MalformedId( 'Malformed folder id ( %s ) specified, unable to decode.' % str( encoded_id ) )