[hg] galaxy 3688: Add categories to the community app
details: http://www.bx.psu.edu/hg/galaxy/rev/87cee993fa2d changeset: 3688:87cee993fa2d user: Nate Coraor <nate@bx.psu.edu> date: Fri Apr 23 16:11:55 2010 -0400 description: Add categories to the community app diffstat: lib/galaxy/webapps/community/controllers/admin.py | 275 ++++++++++ lib/galaxy/webapps/community/controllers/tool_browser.py | 26 +- lib/galaxy/webapps/community/controllers/upload.py | 7 +- lib/galaxy/webapps/community/model/__init__.py | 7 +- lib/galaxy/webapps/community/model/mapping.py | 3 +- lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py | 3 +- templates/webapps/community/admin/index.mako | 9 + templates/webapps/community/base_panels.mako | 2 +- templates/webapps/community/tool/edit_tool.mako | 4 +- 9 files changed, 323 insertions(+), 13 deletions(-) diffs (452 lines): diff -r 318dc4410301 -r 87cee993fa2d lib/galaxy/webapps/community/controllers/admin.py --- a/lib/galaxy/webapps/community/controllers/admin.py Fri Apr 23 15:31:52 2010 -0400 +++ b/lib/galaxy/webapps/community/controllers/admin.py Fri Apr 23 16:11:55 2010 -0400 @@ -277,8 +277,283 @@ def build_initial_query( self, session ): return session.query( self.model_class ) +class CategoryListGrid( grids.Grid ): + class NameColumn( grids.TextColumn ): + def get_value( self, trans, grid, category ): + return category.name + class DescriptionColumn( grids.TextColumn ): + def get_value( self, trans, grid, category ): + return category.description + class StatusColumn( grids.GridColumn ): + def get_value( self, trans, grid, category ): + if category.deleted: + return "deleted" + return "" + + # Grid definition + webapp = "community" + title = "Categories" + model_class = model.Category + template='/webapps/community/admin/category/grid.mako' + default_sort_key = "name" + columns = [ + NameColumn( "Name", + key="name", + link=( lambda item: dict( operation="Edit category", id=item.id, webapp="community" ) ), + model_class=model.Category, + attach_popup=True, + filterable="advanced" ), + DescriptionColumn( "Description", attach_popup=False ), + StatusColumn( "Status", attach_popup=False ), + # Columns that are valid for filtering but are not visible. + grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) + ] + columns.append( grids.MulticolFilterColumn( "Search", + cols_to_filter=[ columns[0], columns[1], columns[2] ], + key="free-text-search", + visible=False, + filterable="standard" ) ) + global_actions = [ + grids.GridAction( "Add new category", + dict( controller='admin', action='categories', operation='create', webapp="community" ) ) + ] + operations = [ grids.GridOperation( "Rename", + condition=( lambda item: not item.deleted ), + allow_multiple=False, + url_args=dict( webapp="community", action="rename_category" ) ), + grids.GridOperation( "Delete", + condition=( lambda item: not item.deleted ), + allow_multiple=True, + url_args=dict( webapp="community", action="mark_category_deleted" ) ), + grids.GridOperation( "Undelete", + condition=( lambda item: item.deleted ), + allow_multiple=True, + url_args=dict( webapp="community", action="undelete_category" ) ), + grids.GridOperation( "Purge", + condition=( lambda item: item.deleted ), + allow_multiple=True, + url_args=dict( webapp="community", action="purge_category" ) ) ] + standard_filters = [ + grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), + grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), + grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) + ] + num_rows_per_page = 50 + preserve_state = False + use_paging = True + def get_current_item( self, trans ): + return None + def build_initial_query( self, session ): + return session.query( self.model_class ) + class AdminCommunity( BaseController, Admin ): user_list_grid = UserListGrid() role_list_grid = RoleListGrid() group_list_grid = GroupListGrid() + category_list_grid = CategoryListGrid() + + @web.expose + @web.require_admin + def categories( self, trans, **kwargs ): + if 'operation' in kwargs: + operation = kwargs['operation'].lower() + if operation == "create": + return self.create_category( trans, **kwargs ) + if operation == "delete": + return self.mark_category_deleted( trans, **kwargs ) + if operation == "undelete": + return self.undelete_category( trans, **kwargs ) + if operation == "purge": + return self.purge_category( trans, **kwargs ) + if operation == "rename": + return self.rename_category( trans, **kwargs ) + # Render the list view + return self.category_list_grid( trans, **kwargs ) + + @web.expose + @web.require_admin + def create_category( self, trans, **kwd ): + params = util.Params( kwd ) + webapp = params.get( 'webapp', 'community' ) + message = util.restore_text( params.get( 'message', '' ) ) + status = params.get( 'status', 'done' ) + if params.get( 'create_category_button', False ): + name = util.restore_text( params.name ) + description = util.restore_text( params.description ) + if not name or not description: + message = "Enter a valid name and a description" + elif trans.sa_session.query( trans.app.model.Category ).filter( trans.app.model.Category.table.c.name==name ).first(): + message = "A category with that name already exists" + else: + # Create the category + category = trans.app.model.Category( name=name, description=description ) + trans.sa_session.add( category ) + message = "Category '%s' has been created" % category.name + trans.sa_session.flush() + trans.response.send_redirect( web.url_for( controller='admin', + action='categories', + webapp=webapp, + message=util.sanitize_text( message ), + status='done' ) ) + trans.response.send_redirect( web.url_for( controller='admin', + action='create_category', + webapp=webapp, + message=util.sanitize_text( message ), + status='error' ) ) + return trans.fill_template( '/webapps/community/admin/category/category_create.mako', + webapp=webapp, + message=message, + status=status ) + @web.expose + @web.require_admin + def rename_category( self, trans, **kwd ): + params = util.Params( kwd ) + webapp = params.get( 'webapp', 'galaxy' ) + message = util.restore_text( params.get( 'message', '' ) ) + status = params.get( 'status', 'done' ) + id = params.get( 'id', None ) + if not id: + message = "No category ids received for renaming" + trans.response.send_redirect( web.url_for( controller='admin', + action='categories', + webapp=webapp, + message=message, + status='error' ) ) + category = get_category( trans, id ) + if params.get( 'rename_category_button', False ): + old_name = category.name + new_name = util.restore_text( params.name ) + new_description = util.restore_text( params.description ) + if not new_name: + message = 'Enter a valid name' + status = 'error' + elif trans.sa_session.query( trans.app.model.Category ).filter( trans.app.model.Category.table.c.name==new_name ).first(): + message = 'A category with that name already exists' + status = 'error' + else: + category.name = new_name + category.description = new_description + trans.sa_session.add( category ) + trans.sa_session.flush() + message = "Category '%s' has been renamed to '%s'" % ( old_name, new_name ) + return trans.response.send_redirect( web.url_for( controller='admin', + action='categories', + webapp=webapp, + message=util.sanitize_text( message ), + status='done' ) ) + return trans.fill_template( '/webapps/community/admin/category/category_rename.mako', + category=category, + webapp=webapp, + message=message, + status=status ) + @web.expose + @web.require_admin + def mark_category_deleted( self, trans, **kwd ): + params = util.Params( kwd ) + webapp = params.get( 'webapp', 'galaxy' ) + id = kwd.get( 'id', None ) + if not id: + message = "No category ids received for deleting" + trans.response.send_redirect( web.url_for( controller='admin', + action='categories', + webapp=webapp, + message=message, + status='error' ) ) + ids = util.listify( id ) + message = "Deleted %d categories: " % len( ids ) + for category_id in ids: + category = get_category( trans, category_id ) + category.deleted = True + trans.sa_session.add( category ) + trans.sa_session.flush() + message += " %s " % category.name + trans.response.send_redirect( web.url_for( controller='admin', + action='categories', + webapp=webapp, + message=util.sanitize_text( message ), + status='done' ) ) + @web.expose + @web.require_admin + def undelete_category( self, trans, **kwd ): + params = util.Params( kwd ) + webapp = params.get( 'webapp', 'galaxy' ) + id = kwd.get( 'id', None ) + if not id: + message = "No category ids received for undeleting" + trans.response.send_redirect( web.url_for( controller='admin', + action='categories', + webapp=webapp, + message=message, + status='error' ) ) + ids = util.listify( id ) + count = 0 + undeleted_categories = "" + for category_id in ids: + category = get_category( trans, category_id ) + if not category.deleted: + message = "Category '%s' has not been deleted, so it cannot be undeleted." % category.name + trans.response.send_redirect( web.url_for( controller='admin', + action='categories', + webapp=webapp, + message=util.sanitize_text( message ), + status='error' ) ) + category.deleted = False + trans.sa_session.add( category ) + trans.sa_session.flush() + count += 1 + undeleted_categories += " %s" % category.name + message = "Undeleted %d categories: %s" % ( count, undeleted_categories ) + trans.response.send_redirect( web.url_for( controller='admin', + action='categories', + webapp=webapp, + message=util.sanitize_text( message ), + status='done' ) ) + @web.expose + @web.require_admin + def purge_category( self, trans, **kwd ): + # This method should only be called for a Category that has previously been deleted. + # Purging a deleted Category deletes all of the following from the database: + # - ToolCategoryAssociations where category_id == Category.id + params = util.Params( kwd ) + webapp = params.get( 'webapp', 'galaxy' ) + id = kwd.get( 'id', None ) + if not id: + message = "No category ids received for purging" + trans.response.send_redirect( web.url_for( controller='admin', + action='categories', + webapp=webapp, + message=util.sanitize_text( message ), + status='error' ) ) + ids = util.listify( id ) + message = "Purged %d categories: " % len( ids ) + for category_id in ids: + category = get_category( trans, category_id ) + if not category.deleted: + message = "Category '%s' has not been deleted, so it cannot be purged." % category.name + trans.response.send_redirect( web.url_for( controller='admin', + action='categories', + webapp=webapp, + message=util.sanitize_text( message ), + status='error' ) ) + # Delete ToolCategoryAssociations + for tca in category.tools: + trans.sa_session.delete( tca ) + trans.sa_session.flush() + message += " %s " % category.name + trans.response.send_redirect( web.url_for( controller='admin', + action='categories', + webapp=webapp, + message=util.sanitize_text( message ), + status='done' ) ) + +## ---- Utility methods ------------------------------------------------------- + +def get_category( trans, id ): + """Get a User from the database by id.""" + # Load user from database + id = trans.security.decode_id( id ) + category = trans.sa_session.query( trans.model.Category ).get( id ) + if not category: + return trans.show_error_message( "Category not found for id (%s)" % str( id ) ) + return category diff -r 318dc4410301 -r 87cee993fa2d lib/galaxy/webapps/community/controllers/tool_browser.py --- a/lib/galaxy/webapps/community/controllers/tool_browser.py Fri Apr 23 15:31:52 2010 -0400 +++ b/lib/galaxy/webapps/community/controllers/tool_browser.py Fri Apr 23 16:11:55 2010 -0400 @@ -115,8 +115,15 @@ message = 'Uploading new version not implemented' status = 'error' elif params.save_button: - tool.user_description = params.description - tool.category = params.category + tool.user_description = util.restore_text( params.description ) + categories = [] + set_tool_category_associations( trans, tool, util.listify( params.category ) ) + trans.sa_session.add( tool ) + trans.sa_session.flush() + return trans.response.send_redirect( web.url_for( controller='tool_browser', + action='browse_tools', + message='Saved categories and description for %s' % tool.name, + status='done' ) ) categories = trans.sa_session.query( trans.model.Category ).order_by( trans.model.Category.table.c.name ).all() return trans.fill_template( '/webapps/community/tool/edit_tool.mako', encoded_id = encoded_id, @@ -124,3 +131,18 @@ categories=categories, message=message, status=status ) + +## ---- Utility methods ------------------------------------------------------- + +# It may make sense to create something like the security controller to do +# this, but seems unnecessary for this single operation + +def set_tool_category_associations( trans, tool, categories, delete_existing_assocs=True ): + if delete_existing_assocs: + for a in tool.categories: + trans.sa_session.delete( a ) + trans.sa_session.flush() + for category in categories: + if not isinstance( category, trans.model.Category ): + category = trans.sa_session.query( trans.model.Category ).get( int( category ) ) + tool.categories.append( trans.model.ToolCategoryAssociation( tool, category ) ) diff -r 318dc4410301 -r 87cee993fa2d lib/galaxy/webapps/community/controllers/upload.py --- a/lib/galaxy/webapps/community/controllers/upload.py Fri Apr 23 15:31:52 2010 -0400 +++ b/lib/galaxy/webapps/community/controllers/upload.py Fri Apr 23 16:11:55 2010 -0400 @@ -49,12 +49,15 @@ os.link( uploaded_file.name, obj.file_name ) except OSError: shutil.copy( uploaded_file.name, obj.file_name ) - message = 'Uploaded %s' % meta.message + return trans.response.send_redirect( web.url_for( controller='tool_browser', + action='edit_tool', + message='Uploaded %s' % meta.message, + status='done' ) ) except datatypes.DatatypeVerificationError, e: message = str( e ) status = 'error' except sqlalchemy.exc.IntegrityError: - message = 'A tool with the same ID already exists. If you are trying to update this tool to a new version, please ... ??? ... Otherwise, please choose a new ID.' + message = 'A tool with the same ID already exists. If you are trying to update this tool to a new version, please use the upload form on the "Edit Tool" page. Otherwise, please choose a new ID.' status = 'error' uploaded_file.close() selected_upload_type = params.get( 'type', 'tool' ) diff -r 318dc4410301 -r 87cee993fa2d lib/galaxy/webapps/community/model/__init__.py --- a/lib/galaxy/webapps/community/model/__init__.py Fri Apr 23 15:31:52 2010 -0400 +++ b/lib/galaxy/webapps/community/model/__init__.py Fri Apr 23 16:11:55 2010 -0400 @@ -134,10 +134,10 @@ return "Tag(id=%s, type=%i, parent_id=%s, name=%s)" % ( self.id, self.type, self.parent_id, self.name ) class Category( object ): - def __init__( self, id=None, name=None, description=None ): - self.id = id + def __init__( self, name=None, description=None, deleted=False ): self.name = name self.description = description + self.deleted = deleted class ItemTagAssociation ( object ): def __init__( self, id=None, user=None, item_id=None, tag_id=None, user_tname=None, value=None ): @@ -156,8 +156,7 @@ pass class ToolCategoryAssociation( object ): - def __init__( self, id=None, tool=None, category=None ): - self.id = id + def __init__( self, tool=None, category=None ): self.tool = tool self.category = category diff -r 318dc4410301 -r 87cee993fa2d lib/galaxy/webapps/community/model/mapping.py --- a/lib/galaxy/webapps/community/model/mapping.py Fri Apr 23 15:31:52 2010 -0400 +++ b/lib/galaxy/webapps/community/model/mapping.py Fri Apr 23 16:11:55 2010 -0400 @@ -119,7 +119,8 @@ Column( "create_time", DateTime, default=now ), Column( "update_time", DateTime, default=now, onupdate=now ), Column( "name", TrimmedString( 255 ), index=True, unique=True ), - Column( "description" , TEXT ) ) + Column( "description" , TEXT ), + Column( "deleted", Boolean, index=True, default=False ) ) ToolCategoryAssociation.table = Table( "tool_category_association", metadata, Column( "id", Integer, primary_key=True ), diff -r 318dc4410301 -r 87cee993fa2d lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py --- a/lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py Fri Apr 23 15:31:52 2010 -0400 +++ b/lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py Fri Apr 23 16:11:55 2010 -0400 @@ -96,7 +96,8 @@ Column( "create_time", DateTime, default=now ), Column( "update_time", DateTime, default=now, onupdate=now ), Column( "name", TrimmedString( 255 ), index=True, unique=True ), - Column( "description" , TEXT ) ) + Column( "description" , TEXT ), + Column( "deleted", Boolean, index=True, default=False ) ) ToolCategoryAssociation_table = Table( "tool_category_association", metadata, Column( "id", Integer, primary_key=True ), diff -r 318dc4410301 -r 87cee993fa2d templates/webapps/community/admin/index.mako --- a/templates/webapps/community/admin/index.mako Fri Apr 23 15:31:52 2010 -0400 +++ b/templates/webapps/community/admin/index.mako Fri Apr 23 16:11:55 2010 -0400 @@ -88,6 +88,15 @@ <div class="toolTitle"><a href="${h.url_for( controller='tool_browser', action='browse_tools', webapp='community' )}" target="galaxy_main">Manage tools</a></div> </div> </div> + <div class="toolSectionPad"></div> + <div class="toolSectionTitle"> + <span>Community</span> + </div> + <div class="toolSectionBody"> + <div class="toolSectionBg"> + <div class="toolTitle"><a href="${h.url_for( controller='admin', action='categories', webapp='community' )}" target="galaxy_main">Manage categories</a></div> + </div> + </div> </div> </div> </div> diff -r 318dc4410301 -r 87cee993fa2d templates/webapps/community/base_panels.mako --- a/templates/webapps/community/base_panels.mako Fri Apr 23 15:31:52 2010 -0400 +++ b/templates/webapps/community/base_panels.mako Fri Apr 23 16:11:55 2010 -0400 @@ -92,7 +92,7 @@ <div class="title" style="position: absolute; top: 0; left: 0;"> <a href="${app.config.get( 'logo_url', '/' )}"> <img border="0" src="${h.url_for('/static/images/galaxyIcon_noText.png')}" style="width: 26px; vertical-align: top;"> - Galaxy + Galaxy Community %if app.config.brand: <span class='brand'>/ ${app.config.brand}</span> %endif diff -r 318dc4410301 -r 87cee993fa2d templates/webapps/community/tool/edit_tool.mako --- a/templates/webapps/community/tool/edit_tool.mako Fri Apr 23 15:31:52 2010 -0400 +++ b/templates/webapps/community/tool/edit_tool.mako Fri Apr 23 16:11:55 2010 -0400 @@ -25,7 +25,7 @@ <div class="form-row"> <label>Categories:</label> <div class="form-row-input"> - <select name="category" multiple size=5> + <select name="category" multiple size=5 style="min-width: 250px;"> %for category in categories: %if category.id in [ tool_category.id for tool_category in tool.categories ]: <option value="${category.id}" selected>${category.name}</option> @@ -39,7 +39,7 @@ </div> <div class="form-row"> <label>Description:</label> - <div class="form-row-input"><textarea name="description" rows="5" cols="35"></textarea></div> + <div class="form-row-input"><textarea name="description" rows="5" cols="35">${tool.user_description}</textarea></div> <div style="clear: both"></div> </div> <div class="form-row">
participants (1)
-
Nate Coraor