From 7378e445c43722acbb41b662f529cf0b209b229b Mon Sep 17 00:00:00 2001 From: Martin Cech Date: Wed, 20 May 2015 15:15:29 -0400 Subject: [PATCH] first take on TS group manager introduce TS manager classes --- lib/galaxy/webapps/tool_shed/api/groups.py | 86 ++++++++-------- lib/tool_shed/managers/__init__.py | 0 lib/tool_shed/managers/groups.py | 110 +++++++++++++++++++++ lib/tool_shed/managers/repositories.py | 68 +++++++++++++ 4 files changed, 223 insertions(+), 41 deletions(-) create mode 100644 lib/tool_shed/managers/__init__.py create mode 100644 lib/tool_shed/managers/groups.py create mode 100644 lib/tool_shed/managers/repositories.py diff --git a/lib/galaxy/webapps/tool_shed/api/groups.py b/lib/galaxy/webapps/tool_shed/api/groups.py index e29483626a0..8ec4bc1f316 100644 --- a/lib/galaxy/webapps/tool_shed/api/groups.py +++ b/lib/galaxy/webapps/tool_shed/api/groups.py @@ -3,12 +3,14 @@ import os from galaxy import util from galaxy import web -from galaxy import exceptions +from galaxy.exceptions import RequestParameterMissingException +from galaxy.exceptions import AdminRequiredException from galaxy.web import require_admin as require_admin 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 import _future_expose_api_anonymous_and_sessionless as expose_api_anonymous_and_sessionless from galaxy.web.base.controller import BaseAPIController +from tool_shed.managers import groups import tool_shed.util.shed_util_common as suc log = logging.getLogger( __name__ ) @@ -17,10 +19,49 @@ log = logging.getLogger( __name__ ) class GroupsController( BaseAPIController ): """RESTful controller for interactions with groups in the Tool Shed.""" + def __init__( self, app ): + super( GroupsController, self ).__init__( app ) + self.group_manager = groups.GroupManager() + def __get_value_mapper( self, trans ): value_mapper = { 'id' : trans.security.encode_id } return value_mapper + @expose_api_anonymous_and_sessionless + def index( self, trans, deleted=False, **kwd ): + """ + GET /api/groups + Return a list of dictionaries that contain information about each Group. + + :param deleted: flag used to include deleted groups + + Example: GET localhost:9009/api/groups + """ + model = trans.app.model + group_dicts = [] + deleted = util.asbool( deleted ) + if deleted and not trans.user_is_admin(): + raise AdminRequiredException( 'Only administrators can query deleted groups.' ) + for group in self.group_manager.list( trans ): + group_dict = group.to_dict( view='collection', value_mapper=self.__get_value_mapper( trans ) ) + group_members = [] + for uga in group.users: + member = ( trans.sa_session.query( model.User ).filter( model.User.table.c.id == uga.user_id ).one() ) + member_repositories = [] + for repo in trans.sa_session.query( model.Repository ) \ + .filter( model.Repository.table.c.user_id == uga.user_id ) \ + .join( model.RepositoryMetadata.table ) \ + .join( model.User.table ) \ + .outerjoin( model.RepositoryCategoryAssociation.table ) \ + .outerjoin( model.Category.table ): + member_repositories.append( { 'name': repo.name, 'times_downloaded': repo.times_downloaded } ) + + member_dict = { 'username' : member.username, 'repositories': member_repositories } + group_members.append( member_dict ) + group_dict[ 'members' ] = group_members + group_dicts.append( group_dict ) + return group_dicts + @expose_api @require_admin def create( self, trans, payload, **kwd ): @@ -53,48 +94,9 @@ class GroupsController( BaseAPIController ): group_dict = group.to_dict( view='element', value_mapper=self.__get_value_mapper( trans ) ) else: - raise exceptions.RequestParameterMissingException( 'Missing required parameter "name".' ) + raise RequestParameterMissingException( 'Missing required parameter "name".' ) return group_dict - @expose_api_anonymous_and_sessionless - def index( self, trans, deleted=False, **kwd ): - """ - GET /api/groups - Return a list of dictionaries that contain information about each Group. - - :param deleted: flag used to include deleted groups - - Example: GET localhost:9009/api/groups - """ - model = trans.app.model - group_dicts = [] - deleted = util.asbool( deleted ) - if deleted and not trans.user_is_admin(): - raise exceptions.AdminRequiredException( 'Only administrators can query deleted groups.' ) - for group in trans.sa_session.query( model.Group ) \ - # .enable_eagerloads( True ) \ - .filter( model.Group.table.c.deleted == deleted ) \ - .order_by( model.Group.table.c.name ): - group_dict = group.to_dict( view='collection', value_mapper=self.__get_value_mapper( trans ) ) - - group_members = [] - for uga in group.users: - member = ( trans.sa_session.query( model.User ).filter( model.User.table.c.id == uga.user_id ).one() ) - member_repositories = [] - for repo in trans.sa_session.query( model.Repository ) \ - .filter( model.Repository.table.c.user_id == uga.user_id ) \ - .join( model.RepositoryMetadata.table ) \ - .join( model.User.table ) \ - .outerjoin( model.RepositoryCategoryAssociation.table ) \ - .outerjoin( model.Category.table ): - member_repositories.append( { 'name': repo.name, 'times_downloaded': repo.times_downloaded } ) - - member_dict = { 'username' : member.username, 'repositories': member_repositories } - group_members.append( member_dict ) - group_dict[ 'members' ] = group_members - group_dicts.append( group_dict ) - return group_dicts - @expose_api_anonymous_and_sessionless def show( self, trans, id, **kwd ): """ @@ -112,3 +114,5 @@ class GroupsController( BaseAPIController ): return group_dict group_dict = group.to_dict( view='element', value_mapper=self.__get_value_mapper( trans ) ) return group_dict + + diff --git a/lib/tool_shed/managers/__init__.py b/lib/tool_shed/managers/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/tool_shed/managers/groups.py b/lib/tool_shed/managers/groups.py new file mode 100644 index 00000000000..ad32d7c4fe8 --- /dev/null +++ b/lib/tool_shed/managers/groups.py @@ -0,0 +1,110 @@ +""" +Manager and Serializer for TS groups. +""" +from galaxy.exceptions import InconsistentDatabase +from galaxy.exceptions import RequestParameterInvalidException +from galaxy.exceptions import InternalServerError +from galaxy.exceptions import ItemAccessibilityException +from sqlalchemy.orm.exc import MultipleResultsFound +from sqlalchemy.orm.exc import NoResultFound + +import logging +log = logging.getLogger( __name__ ) + + +# ============================================================================= +class GroupManager( object ): + """ + Interface/service object for interacting with TS groups. + """ + + def __init__( self, *args, **kwargs ): + super( GroupManager, self ).__init__( *args, **kwargs ) + + def get( self, trans, decoded_group_id ): + """ + Get the group from the DB. + + :param decoded_group_id: decoded group id + :type decoded_group_id: int + + :returns: the requested group + :rtype: Group + """ + try: + group = trans.sa_session.query( trans.app.model.Group ).filter( trans.app.model.Group.table.c.id == decoded_group_id ).one() + except MultipleResultsFound: + raise InconsistentDatabase( 'Multiple groups found with the same id.' ) + except NoResultFound: + raise RequestParameterInvalidException( 'No group found with the id provided.' ) + except Exception, e: + raise InternalServerError( 'Error loading from the database.' ) + return group + + def create( self, trans, name, description=''): + """ + Create a new group. + """ + if not trans.user_is_admin: + raise ItemAccessibilityException( 'Only administrators can create groups.' ) + else: + group = trans.app.model.Group( name=name, description=description ) + trans.sa_session.add( group ) + trans.sa_session.flush() + return group + + def update( self, trans, group, name=None, description=None ): + """ + Update the given group + """ + changed = False + if not trans.user_is_admin(): + raise ItemAccessibilityException( 'Only administrators can update groups.' ) + if group.deleted: + raise RequestParameterInvalidException( 'You cannot modify a deleted group. Undelete it first.' ) + if name is not None: + group.name = name + changed = True + if description is not None: + group.description = description + changed = True + if changed: + trans.sa_session.add( group ) + trans.sa_session.flush() + return group + + def delete( self, trans, group, undelete=False ): + """ + Mark given group deleted/undeleted based on the flag. + """ + if not trans.user_is_admin(): + raise ItemAccessibilityException( 'Only administrators can delete and undelete groups.' ) + if undelete: + group.deleted = False + else: + group.deleted = True + trans.sa_session.add( group ) + trans.sa_session.flush() + return group + + def list( self, trans, deleted=False ): + """ + Return a list of groups from the DB. + + :returns: query that will emit all groups + :rtype: sqlalchemy query + """ + is_admin = trans.user_is_admin() + query = trans.sa_session.query( trans.app.model.Group ) + if is_admin: + if deleted is None: + # Flag is not specified, do not filter on it. + pass + elif deleted: + query = query.filter( trans.app.model.Group.table.c.deleted == True ) + else: + query = query.filter( trans.app.model.Group.table.c.deleted == False ) + else: + query = query.filter( trans.app.model.Group.table.c.deleted == False ) + return query + diff --git a/lib/tool_shed/managers/repositories.py b/lib/tool_shed/managers/repositories.py new file mode 100644 index 00000000000..e0160318a16 --- /dev/null +++ b/lib/tool_shed/managers/repositories.py @@ -0,0 +1,68 @@ +""" +Manager and Serializer for TS repositories. +""" +from galaxy.exceptions import InconsistentDatabase +from galaxy.exceptions import RequestParameterInvalidException +from galaxy.exceptions import InternalServerError +from galaxy.exceptions import ItemAccessibilityException +from sqlalchemy.orm.exc import MultipleResultsFound +from sqlalchemy.orm.exc import NoResultFound + +import logging +log = logging.getLogger( __name__ ) + + +# ============================================================================= +class RepoManager( object ): + """ + Interface/service object for interacting with TS repositories. + """ + + def __init__( self, *args, **kwargs ): + super( RepoManager, self ).__init__( *args, **kwargs ) + + def get( self, trans, decoded_repo_id ): + """ + Get the repo from the DB. + + :param decoded_repo_id: decoded repo id + :type decoded_repo_id: int + + :returns: the requested repo + :rtype: Repository + """ + try: + repo = trans.sa_session.query( trans.app.model.Repository ).filter( trans.app.model.Repository.table.c.id == decoded_repo_id ).one() + except MultipleResultsFound: + raise exceptions.InconsistentDatabase( 'Multiple repositories found with the same id.' ) + except NoResultFound: + raise exceptions.RequestParameterInvalidException( 'No repository found with the id provided.' ) + except Exception, e: + raise exceptions.InternalServerError( 'Error loading from the database.' ) + return repo + + def list_by_owner( self, trans, user_id ): + """ + Return a list of of repositories owned by a given TS user from the DB. + + :returns: query that will emit repositories owned by given user + :rtype: sqlalchemy query + """ + query = trans.sa_session.query( trans.app.model.Repository ).filter( trans.app.model.Repository.table.c.user_id == user_id ) + return query + + def create( self, trans, name, description=''): + """ + Create a new group. + """ + + def update( self, trans, group, name=None, description=None ): + """ + Update the given group + """ + + def delete( self, trans, group, undelete=False ): + """ + Mark given group deleted/undeleted based on the flag. + """ +