class FormsGrid( grids.Grid ): # Custom column types class NameColumn( grids.TextColumn ): def get_value(self, trans, grid, form): return form.latest_form.name class DescriptionColumn( grids.TextColumn ): def get_value(self, trans, grid, form): return form.latest_form.desc class TypeColumn( grids.TextColumn ): def get_value(self, trans, grid, form): return form.latest_form.type class DeletedColumn( grids.GridColumn ): def get_accepted_filters( self ): """ Returns a list of accepted filters for this column. """ accepted_filter_labels_and_vals = { "active" : "False", "deleted" : "True", "all": "All" } accepted_filters = [] for label, val in accepted_filter_labels_and_vals.items(): args = { self.key: val } accepted_filters.append( grids.GridColumnFilter( label, args) ) return accepted_filters # Grid definition title = "Forms" template = "admin/forms/grid.mako" model_class = model.FormDefinitionCurrent default_sort_key = "-create_time" num_rows_per_page = 50 preserve_state = True use_paging = True default_filter = dict( deleted="False" ) columns = [ NameColumn( "Name", key="name", model_class=model.FormDefinition, link=( lambda item: iff( item.deleted, None, dict( operation="view", id=item.id ) ) ), attach_popup=True, filterable="advanced" ), DescriptionColumn( "Description", key='desc', model_class=model.FormDefinition, filterable="advanced" ), TypeColumn( "Type" ), DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ) ] columns.append( grids.MulticolFilterColumn( "Search", cols_to_filter=[ columns[0], columns[1] ], key="free-text-search", visible=False, filterable="standard" ) ) operations = [ grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ), grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: not item.deleted ) ), grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ), ] global_actions = [ grids.GridAction( "Create new form", dict( controller='forms', action='new' ) ) ]
class UserRequestsGrid( RequestsGrid ): operations = [ operation for operation in RequestsGrid.operations ] operations.append( grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: item.is_unsubmitted and not item.deleted ) ) ) operations.append( grids.GridOperation( "Delete", allow_multiple=True, condition=( lambda item: item.is_new and not item.deleted ) ) ) operations.append( grids.GridOperation( "Undelete", allow_multiple=True, condition=( lambda item: item.deleted ) ) ) def apply_query_filter( self, trans, query, **kwd ): return query.filter_by( user=trans.user )
class StoredWorkflowAllPublishedGrid(grids.Grid): title = "Published Workflows" model_class = model.StoredWorkflow default_sort_key = "update_time" default_filter = dict(public_url="All", username="******", tags="All") columns = [ grids.PublicURLColumn("Name", key="name", filterable="advanced", attach_popup=True), grids.OwnerAnnotationColumn("Annotation", key="annotation", model_annotation_association_class=model.StoredWorkflowAnnotationAssociation, filterable="advanced"), grids.OwnerColumn("Owner", key="username", model_class=model.User, filterable="advanced"), grids.CommunityRatingColumn("Community Rating", key="rating"), grids.CommunityTagsColumn("Community Tags", key="tags", model_tag_association_class=model.StoredWorkflowTagAssociation, filterable="advanced", grid_name="PublicWorkflowListGrid"), grids.ReverseSortColumn("Last Updated", key="update_time", format=time_ago) ] columns.append( grids.MulticolFilterColumn( "Search name, annotation, owner, and tags", cols_to_filter=[columns[0], columns[1], columns[2], columns[4]], key="free-text-search", visible=False, filterable="standard" ) ) operations = [ grids.GridOperation( 'Run', condition=(lambda item: not item.deleted), allow_multiple=False, url_args=dict(controller='workflows', action="run") ), grids.GridOperation( "Import", condition=(lambda item: not item.deleted), allow_multiple=False, url_args=dict(action="imp") ), grids.GridOperation( "Save as File", condition=(lambda item: not item.deleted), allow_multiple=False, url_args=dict(action="export_to_file") ), ] num_rows_per_page = 50 use_paging = True def build_initial_query(self, trans, **kwargs): # See optimization description comments and TODO for tags in matching public histories query. # In addition to that - be sure to lazyload the latest_workflow - it isn't needed and it causes all # of its steps to be eagerly loaded. return trans.sa_session.query(self.model_class).join("user").options(lazyload("latest_workflow"), eagerload("user").load_only("username"), eagerload("annotations"), undefer("average_rating")) def apply_query_filter(self, trans, query, **kwargs): # A public workflow is published, has a slug, and is not deleted. return query.filter( self.model_class.published == expression.true()).filter( self.model_class.slug.isnot(None)).filter( self.model_class.deleted == expression.false())
class AdminRequestsGrid(RequestsGrid): class UserColumn(grids.TextColumn): def get_value(self, trans, grid, request): return escape(request.user.email) # Grid definition columns = [col for col in RequestsGrid.columns] columns.append(UserColumn("User", model_class=model.User, key='username')) operations = [operation for operation in RequestsGrid.operations] operations.append( grids.GridOperation("Edit", allow_multiple=False, condition=(lambda item: not item.deleted))) operations.append( grids.GridOperation( "Reject", allow_multiple=False, condition=(lambda item: not item.deleted and item.is_submitted))) operations.append( grids.GridOperation("Delete", allow_multiple=True, condition=(lambda item: not item.deleted))) operations.append( grids.GridOperation("Undelete", condition=(lambda item: item.deleted))) global_actions = [ grids.GridAction( "Create new request", dict(controller='requests_common', action='create_request', cntrller='requests_admin')) ]
class FormsGrid(grids.Grid): # Custom column types class NameColumn(grids.TextColumn): def get_value(self, trans, grid, form): return escape(form.latest_form.name) class DescriptionColumn(grids.TextColumn): def get_value(self, trans, grid, form): return escape(form.latest_form.desc) class TypeColumn(grids.TextColumn): def get_value(self, trans, grid, form): return form.latest_form.type # Grid definition title = "Forms" template = "admin/forms/grid.mako" model_class = model.FormDefinitionCurrent default_sort_key = "-create_time" num_rows_per_page = 50 preserve_state = True use_paging = True default_filter = dict(deleted="False") columns = [ NameColumn("Name", key="name", model_class=model.FormDefinition, link=(lambda item: iff(item.deleted, None, dict(operation="view_latest_form_definition", id=item.id))), attach_popup=True, filterable="advanced"), DescriptionColumn("Description", key='desc', model_class=model.FormDefinition, filterable="advanced"), TypeColumn("Type"), grids.DeletedColumn("Deleted", key="deleted", visible=False, filterable="advanced") ] columns.append(grids.MulticolFilterColumn("Search", cols_to_filter=[columns[0], columns[1]], key="free-text-search", visible=False, filterable="standard")) operations = [ grids.GridOperation("Edit", allow_multiple=False, condition=(lambda item: not item.deleted)), grids.GridOperation("Delete", allow_multiple=True, condition=(lambda item: not item.deleted)), grids.GridOperation("Undelete", condition=(lambda item: item.deleted)), ] global_actions = [ grids.GridAction("Create new form", dict(controller='forms', action='create_form_definition')) ] def build_initial_query(self, trans, **kwargs): return trans.sa_session.query(self.model_class).join(model.FormDefinition, self.model_class.latest_form_id == model.FormDefinition.id)
class VisualizationListGrid(grids.Grid): def get_url_args(item): """ Returns dictionary used to create item link. """ url_kwargs = dict(controller='visualization', id=item.id) # TODO: hack to build link to saved visualization - need trans in this function instead in order to do # link_data = trans.app.visualizations_registry.get_visualizations( trans, item ) if item.type in registry.VisualizationsRegistry.BUILT_IN_VISUALIZATIONS: url_kwargs['action'] = item.type else: url_kwargs['__route_name__'] = 'saved_visualization' url_kwargs['visualization_name'] = item.type url_kwargs['action'] = 'saved' return url_kwargs def get_display_name(self, trans, item): if trans.app.visualizations_registry and item.type in trans.app.visualizations_registry.plugins: plugin = trans.app.visualizations_registry.plugins[item.type] return plugin.config.get('name', item.type) return item.type # Grid definition title = "Saved Visualizations" model_class = model.Visualization default_sort_key = "-update_time" default_filter = dict(title="All", deleted="False", tags="All", sharing="All") columns = [ grids.TextColumn("Title", key="title", attach_popup=True, link=get_url_args), grids.TextColumn("Type", method='get_display_name'), grids.TextColumn("Build", key="dbkey"), grids.IndividualTagsColumn("Tags", key="tags", model_tag_association_class=model.VisualizationTagAssociation, filterable="advanced", grid_name="VisualizationListGrid"), grids.SharingStatusColumn("Sharing", key="sharing", filterable="advanced", sortable=False), grids.GridColumn("Created", key="create_time", format=time_ago), grids.GridColumn("Last Updated", key="update_time", format=time_ago), ] columns.append( grids.MulticolFilterColumn( "Search", cols_to_filter=[columns[0], columns[2]], key="free-text-search", visible=False, filterable="standard") ) operations = [ grids.GridOperation("Open", allow_multiple=False, url_args=get_url_args), grids.GridOperation("Edit Attributes", allow_multiple=False, url_args=dict(controller="", action='visualizations/edit')), grids.GridOperation("Copy", allow_multiple=False, condition=(lambda item: not item.deleted)), grids.GridOperation("Share or Publish", allow_multiple=False, condition=(lambda item: not item.deleted), url_args=dict(action='sharing')), grids.GridOperation("Delete", condition=(lambda item: not item.deleted), confirm="Are you sure you want to delete this visualization?"), ] def apply_query_filter(self, trans, query, **kwargs): return query.filter_by(user=trans.user, deleted=False)
class AdminRepositoryListGrid(RepositoryListGrid): operations = [operation for operation in RepositoryListGrid.operations] operations.append( grids.GridOperation("Delete", allow_multiple=False, condition=(lambda item: not item.deleted), async_compatible=False)) operations.append( grids.GridOperation("Undelete", allow_multiple=False, condition=(lambda item: item.deleted), async_compatible=False)) standard_filters = []
class AdminRepositoryGrid(RepositoryGrid): class DeletedColumn(grids.BooleanColumn): def get_value(self, trans, grid, repository): if repository.deleted: return 'yes' return '' columns = [ RepositoryGrid.NameColumn( "Name", key="name", link=(lambda item: dict(operation="view_or_manage_repository", id=item.id)), attach_popup=True), RepositoryGrid.HeadsColumn("Heads"), RepositoryGrid.UserColumn( "Owner", model_class=model.User, link=(lambda item: dict(operation="repositories_by_user", id=item.id)), attach_popup=False, key="User.username"), RepositoryGrid.DeprecatedColumn("Deprecated", key="deprecated", attach_popup=False), # Columns that are valid for filtering but are not visible. DeletedColumn("Deleted", key="deleted", attach_popup=False) ] columns.append( grids.MulticolFilterColumn("Search repository name", cols_to_filter=[columns[0]], key="free-text-search", visible=False, filterable="standard")) operations = [operation for operation in RepositoryGrid.operations] operations.append( grids.GridOperation("Delete", allow_multiple=False, condition=(lambda item: not item.deleted), async_compatible=False)) operations.append( grids.GridOperation("Undelete", allow_multiple=False, condition=(lambda item: item.deleted), async_compatible=False)) standard_filters = [] default_filter = {} def build_initial_query(self, trans, **kwd): return trans.sa_session.query(model.Repository) \ .join(model.User.table)
class UserOpenIDGrid(grids.Grid): use_panels = False title = "OpenIDs linked to your account" model_class = model.UserOpenID template = '/user/openid_manage.mako' default_filter = {"openid": "All"} default_sort_key = "-create_time" columns = [ grids.TextColumn("OpenID URL", key="openid", link=(lambda x: dict(action='openid_auth', login_button="Login", openid_url=x.openid if not x.provider else '', openid_provider=x.provider, auto_associate=True))), grids.GridColumn("Created", key="create_time", format=time_ago), ] operations = [ grids.GridOperation("Delete", async_compatible=True), ] def build_initial_query(self, trans, **kwd): return trans.sa_session.query( self.model_class).filter(self.model_class.user_id == trans.user.id)
class InteractiveToolEntryPointListGrid(grids.Grid): use_panels = True title = "Available InteractiveTools" model_class = model.InteractiveToolEntryPoint default_filter = {"name": "All"} default_sort_key = "-update_time" columns = [ EntryPointLinkColumn("Name", filterable="advanced"), JobStatusColumn("Job Info", key="job_state", model_class=model.Job), grids.GridColumn("Created", key="created_time", format=time_ago), grids.GridColumn("Last Updated", key="modified_time", format=time_ago), ] columns.append( grids.MulticolFilterColumn( "Search", cols_to_filter=[columns[0]], key="free-text-search", visible=False, filterable="standard" ) ) operations = [ grids.GridOperation("Stop", condition=(lambda item: item.active), async_compatible=False), ] def build_initial_query(self, trans, **kwargs): # Get list of user's active InteractiveTools return trans.app.interactivetool_manager.get_nonterminal_for_user_by_trans(trans)
class RepositoriesWithoutReviewsGrid(RepositoriesWithReviewsGrid): # This grid filters out repositories that have been marked as deprecated. title = "Repositories with no reviews" columns = [ RepositoriesWithReviewsGrid.NameColumn( "Repository name", key="name", link=(lambda item: dict(operation="view_or_manage_repository", id=item.id)), attach_popup=True), RepositoriesWithReviewsGrid.DescriptionColumn("Synopsis", key="description", attach_popup=False), RepositoriesWithReviewsGrid.WithoutReviewsRevisionColumn( "Revisions for review"), RepositoriesWithReviewsGrid.UserColumn("Owner", attach_popup=False) ] columns.append( grids.MulticolFilterColumn("Search repository name, description", cols_to_filter=[columns[0], columns[1]], key="free-text-search", visible=False, filterable="standard")) operations = [ grids.GridOperation("Inspect repository revisions", allow_multiple=False, condition=(lambda item: not item.deleted), async_compatible=False) ] def build_initial_query(self, trans, **kwd): return trans.sa_session.query( model.Repository ) \ .filter( and_( model.Repository.table.c.deprecated == False, model.Repository.reviews == None ) ) \ .join( model.User.table )
class PageListGrid( grids.Grid ): # Grid definition use_panels = True title = "Pages" model_class = model.Page default_filter = { "published" : "All"} default_sort_key = "-create_time" columns = [ grids.TextColumn( "Title", key="title", model_class=model.Page, attach_popup=True, filterable="standard" ), PublicURLColumn( "Public URL" ), grids.GridColumn( "Published", key="published", format=format_bool, filterable="standard" ), grids.GridColumn( "Created", key="create_time", format=time_ago ), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), ] global_actions = [ grids.GridAction( "Add new page", dict( action='create' ) ) ] operations = [ grids.GridOperation( "View", allow_multiple=False, url_args=dict( action='display') ), grids.GridOperation( "Edit name/id", allow_multiple=False, url_args=dict( action='edit') ), grids.GridOperation( "Edit content", allow_multiple=False, url_args=dict( action='edit_content') ), grids.GridOperation( "Delete" ), grids.GridOperation( "Publish", condition=( lambda item: not item.published ) ), grids.GridOperation( "Unpublish", condition=( lambda item: item.published ) ), ] def apply_default_filter( self, trans, query, **kwargs ): return query.filter_by( user=trans.user, deleted=False )
class PageListGrid( grids.Grid ): # Custom column. class URLColumn( grids.PublicURLColumn ): def get_value( self, trans, grid, item ): return url_for(controller='page', action='display_by_username_and_slug', username=item.user.username, slug=item.slug ) # Grid definition use_panels = True title = "Pages" model_class = model.Page default_filter = { "published" : "All", "tags" : "All", "title" : "All", "sharing" : "All" } default_sort_key = "-update_time" columns = [ grids.TextColumn( "Title", key="title", attach_popup=True, filterable="advanced" ), URLColumn( "Public URL" ), grids.OwnerAnnotationColumn( "Annotation", key="annotation", model_annotation_association_class=model.PageAnnotationAssociation, filterable="advanced" ), grids.IndividualTagsColumn( "Tags", key="tags", model_tag_association_class=model.PageTagAssociation, filterable="advanced", grid_name="PageListGrid" ), grids.SharingStatusColumn( "Sharing", key="sharing", filterable="advanced", sortable=False ), grids.GridColumn( "Created", key="create_time", format=time_ago ), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), ] columns.append( grids.MulticolFilterColumn( "Search", cols_to_filter=[ columns[0], columns[2] ], key="free-text-search", visible=False, filterable="standard" ) ) global_actions = [ grids.GridAction( "Add new page", dict( action='create' ) ) ] operations = [ grids.DisplayByUsernameAndSlugGridOperation( "View", allow_multiple=False ), grids.GridOperation( "Edit content", allow_multiple=False, url_args=dict( action='edit_content') ), grids.GridOperation( "Edit attributes", allow_multiple=False, url_args=dict( action='edit') ), grids.GridOperation( "Share or Publish", allow_multiple=False, condition=( lambda item: not item.deleted ), async_compatible=False ), grids.GridOperation( "Delete", confirm="Are you sure you want to delete this page?" ), ] def apply_query_filter( self, trans, query, **kwargs ): return query.filter_by( user=trans.user, deleted=False )
class SharedHistoryListGrid(grids.Grid): # Custom column types class DatasetsByStateColumn(grids.GridColumn): def get_value(self, trans, grid, history): rval = '' for state in ('ok', 'running', 'queued', 'error'): total = sum(1 for d in history.active_datasets if d.state == state) if total: rval += f'<div class="count-box state-color-{state}">{total}</div>' return rval class SharedByColumn(grids.GridColumn): def get_value(self, trans, grid, history): return escape(history.user.email) # Grid definition title = "Histories shared with you by others" model_class = model.History default_sort_key = "-update_time" columns = [ grids.GridColumn("Name", key="name", attach_popup=True), DatasetsByStateColumn("Datasets", sortable=False), grids.GridColumn("Created", key="create_time", format=time_ago), grids.GridColumn("Last Updated", key="update_time", format=time_ago), SharedByColumn("Shared by", key="user_id") ] operations = [ grids.GridOperation("View", allow_multiple=False, url_args=dict(controller="", action="histories/view")), grids.GridOperation("Copy", allow_multiple=False), grids.GridOperation("Unshare", allow_multiple=False) ] def build_initial_query(self, trans, **kwargs): return trans.sa_session.query(self.model_class).join('users_shared_with') def apply_query_filter(self, trans, query, **kwargs): return query.filter(model.HistoryUserShareAssociation.user == trans.user)
class StoredWorkflowListGrid(grids.Grid): class StepsColumn(grids.GridColumn): def get_value(self, trans, grid, workflow): return len(workflow.latest_workflow.steps) # Grid definition use_panels = True title = "Saved Workflows" model_class = model.StoredWorkflow default_filter = {"name": "All", "tags": "All"} default_sort_key = "-update_time" columns = [ grids.TextColumn("Name", key="name", attach_popup=True, filterable="advanced"), grids.IndividualTagsColumn( "Tags", "tags", model_tag_association_class=model.StoredWorkflowTagAssociation, filterable="advanced", grid_name="StoredWorkflowListGrid"), StepsColumn("Steps"), grids.GridColumn("Created", key="create_time", format=time_ago), grids.GridColumn("Last Updated", key="update_time", format=time_ago), ] columns.append( grids.MulticolFilterColumn("Search", cols_to_filter=[columns[0], columns[1]], key="free-text-search", visible=False, filterable="standard")) operations = [ grids.GridOperation("Edit", allow_multiple=False, condition=(lambda item: not item.deleted), async_compatible=False), grids.GridOperation("Run", condition=(lambda item: not item.deleted), async_compatible=False), grids.GridOperation("Copy", condition=(lambda item: not item.deleted), async_compatible=False), grids.GridOperation("Rename", condition=(lambda item: not item.deleted), async_compatible=False), grids.GridOperation("Sharing", condition=(lambda item: not item.deleted), async_compatible=False), grids.GridOperation("Delete", condition=(lambda item: item.deleted), async_compatible=True), ] def apply_query_filter(self, trans, query, **kwargs): return query.filter_by(user=trans.user, deleted=False)
class RepositoriesReadyForReviewGrid(RepositoriesWithoutReviewsGrid): # Repositories that are ready for human review are those that either: # 1) Have no tools # 2) Have tools that have been proven to be functionally correct within Galaxy. # This grid filters out repositories that have been marked as either deprecated or deleted. title = "Repositories ready for review" columns = [ RepositoriesWithoutReviewsGrid.NameColumn( "Repository name", key="name", link=(lambda item: dict(operation="view_or_manage_repository", id=item.id)), attach_popup=True), RepositoriesWithoutReviewsGrid.DescriptionColumn("Synopsis", key="description", attach_popup=False), RepositoriesWithoutReviewsGrid.WithoutReviewsRevisionColumn( "Revisions for review"), RepositoriesWithoutReviewsGrid.UserColumn("Owner", model_class=model.User, attach_popup=False, key="User.username") ] columns.append( grids.MulticolFilterColumn("Search repository name, description", cols_to_filter=[columns[0], columns[1]], key="free-text-search", visible=False, filterable="standard")) operations = [ grids.GridOperation("Inspect repository revisions", allow_multiple=False, condition=(lambda item: not item.deleted), async_compatible=False) ] def build_initial_query(self, trans, **kwd): return trans.sa_session.query( model.Repository ) \ .filter( and_( model.Repository.table.c.deleted == False, model.Repository.table.c.deprecated == False, model.Repository.reviews == None ) ) \ .join( model.RepositoryMetadata.table ) \ .filter( and_( model.RepositoryMetadata.table.c.downloadable == True, or_( model.RepositoryMetadata.table.c.includes_tools == False, and_( model.RepositoryMetadata.table.c.includes_tools == True, model.RepositoryMetadata.table.c.tools_functionally_correct == True ) ) ) ) \ .join( model.User.table )
class VisualizationListGrid( grids.Grid ): def get_url_args( item ): """ Returns dictionary used to create item link. """ controller = "visualization" action = item.type if item.type == "phyloviz": controller = "phyloviz" action = "visualization" return dict( controller=controller, action=action, id=item.id ) # Grid definition title = "Saved Visualizations" model_class = model.Visualization default_sort_key = "-update_time" default_filter = dict( title="All", deleted="False", tags="All", sharing="All" ) columns = [ grids.TextColumn( "Title", key="title", attach_popup=True, link=get_url_args ), grids.TextColumn( "Type", key="type" ), grids.TextColumn( "Dbkey", key="dbkey" ), grids.IndividualTagsColumn( "Tags", key="tags", model_tag_association_class=model.VisualizationTagAssociation, filterable="advanced", grid_name="VisualizationListGrid" ), grids.SharingStatusColumn( "Sharing", key="sharing", filterable="advanced", sortable=False ), grids.GridColumn( "Created", key="create_time", format=time_ago ), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), ] columns.append( grids.MulticolFilterColumn( "Search", cols_to_filter=[ columns[0], columns[2] ], key="free-text-search", visible=False, filterable="standard" ) ) global_actions = [ grids.GridAction( "Create new visualization", dict( action='create' ) ) ] operations = [ grids.GridOperation( "Open", allow_multiple=False, url_args=get_url_args ), grids.GridOperation( "Open in Circster", allow_multiple=False, condition=( lambda item: item.type == 'trackster' ), url_args=dict( action='circster' ) ), grids.GridOperation( "Edit Attributes", allow_multiple=False, url_args=dict( action='edit') ), grids.GridOperation( "Copy", allow_multiple=False, condition=( lambda item: not item.deleted ), async_compatible=False, url_args=dict( action='copy') ), grids.GridOperation( "Share or Publish", allow_multiple=False, condition=( lambda item: not item.deleted ), async_compatible=False ), grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ), async_compatible=True, confirm="Are you sure you want to delete this visualization?" ), ] def apply_query_filter( self, trans, query, **kwargs ): return query.filter_by( user=trans.user, deleted=False )
class ReviewedRepositoriesIOwnGrid(RepositoriesWithReviewsGrid): title = "Reviewed repositories I own" columns = [ RepositoriesWithReviewsGrid.NameColumn( "Repository name", key="name", link=(lambda item: dict(operation="view_or_manage_repository", id=item.id)), attach_popup=True), RepositoriesWithReviewsGrid.WithReviewsRevisionColumn( "Reviewed revisions"), RepositoriesWithReviewsGrid.WithoutReviewsRevisionColumn( "Revisions for review"), RepositoriesWithReviewsGrid.ReviewersColumn("Reviewers", attach_popup=False), RepositoryGrid.DeprecatedColumn("Deprecated") ] columns.append( grids.MulticolFilterColumn("Search repository name", cols_to_filter=[columns[0]], key="free-text-search", visible=False, filterable="standard")) operations = [ grids.GridOperation("Inspect repository revisions", allow_multiple=False, condition=(lambda item: not item.deleted), async_compatible=False) ] def build_initial_query(self, trans, **kwd): return trans.sa_session.query( model.Repository ) \ .join( ( model.RepositoryReview.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \ .filter( model.Repository.table.c.user_id == trans.user.id ) \ .join( ( model.User.table, model.User.table.c.id == model.RepositoryReview.table.c.user_id ) ) \ .outerjoin( ( model.ComponentReview.table, model.ComponentReview.table.c.repository_review_id == model.RepositoryReview.table.c.id ) ) \ .outerjoin( ( model.Component.table, model.Component.table.c.id == model.ComponentReview.table.c.component_id ) )
class DataTransferGrid(grids.Grid): # Custom column types class NameColumn(grids.TextColumn): def get_value(self, trans, grid, sample_dataset): return escape(sample_dataset.name) class SizeColumn(grids.TextColumn): def get_value(self, trans, grid, sample_dataset): return sample_dataset.size class StatusColumn(grids.TextColumn): def get_value(self, trans, grid, sample_dataset): return sample_dataset.status class ExternalServiceColumn(grids.TextColumn): def get_value(self, trans, grid, sample_dataset): try: return escape(sample_dataset.external_service.name) except: return 'None' # Grid definition title = "Sample Datasets" template = "admin/requests/sample_datasets_grid.mako" model_class = model.SampleDataset default_sort_key = "-create_time" num_rows_per_page = 50 preserve_state = True use_paging = False columns = [ NameColumn("Name", link=(lambda item: dict(operation="view", id=item.id)), attach_popup=True, filterable="advanced"), SizeColumn("Size", filterable="advanced"), grids.GridColumn("Last Updated", key="update_time", format=time_ago), ExternalServiceColumn( 'External service', link=(lambda item: dict(operation="view_external_service", id=item.external_service.id)), ), StatusColumn("Transfer Status", filterable="advanced", label_id_prefix='datasetTransferStatus-'), ] columns.append( grids.MulticolFilterColumn("Search", cols_to_filter=[columns[0]], key="free-text-search", visible=False, filterable="standard")) operations = [ grids.GridOperation( "Transfer", allow_multiple=True, condition=(lambda item: item.status in [model.SampleDataset.transfer_status.NOT_STARTED])), grids.GridOperation( "Rename", allow_multiple=True, allow_popup=False, condition=(lambda item: item.status in [model.SampleDataset.transfer_status.NOT_STARTED])), grids.GridOperation( "Delete", allow_multiple=True, condition=(lambda item: item.status in [model.SampleDataset.transfer_status.NOT_STARTED])) ] def apply_query_filter(self, trans, query, **kwd): sample_id = kwd.get('sample_id', None) if not sample_id: return query return query.filter_by(sample_id=trans.security.decode_id(sample_id))
class InstalledRepositoryGrid(grids.Grid): class ToolShedStatusColumn(grids.TextColumn): def get_value(self, trans, grid, tool_shed_repository): if tool_shed_repository.tool_shed_status: tool_shed_status_str = '' if tool_shed_repository.is_deprecated_in_tool_shed: tool_shed_status_str += generate_deprecated_repository_img_str( include_mouse_over=True) if tool_shed_repository.is_latest_installable_revision: tool_shed_status_str += generate_latest_revision_img_str( include_mouse_over=True) if tool_shed_repository.revision_update_available: tool_shed_status_str += generate_revision_updates_img_str( include_mouse_over=True) if tool_shed_repository.upgrade_available: tool_shed_status_str += generate_revision_upgrades_img_str( include_mouse_over=True) if tool_shed_repository.includes_workflows: tool_shed_status_str += generate_includes_workflows_img_str( include_mouse_over=True) else: tool_shed_status_str = generate_unknown_img_str( include_mouse_over=True) return tool_shed_status_str class NameColumn(grids.TextColumn): def get_value(self, trans, grid, tool_shed_repository): return str(tool_shed_repository.name) class DescriptionColumn(grids.TextColumn): def get_value(self, trans, grid, tool_shed_repository): return util.unicodify(tool_shed_repository.description) class OwnerColumn(grids.TextColumn): def get_value(self, trans, grid, tool_shed_repository): return str(tool_shed_repository.owner) class RevisionColumn(grids.TextColumn): def get_value(self, trans, grid, tool_shed_repository): return str(tool_shed_repository.changeset_revision) class StatusColumn(grids.TextColumn): def get_value(self, trans, grid, tool_shed_repository): return suc.get_tool_shed_repository_status_label( trans.app, tool_shed_repository) class ToolShedColumn(grids.TextColumn): def get_value(self, trans, grid, tool_shed_repository): return tool_shed_repository.tool_shed class DeletedColumn(grids.DeletedColumn): def get_accepted_filters(self): """ Returns a list of accepted filters for this column. """ accepted_filter_labels_and_vals = { "Active": "False", "Deactivated or uninstalled": "True", "All": "All" } accepted_filters = [] for label, val in accepted_filter_labels_and_vals.items(): args = {self.key: val} accepted_filters.append(grids.GridColumnFilter(label, args)) return accepted_filters # Grid definition title = "Installed tool shed repositories" model_class = tool_shed_install.ToolShedRepository template = '/admin/tool_shed_repository/grid.mako' default_sort_key = "name" columns = [ ToolShedStatusColumn(label=""), NameColumn( label="Name", key="name", link=(lambda item: iff( item.status in [ tool_shed_install.ToolShedRepository.installation_status. CLONING ], None, dict(operation="manage_repository", id=item.id))), attach_popup=True), DescriptionColumn(label="Description"), OwnerColumn(label="Owner"), RevisionColumn(label="Revision"), StatusColumn(label="Installation Status", filterable="advanced"), ToolShedColumn(label="Tool shed"), # Columns that are valid for filtering but are not visible. DeletedColumn(label="Status", key="deleted", visible=False, filterable="advanced") ] columns.append( grids.MulticolFilterColumn("Search repository name", cols_to_filter=[columns[1]], key="free-text-search", visible=False, filterable="standard")) global_actions = [ grids.GridAction( label="Update tool shed status", url_args=dict( controller='admin_toolshed', action='update_tool_shed_status_for_installed_repository', all_installed_repositories=True), inbound=False) ] operations = [ grids.GridOperation( label="Update tool shed status", condition=( lambda item: not item.deleted ), allow_multiple=False, url_args=dict( controller='admin_toolshed', action='browse_repositories', operation='update tool shed status' ) ), grids.GridOperation( label="Get updates", condition=( lambda item: \ not item.deleted and \ item.revision_update_available and \ item.status not in \ [ tool_shed_install.ToolShedRepository.installation_status.ERROR, tool_shed_install.ToolShedRepository.installation_status.NEW ] ), allow_multiple=False, url_args=dict( controller='admin_toolshed', action='browse_repositories', operation='get updates' ) ), grids.GridOperation( label="Install latest revision", condition=( lambda item: item.upgrade_available ), allow_multiple=False, url_args=dict( controller='admin_toolshed', action='browse_repositories', operation='install latest revision' ) ), grids.GridOperation( label="Install", condition=( lambda item: \ not item.deleted and \ item.status == tool_shed_install.ToolShedRepository.installation_status.NEW ), allow_multiple=False, url_args=dict( controller='admin_toolshed', action='manage_repository', operation='install' ) ), grids.GridOperation( label="Deactivate or uninstall", condition=( lambda item: \ not item.deleted and \ item.status != tool_shed_install.ToolShedRepository.installation_status.NEW ), allow_multiple=False, url_args=dict( controller='admin_toolshed', action='browse_repositories', operation='deactivate or uninstall' ) ), grids.GridOperation( label="Reset to install", condition=( lambda item: \ ( item.status == tool_shed_install.ToolShedRepository.installation_status.ERROR ) ), allow_multiple=False, url_args=dict( controller='admin_toolshed', action='browse_repositories', operation='reset to install' ) ), grids.GridOperation( label="Activate or reinstall", condition=( lambda item: item.deleted ), allow_multiple=False, target=None, url_args=dict( controller='admin_toolshed', action='browse_repositories', operation='activate or reinstall' ) ), grids.GridOperation( label="Purge", condition=( lambda item: item.is_new ), allow_multiple=False, target=None, url_args=dict( controller='admin_toolshed', action='browse_repositories', operation='purge' ) ) ] standard_filters = [] default_filter = dict(deleted="False") num_rows_per_page = 50 preserve_state = False use_paging = False def build_initial_query(self, trans, **kwd): return trans.install_model.context.query( self.model_class ) \ .order_by( self.model_class.table.c.tool_shed, self.model_class.table.c.name, self.model_class.table.c.owner, self.model_class.table.c.ctx_rev ) @property def legend(self): legend_str = '%s Updates are available in the Tool Shed for this revision<br/>' % generate_revision_updates_img_str( ) legend_str += '%s A newer installable revision is available for this repository<br/>' % generate_revision_upgrades_img_str( ) legend_str += '%s This is the latest installable revision of this repository<br/>' % generate_latest_revision_img_str( ) legend_str += '%s This repository is deprecated in the Tool Shed<br/>' % generate_deprecated_repository_img_str( ) legend_str += '%s This repository contains exported workflows<br/>' % generate_includes_workflows_img_str( ) legend_str += '%s Unable to get information from the Tool Shed<br/>' % generate_unknown_img_str( ) return legend_str
def browse_libraries(self, trans, **kwd): if 'operation' in kwd: operation = kwd['operation'].lower() if operation == "browse": return trans.response.send_redirect( web.url_for(controller='library_common', action='browse_library', cntrller='library_admin', **kwd)) elif operation == "delete": return self.delete_library(trans, **kwd) elif operation == "undelete": return self.undelete_library(trans, **kwd) self.library_list_grid.operations = [] if 'f-deleted' in kwd: if kwd['f-deleted'] != 'All': if galaxy.util.string_as_bool(kwd['f-deleted']): # We're viewing deleted data libraries, so add a GridOperation # enabling one or more of them to be undeleted. self.library_list_grid.operations = [ grids.GridOperation( "Undelete", condition=(lambda item: item.deleted), allow_multiple=True, allow_popup=False, url_args=dict(webapp="galaxy")) ] else: # We're viewing active data libraries, so add a GridOperation # enabling one or more of them to be deleted. self.library_list_grid.operations = [ grids.GridOperation( "Delete", condition=(lambda item: not item.deleted), allow_multiple=True, allow_popup=False, url_args=dict(webapp="galaxy")) ] else: # We're viewing active data libraries, so add a GridOperation # enabling one or more of them to be deleted. self.library_list_grid.operations = [ grids.GridOperation("Delete", condition=(lambda item: not item.deleted), allow_multiple=True, allow_popup=False, url_args=dict(webapp="galaxy")) ] if 'f-free-text-search' in kwd: search_term = kwd["f-free-text-search"] if trans.app.config.enable_lucene_library_search: indexed_search_enabled = True search_url = trans.app.config.config_dict.get( "fulltext_find_url", "") if search_url: status, message, lddas = lucene_search( trans, 'library_admin', search_term, search_url, **kwd) elif trans.app.config.enable_whoosh_library_search: indexed_search_enabled = True status, message, lddas = whoosh_search(trans, 'library_admin', search_term, **kwd) else: indexed_search_enabled = False if indexed_search_enabled: comptypes = get_comptypes(trans) show_deleted = galaxy.util.string_as_bool( kwd.get('show_deleted', False)) use_panels = galaxy.util.string_as_bool( kwd.get('use_panels', False)) return trans.fill_template( '/library/common/library_dataset_search_results.mako', cntrller='library_admin', search_term=search_term, comptypes=comptypes, lddas=lddas, show_deleted=show_deleted, use_panels=use_panels, message=escape(message), status=escape(status)) # Render the list view return self.library_list_grid(trans, **kwd)
class AdminGalaxy( BaseUIController, Admin, AdminActions, UsesQuotaMixin, QuotaParamParser ): user_list_grid = UserListGrid() role_list_grid = RoleListGrid() group_list_grid = GroupListGrid() quota_list_grid = QuotaListGrid() tool_version_list_grid = ToolVersionListGrid() delete_operation = grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ), allow_multiple=True ) undelete_operation = grids.GridOperation( "Undelete", condition=( lambda item: item.deleted and not item.purged ), allow_multiple=True ) purge_operation = grids.GridOperation( "Purge", condition=( lambda item: item.deleted and not item.purged ), allow_multiple=True ) @web.expose @web.require_admin def quotas( self, trans, **kwargs ): if 'operation' in kwargs: operation = kwargs.pop('operation').lower() if operation == "quotas": return self.quota( trans, **kwargs ) if operation == "create": return self.create_quota( trans, **kwargs ) if operation == "delete": return self.mark_quota_deleted( trans, **kwargs ) if operation == "undelete": return self.undelete_quota( trans, **kwargs ) if operation == "purge": return self.purge_quota( trans, **kwargs ) if operation == "change amount": return self.edit_quota( trans, **kwargs ) if operation == "manage users and groups": return self.manage_users_and_groups_for_quota( trans, **kwargs ) if operation == "rename": return self.rename_quota( trans, **kwargs ) if operation == "edit": return self.edit_quota( trans, **kwargs ) # Render the list view return self.quota_list_grid( trans, **kwargs ) @web.expose @web.require_admin def create_quota( self, trans, **kwd ): params = self.get_quota_params( kwd ) if params.get( 'create_quota_button', False ): try: quota, message = self._create_quota( params ) return trans.response.send_redirect( web.url_for( controller='admin', action='quotas', webapp=params.webapp, message=sanitize_text( message ), status='done' ) ) except MessageException as e: params.message = str( e ) params.status = 'error' in_users = map( int, params.in_users ) in_groups = map( int, params.in_groups ) new_in_users = [] new_in_groups = [] for user in trans.sa_session.query( trans.app.model.User ) \ .filter( trans.app.model.User.table.c.deleted == expression.false() ) \ .order_by( trans.app.model.User.table.c.email ): if user.id in in_users: new_in_users.append( ( user.id, user.email ) ) else: params.out_users.append( ( user.id, user.email ) ) for group in trans.sa_session.query( trans.app.model.Group ) \ .filter( trans.app.model.Group.table.c.deleted == expression.false() ) \ .order_by( trans.app.model.Group.table.c.name ): if group.id in in_groups: new_in_groups.append( ( group.id, group.name ) ) else: params.out_groups.append( ( group.id, group.name ) ) return trans.fill_template( '/admin/quota/quota_create.mako', webapp=params.webapp, name=params.name, description=params.description, amount=params.amount, operation=params.operation, default=params.default, in_users=new_in_users, out_users=params.out_users, in_groups=new_in_groups, out_groups=params.out_groups, message=params.message, status=params.status ) @web.expose @web.require_admin def rename_quota( self, trans, **kwd ): quota, params = self._quota_op( trans, 'rename_quota_button', self._rename_quota, kwd ) if not quota: return return trans.fill_template( '/admin/quota/quota_rename.mako', id=params.id, name=params.name or quota.name, description=params.description or quota.description, webapp=params.webapp, message=params.message, status=params.status ) @web.expose @web.require_admin def manage_users_and_groups_for_quota( self, trans, **kwd ): quota, params = self._quota_op( trans, 'quota_members_edit_button', self._manage_users_and_groups_for_quota, kwd ) if not quota: return in_users = [] out_users = [] in_groups = [] out_groups = [] for user in trans.sa_session.query( trans.app.model.User ) \ .filter( trans.app.model.User.table.c.deleted == expression.false() ) \ .order_by( trans.app.model.User.table.c.email ): if user in [ x.user for x in quota.users ]: in_users.append( ( user.id, user.email ) ) else: out_users.append( ( user.id, user.email ) ) for group in trans.sa_session.query( trans.app.model.Group ) \ .filter( trans.app.model.Group.table.c.deleted == expression.false()) \ .order_by( trans.app.model.Group.table.c.name ): if group in [ x.group for x in quota.groups ]: in_groups.append( ( group.id, group.name ) ) else: out_groups.append( ( group.id, group.name ) ) return trans.fill_template( '/admin/quota/quota.mako', id=params.id, name=quota.name, in_users=in_users, out_users=out_users, in_groups=in_groups, out_groups=out_groups, webapp=params.webapp, message=params.message, status=params.status ) @web.expose @web.require_admin def edit_quota( self, trans, **kwd ): quota, params = self._quota_op( trans, 'edit_quota_button', self._edit_quota, kwd ) if not quota: return return trans.fill_template( '/admin/quota/quota_edit.mako', id=params.id, operation=params.operation or quota.operation, display_amount=params.amount or quota.display_amount, webapp=params.webapp, message=params.message, status=params.status ) @web.expose @web.require_admin def set_quota_default( self, trans, **kwd ): quota, params = self._quota_op( trans, 'set_default_quota_button', self._set_quota_default, kwd ) if not quota: return if params.default: default = params.default elif quota.default: default = quota.default[0].type else: default = "no" return trans.fill_template( '/admin/quota/quota_set_default.mako', id=params.id, default=default, webapp=params.webapp, message=params.message, status=params.status ) @web.expose @web.require_admin def unset_quota_default( self, trans, **kwd ): quota, params = self._quota_op( trans, True, self._unset_quota_default, kwd ) if not quota: return return trans.response.send_redirect( web.url_for( controller='admin', action='quotas', webapp=params.webapp, message=sanitize_text( params.message ), status='error' ) ) @web.expose @web.require_admin def mark_quota_deleted( self, trans, **kwd ): quota, params = self._quota_op( trans, True, self._mark_quota_deleted, kwd, listify=True ) if not quota: return return trans.response.send_redirect( web.url_for( controller='admin', action='quotas', webapp=params.webapp, message=sanitize_text( params.message ), status='error' ) ) @web.expose @web.require_admin def undelete_quota( self, trans, **kwd ): quota, params = self._quota_op( trans, True, self._undelete_quota, kwd, listify=True ) if not quota: return return trans.response.send_redirect( web.url_for( controller='admin', action='quotas', webapp=params.webapp, message=sanitize_text( params.message ), status='error' ) ) @web.expose @web.require_admin def purge_quota( self, trans, **kwd ): quota, params = self._quota_op( trans, True, self._purge_quota, kwd, listify=True ) if not quota: return return trans.response.send_redirect( web.url_for( controller='admin', action='quotas', webapp=params.webapp, message=sanitize_text( params.message ), status='error' ) ) def _quota_op( self, trans, do_op, op_method, kwd, listify=False ): params = self.get_quota_params( kwd ) if listify: quota = [] messages = [] for id in galaxy.util.listify( params.id ): try: quota.append( self.get_quota( trans, id ) ) except MessageException as e: messages.append( str( e ) ) if messages: return None, trans.response.send_redirect( web.url_for( controller='admin', action='quotas', webapp=params.webapp, message=sanitize_text( ', '.join( messages ) ), status='error' ) ) else: try: quota = self.get_quota( trans, params.id, deleted=False ) except MessageException as e: return None, trans.response.send_redirect( web.url_for( controller='admin', action='quotas', webapp=params.webapp, message=sanitize_text( str( e ) ), status='error' ) ) if do_op is True or ( do_op is not False and params.get( do_op, False ) ): try: message = op_method( quota, params ) return None, trans.response.send_redirect( web.url_for( controller='admin', action='quotas', webapp=params.webapp, message=sanitize_text( message ), status='done' ) ) except MessageException as e: params.message = e.err_msg params.status = e.type return quota, params @web.expose @web.require_admin def impersonate( self, trans, email=None, **kwd ): if not trans.app.config.allow_user_impersonation: return trans.show_error_message( "User impersonation is not enabled in this instance of Galaxy." ) message = '' status = 'done' emails = None if email is not None: user = trans.sa_session.query( trans.app.model.User ).filter_by( email=email ).first() if user: trans.handle_user_logout() trans.handle_user_login(user) message = 'You are now logged in as %s, <a target="_top" href="%s">return to the home page</a>' % ( email, url_for( controller='root' ) ) emails = [] else: message = 'Invalid user selected' status = 'error' if emails is None: emails = [ u.email for u in trans.sa_session.query( trans.app.model.User ).enable_eagerloads( False ).all() ] return trans.fill_template( 'admin/impersonate.mako', emails=emails, message=message, status=status ) def check_for_tool_dependencies( self, trans, migration_stage ): # Get the 000x_tools.xml file associated with migration_stage. tools_xml_file_path = os.path.abspath( os.path.join( trans.app.config.root, 'scripts', 'migrate_tools', '%04d_tools.xml' % migration_stage ) ) tree = galaxy.util.parse_xml( tools_xml_file_path ) root = tree.getroot() tool_shed = root.get( 'name' ) shed_url = common_util.get_tool_shed_url_from_tool_shed_registry( trans.app, tool_shed ) repo_name_dependency_tups = [] if shed_url: for elem in root: if elem.tag == 'repository': tool_dependencies = [] tool_dependencies_dict = {} repository_name = elem.get( 'name' ) changeset_revision = elem.get( 'changeset_revision' ) params = dict( name=repository_name, owner='devteam', changeset_revision=changeset_revision ) pathspec = [ 'repository', 'get_tool_dependencies' ] text = url_get( shed_url, password_mgr=self.app.tool_shed_registry.url_auth( shed_url ), pathspec=pathspec, params=params ) if text: tool_dependencies_dict = encoding_util.tool_shed_decode( text ) for dependency_key, requirements_dict in tool_dependencies_dict.items(): tool_dependency_name = requirements_dict[ 'name' ] tool_dependency_version = requirements_dict[ 'version' ] tool_dependency_type = requirements_dict[ 'type' ] tool_dependency_readme = requirements_dict.get( 'readme', '' ) tool_dependencies.append( ( tool_dependency_name, tool_dependency_version, tool_dependency_type, tool_dependency_readme ) ) repo_name_dependency_tups.append( ( repository_name, tool_dependencies ) ) return repo_name_dependency_tups @web.expose @web.require_admin def review_tool_migration_stages( self, trans, **kwd ): message = escape( galaxy.util.restore_text( kwd.get( 'message', '' ) ) ) status = galaxy.util.restore_text( kwd.get( 'status', 'done' ) ) migration_stages_dict = odict() migration_modules = [] migration_scripts_dir = os.path.abspath( os.path.join( trans.app.config.root, 'lib', 'tool_shed', 'galaxy_install', 'migrate', 'versions' ) ) migration_scripts_dir_contents = os.listdir( migration_scripts_dir ) for item in migration_scripts_dir_contents: if os.path.isfile( os.path.join( migration_scripts_dir, item ) ) and item.endswith( '.py' ): module = item.replace( '.py', '' ) migration_modules.append( module ) if migration_modules: migration_modules.sort() # Remove the 0001_tools.py script since it is the seed. migration_modules = migration_modules[ 1: ] # Reverse the list so viewing will be newest to oldest. migration_modules.reverse() for migration_module in migration_modules: migration_stage = int( migration_module.replace( '_tools', '' ) ) repo_name_dependency_tups = self.check_for_tool_dependencies( trans, migration_stage ) open_file_obj, file_name, description = imp.find_module( migration_module, [ migration_scripts_dir ] ) imported_module = imp.load_module( 'upgrade', open_file_obj, file_name, description ) migration_info = imported_module.__doc__ open_file_obj.close() migration_stages_dict[ migration_stage ] = ( migration_info, repo_name_dependency_tups ) return trans.fill_template( 'admin/review_tool_migration_stages.mako', migration_stages_dict=migration_stages_dict, message=message, status=status ) @web.expose @web.require_admin def tool_errors( self, trans, **kwd ): return trans.fill_template('admin/tool_errors.mako', tool_errors=global_tool_errors.error_stack) @web.expose @web.require_admin def view_datatypes_registry( self, trans, **kwd ): message = escape( galaxy.util.restore_text( kwd.get( 'message', '' ) ) ) status = galaxy.util.restore_text( kwd.get( 'status', 'done' ) ) return trans.fill_template( 'admin/view_datatypes_registry.mako', message=message, status=status ) @web.expose @web.require_admin def view_tool_data_tables( self, trans, **kwd ): message = escape( galaxy.util.restore_text( kwd.get( 'message', '' ) ) ) status = galaxy.util.restore_text( kwd.get( 'status', 'done' ) ) return trans.fill_template( 'admin/view_data_tables_registry.mako', message=message, status=status ) @web.expose @web.require_admin def display_applications( self, trans, **kwd ): return trans.fill_template( 'admin/view_display_applications.mako', display_applications=trans.app.datatypes_registry.display_applications ) @web.expose @web.require_admin def reload_display_application( self, trans, **kwd ): galaxy.queue_worker.send_control_task(trans.app, 'reload_display_application', noop_self=True, kwargs={'display_application_ids': kwd.get( 'id' )} ) reloaded, failed = trans.app.datatypes_registry.reload_display_applications( kwd.get( 'id' ) ) if not reloaded and failed: return trans.show_error_message( 'Unable to reload any of the %i requested display applications ("%s").' % ( len( failed ), '", "'.join( failed ) ) ) if failed: return trans.show_warn_message( 'Reloaded %i display applications ("%s"), but failed to reload %i display applications ("%s").' % ( len( reloaded ), '", "'.join( reloaded ), len( failed ), '", "'.join( failed ) ) ) if not reloaded: return trans.show_warn_message( 'You need to request at least one display application to reload.' ) return trans.show_ok_message( 'Reloaded %i requested display applications ("%s").' % ( len( reloaded ), '", "'.join( reloaded ) ) ) @web.expose @web.require_admin def recalculate_user_disk_usage( self, trans, **kwd ): user_id = kwd.get( 'id', None ) user = trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( user_id ) ) if not user: return trans.show_error_message( "User not found for id (%s)" % sanitize_text( str( user_id ) ) ) current = user.get_disk_usage() user.calculate_and_set_disk_usage() new = user.get_disk_usage() if new in ( current, None ): message = 'Usage is unchanged at %s.' % nice_size( current ) else: message = 'Usage has changed by %s to %s.' % ( nice_size( new - current ), nice_size( new ) ) return trans.response.send_redirect( web.url_for( controller='admin', action='users', message=sanitize_text( message ), status='info' ) )
class QuotaListGrid( grids.Grid ): class NameColumn( grids.TextColumn ): def get_value( self, trans, grid, quota ): return escape(quota.name) class DescriptionColumn( grids.TextColumn ): def get_value( self, trans, grid, quota ): if quota.description: return escape(quota.description) return '' class AmountColumn( grids.TextColumn ): def get_value( self, trans, grid, quota ): return quota.operation + quota.display_amount class StatusColumn( grids.GridColumn ): def get_value( self, trans, grid, quota ): if quota.deleted: return "deleted" elif quota.default: return "<strong>default for %s users</strong>" % quota.default[0].type return "" class UsersColumn( grids.GridColumn ): def get_value( self, trans, grid, quota ): if quota.users: return len( quota.users ) return 0 class GroupsColumn( grids.GridColumn ): def get_value( self, trans, grid, quota ): if quota.groups: return len( quota.groups ) return 0 # Grid definition title = "Quotas" model_class = model.Quota template = '/admin/quota/grid.mako' default_sort_key = "name" columns = [ NameColumn( "Name", key="name", link=( lambda item: dict( operation="Change amount", id=item.id, webapp="galaxy" ) ), model_class=model.Quota, attach_popup=True, filterable="advanced" ), DescriptionColumn( "Description", key='description', model_class=model.Quota, attach_popup=False, filterable="advanced" ), AmountColumn( "Amount", key='amount', model_class=model.Quota, attach_popup=False, filterable="advanced" ), UsersColumn( "Users", attach_popup=False ), GroupsColumn( "Groups", 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 quota", dict( controller='admin', action='quotas', operation='create' ) ) ] operations = [ grids.GridOperation( "Rename", condition=( lambda item: not item.deleted ), allow_multiple=False, url_args=dict( webapp="galaxy", action="rename_quota" ) ), grids.GridOperation( "Change amount", condition=( lambda item: not item.deleted ), allow_multiple=False, url_args=dict( webapp="galaxy", action="edit_quota" ) ), grids.GridOperation( "Manage users and groups", condition=( lambda item: not item.default and not item.deleted ), allow_multiple=False, url_args=dict( webapp="galaxy", action="manage_users_and_groups_for_quota" ) ), grids.GridOperation( "Set as different type of default", condition=( lambda item: item.default ), allow_multiple=False, url_args=dict( webapp="galaxy", action="set_quota_default" ) ), grids.GridOperation( "Set as default", condition=( lambda item: not item.default and not item.deleted ), allow_multiple=False, url_args=dict( webapp="galaxy", action="set_quota_default" ) ), grids.GridOperation( "Unset as default", condition=( lambda item: item.default and not item.deleted ), allow_multiple=False, url_args=dict( webapp="galaxy", action="unset_quota_default" ) ), grids.GridOperation( "Delete", condition=( lambda item: not item.deleted and not item.default ), allow_multiple=True, url_args=dict( webapp="galaxy", action="mark_quota_deleted" ) ), grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ), allow_multiple=True, url_args=dict( webapp="galaxy", action="undelete_quota" ) ), grids.GridOperation( "Purge", condition=( lambda item: item.deleted ), allow_multiple=True, url_args=dict( webapp="galaxy", action="purge_quota" ) ) ] 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
class UserListGrid( grids.Grid ): class EmailColumn( grids.TextColumn ): def get_value( self, trans, grid, user ): return escape(user.email) class UserNameColumn( grids.TextColumn ): def get_value( self, trans, grid, user ): if user.username: return escape(user.username) return 'not set' class StatusColumn( grids.GridColumn ): def get_value( self, trans, grid, user ): if user.purged: return "purged" elif user.deleted: return "deleted" return "" class GroupsColumn( grids.GridColumn ): def get_value( self, trans, grid, user ): if user.groups: return len( user.groups ) return 0 class RolesColumn( grids.GridColumn ): def get_value( self, trans, grid, user ): if user.roles: return len( user.roles ) return 0 class ExternalColumn( grids.GridColumn ): def get_value( self, trans, grid, user ): if user.external: return 'yes' return 'no' class LastLoginColumn( grids.GridColumn ): def get_value( self, trans, grid, user ): if user.galaxy_sessions: return self.format( user.galaxy_sessions[ 0 ].update_time ) return 'never' class TimeCreatedColumn( grids.GridColumn ): def get_value( self, trans, grid, user ): return user.create_time.strftime('%x') class ActivatedColumn( grids.GridColumn ): def get_value( self, trans, grid, user ): if user.active: return 'Y' else: return 'N' # Grid definition title = "Users" model_class = model.User template = '/admin/user/grid.mako' default_sort_key = "email" columns = [ EmailColumn( "Email", key="email", model_class=model.User, link=( lambda item: dict( operation="information", id=item.id, webapp="galaxy" ) ), attach_popup=True, filterable="advanced", target="top" ), UserNameColumn( "User Name", key="username", model_class=model.User, attach_popup=False, filterable="advanced" ), GroupsColumn( "Groups", attach_popup=False ), RolesColumn( "Roles", attach_popup=False ), ExternalColumn( "External", attach_popup=False ), LastLoginColumn( "Last Login", format=time_ago ), StatusColumn( "Status", attach_popup=False ), TimeCreatedColumn( "Created", attach_popup=False ), ActivatedColumn( "Activated", 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] ], key="free-text-search", visible=False, filterable="standard" ) ) global_actions = [ grids.GridAction( "Create new user", dict( controller='admin', action='users', operation='create', webapp="galaxy" ) ) ] operations = [ grids.GridOperation( "Manage Roles and Groups", condition=( lambda item: not item.deleted ), allow_multiple=False, url_args=dict( webapp="galaxy", action="manage_roles_and_groups_for_user" ) ), grids.GridOperation( "Reset Password", condition=( lambda item: not item.deleted ), allow_multiple=True, allow_popup=False, url_args=dict( webapp="galaxy", action="reset_user_password" ) ), grids.GridOperation( "Recalculate Disk Usage", condition=( lambda item: not item.deleted ), allow_multiple=False, url_args=dict( webapp="galaxy", action="recalculate_user_disk_usage" ) ) ] standard_filters = [ grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), grids.GridColumnFilter( "Deleted", args=dict( deleted=True, purged=False ) ), grids.GridColumnFilter( "Purged", args=dict( purged=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, **kwargs ): return trans.user
class GroupListGrid( grids.Grid ): class NameColumn( grids.TextColumn ): def get_value( self, trans, grid, group ): return escape(group.name) class StatusColumn( grids.GridColumn ): def get_value( self, trans, grid, group ): if group.deleted: return "deleted" return "" class RolesColumn( grids.GridColumn ): def get_value( self, trans, grid, group ): if group.roles: return len( group.roles ) return 0 class UsersColumn( grids.GridColumn ): def get_value( self, trans, grid, group ): if group.members: return len( group.members ) return 0 # Grid definition title = "Groups" model_class = model.Group template = '/admin/dataset_security/group/grid.mako' default_sort_key = "name" columns = [ NameColumn( "Name", key="name", link=( lambda item: dict( operation="Manage users and roles", id=item.id, webapp="galaxy" ) ), model_class=model.Group, attach_popup=True, filterable="advanced" ), UsersColumn( "Users", attach_popup=False ), RolesColumn( "Roles", 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 group", dict( controller='admin', action='groups', operation='create', webapp="galaxy" ) ) ] operations = [ grids.GridOperation( "Rename", condition=( lambda item: not item.deleted ), allow_multiple=False, url_args=dict( webapp="galaxy", action="rename_group" ) ), grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ), allow_multiple=True, url_args=dict( webapp="galaxy", action="mark_group_deleted" ) ), grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ), allow_multiple=True, url_args=dict( webapp="galaxy", action="undelete_group" ) ), grids.GridOperation( "Purge", condition=( lambda item: item.deleted ), allow_multiple=True, url_args=dict( webapp="galaxy", action="purge_group" ) ) ] 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
class RoleListGrid( grids.Grid ): class NameColumn( grids.TextColumn ): def get_value( self, trans, grid, role ): return escape(role.name) class DescriptionColumn( grids.TextColumn ): def get_value( self, trans, grid, role ): if role.description: return escape(role.description) return '' class TypeColumn( grids.TextColumn ): def get_value( self, trans, grid, role ): return role.type class StatusColumn( grids.GridColumn ): def get_value( self, trans, grid, role ): if role.deleted: return "deleted" return "" class GroupsColumn( grids.GridColumn ): def get_value( self, trans, grid, role ): if role.groups: return len( role.groups ) return 0 class UsersColumn( grids.GridColumn ): def get_value( self, trans, grid, role ): if role.users: return len( role.users ) return 0 # Grid definition title = "Roles" model_class = model.Role template = '/admin/dataset_security/role/grid.mako' default_sort_key = "name" columns = [ NameColumn( "Name", key="name", link=( lambda item: dict( operation="Manage users and groups", id=item.id, webapp="galaxy" ) ), model_class=model.Role, attach_popup=True, filterable="advanced" ), DescriptionColumn( "Description", key='description', model_class=model.Role, attach_popup=False, filterable="advanced" ), TypeColumn( "Type", key='type', model_class=model.Role, attach_popup=False, filterable="advanced" ), GroupsColumn( "Groups", attach_popup=False ), UsersColumn( "Users", 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 role", dict( controller='admin', action='roles', operation='create' ) ) ] operations = [ grids.GridOperation( "Edit", condition=( lambda item: not item.deleted ), allow_multiple=False, url_args=dict( webapp="galaxy", action="rename_role" ) ), grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ), allow_multiple=True, url_args=dict( webapp="galaxy", action="mark_role_deleted" ) ), grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ), allow_multiple=True, url_args=dict( webapp="galaxy", action="undelete_role" ) ), grids.GridOperation( "Purge", condition=( lambda item: item.deleted ), allow_multiple=True, url_args=dict( webapp="galaxy", action="purge_role" ) ) ] 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 apply_query_filter( self, trans, query, **kwargs ): return query.filter( model.Role.type != model.Role.types.PRIVATE )
class AdminController(BaseUIController, Admin): user_list_grid = admin_grids.UserGrid() role_list_grid = admin_grids.RoleGrid() group_list_grid = admin_grids.GroupGrid() manage_category_grid = admin_grids.ManageCategoryGrid() repository_grid = admin_grids.AdminRepositoryGrid() repository_metadata_grid = admin_grids.RepositoryMetadataGrid() delete_operation = grids.GridOperation( "Delete", condition=(lambda item: not item.deleted), allow_multiple=True) undelete_operation = grids.GridOperation( "Undelete", condition=(lambda item: item.deleted and not item.purged), allow_multiple=True) purge_operation = grids.GridOperation( "Purge", condition=(lambda item: item.deleted and not item.purged), allow_multiple=True) @web.expose @web.require_admin def browse_repositories(self, trans, **kwd): # We add parameters to the keyword dict in this method in order to rename the param # with an "f-" prefix, simulating filtering by clicking a search link. We have # to take this approach because the "-" character is illegal in HTTP requests. if 'operation' in kwd: operation = kwd['operation'].lower() if operation == "view_or_manage_repository": return trans.response.send_redirect( web.url_for(controller='repository', action='browse_repositories', **kwd)) elif operation == "edit_repository": return trans.response.send_redirect( web.url_for(controller='repository', action='edit_repository', **kwd)) elif operation == "repositories_by_user": # Eliminate the current filters if any exist. for k, v in list(kwd.items()): if k.startswith('f-'): del kwd[k] if 'user_id' in kwd: user = suc.get_user(trans.app, kwd['user_id']) kwd['f-email'] = user.email del kwd['user_id'] else: # The received id is the repository id, so we need to get the id of the user # that uploaded the repository. repository_id = kwd.get('id', None) repository = repository_util.get_repository_in_tool_shed( trans.app, repository_id) kwd['f-email'] = repository.user.email elif operation == "repositories_by_category": # Eliminate the current filters if any exist. for k, v in list(kwd.items()): if k.startswith('f-'): del kwd[k] category_id = kwd.get('id', None) category = suc.get_category(trans.app, category_id) kwd['f-Category.name'] = category.name elif operation == "receive email alerts": if kwd['id']: kwd['caller'] = 'browse_repositories' return trans.response.send_redirect( web.url_for(controller='repository', action='set_email_alerts', **kwd)) else: del kwd['operation'] elif operation == 'delete': return self.delete_repository(trans, **kwd) elif operation == "undelete": return self.undelete_repository(trans, **kwd) # The changeset_revision_select_field in the RepositoryGrid performs a refresh_on_change # which sends in request parameters like changeset_revison_1, changeset_revision_2, etc. One # of the many select fields on the grid performed the refresh_on_change, so we loop through # all of the received values to see which value is not the repository tip. If we find it, we # know the refresh_on_change occurred, and we have the necessary repository id and change set # revision to pass on. for k, v in kwd.items(): changeset_revision_str = 'changeset_revision_' if k.startswith(changeset_revision_str): repository_id = trans.security.encode_id( int(k.lstrip(changeset_revision_str))) repository = repository_util.get_repository_in_tool_shed( trans.app, repository_id) if repository.tip(trans.app) != v: return trans.response.send_redirect( web.url_for(controller='repository', action='browse_repositories', operation='view_or_manage_repository', id=trans.security.encode_id(repository.id), changeset_revision=v)) # Render the list view return self.repository_grid(trans, **kwd) @web.expose @web.require_admin def browse_repository_metadata(self, trans, **kwd): if 'operation' in kwd: operation = kwd['operation'].lower() if operation == "delete": return self.delete_repository_metadata(trans, **kwd) if operation == "view_or_manage_repository_revision": # The received id is a RepositoryMetadata object id, so we need to get the # associated Repository and redirect to view_or_manage_repository with the # changeset_revision. repository_metadata = metadata_util.get_repository_metadata_by_id( trans.app, kwd['id']) repository = repository_metadata.repository kwd['id'] = trans.security.encode_id(repository.id) kwd['changeset_revision'] = repository_metadata.changeset_revision kwd['operation'] = 'view_or_manage_repository' return trans.response.send_redirect( web.url_for(controller='repository', action='browse_repositories', **kwd)) return self.repository_metadata_grid(trans, **kwd) @web.expose @web.require_admin def create_category(self, trans, **kwd): message = escape(kwd.get('message', '')) status = kwd.get('status', 'done') name = kwd.get('name', '').strip() description = kwd.get('description', '').strip() if kwd.get('create_category_button', False): if not name or not description: message = 'Enter a valid name and a description' status = 'error' elif suc.get_category_by_name(trans.app, name): message = 'A category with that name already exists' status = 'error' else: # Create the category category = trans.app.model.Category(name=name, description=description) trans.sa_session.add(category) trans.sa_session.flush() # Update the Tool Shed's repository registry. trans.app.repository_registry.add_category_entry(category) message = "Category '%s' has been created" % escape( category.name) status = 'done' trans.response.send_redirect( web.url_for(controller='admin', action='manage_categories', message=message, status=status)) return trans.fill_template( '/webapps/tool_shed/category/create_category.mako', name=name, description=description, message=message, status=status) @web.expose @web.require_admin def delete_repository(self, trans, **kwd): message = escape(kwd.get('message', '')) status = kwd.get('status', 'done') id = kwd.get('id', None) if id: # Deleting multiple items is currently not allowed (allow_multiple=False), so there will only be 1 id. ids = util.listify(id) count = 0 deleted_repositories = "" for repository_id in ids: repository = repository_util.get_repository_in_tool_shed( trans.app, repository_id) if repository: if not repository.deleted: # Mark all installable repository_metadata records as not installable. for repository_metadata in repository.downloadable_revisions: repository_metadata.downloadable = False trans.sa_session.add(repository_metadata) # Mark the repository admin role as deleted. repository_admin_role = repository.admin_role if repository_admin_role is not None: repository_admin_role.deleted = True trans.sa_session.add(repository_admin_role) repository.deleted = True trans.sa_session.add(repository) trans.sa_session.flush() # Update the repository registry. trans.app.repository_registry.remove_entry(repository) count += 1 deleted_repositories += " %s " % repository.name if count: message = "Deleted %d %s: %s" % ( count, inflector.cond_plural( len(ids), "repository"), escape(deleted_repositories)) else: message = "All selected repositories were already marked deleted." else: message = "No repository ids received for deleting." status = 'error' trans.response.send_redirect( web.url_for(controller='admin', action='browse_repositories', message=util.sanitize_text(message), status=status)) @web.expose @web.require_admin def delete_repository_metadata(self, trans, **kwd): message = escape(kwd.get('message', '')) status = kwd.get('status', 'done') id = kwd.get('id', None) if id: ids = util.listify(id) count = 0 for repository_metadata_id in ids: repository_metadata = metadata_util.get_repository_metadata_by_id( trans.app, repository_metadata_id) trans.sa_session.delete(repository_metadata) trans.sa_session.flush() count += 1 if count: message = "Deleted %d repository metadata %s" % ( count, inflector.cond_plural(len(ids), "record")) else: message = "No repository metadata ids received for deleting." status = 'error' trans.response.send_redirect( web.url_for(controller='admin', action='browse_repository_metadata', message=util.sanitize_text(message), status=status)) @web.expose @web.require_admin def edit_category(self, trans, **kwd): '''Handle requests to edit TS category name or description''' message = escape(kwd.get('message', '')) status = kwd.get('status', 'done') id = kwd.get('id', None) if not id: message = "No category ids received for editing" trans.response.send_redirect( web.url_for(controller='admin', action='manage_categories', message=message, status='error')) category = suc.get_category(trans.app, id) original_category_name = str(category.name) original_category_description = str(category.description) if kwd.get('edit_category_button', False): flush_needed = False new_name = kwd.get('name', '').strip() new_description = kwd.get('description', '').strip() if original_category_name != new_name: if not new_name: message = 'Enter a valid name' status = 'error' elif original_category_name != new_name and suc.get_category_by_name( trans.app, new_name): message = 'A category with that name already exists' status = 'error' else: category.name = new_name flush_needed = True if original_category_description != new_description: category.description = new_description if not flush_needed: flush_needed = True if flush_needed: trans.sa_session.add(category) trans.sa_session.flush() if original_category_name != new_name: # Update the Tool Shed's repository registry. trans.app.repository_registry.edit_category_entry( original_category_name, new_name) message = "The information has been saved for category '%s'" % escape( category.name) status = 'done' return trans.response.send_redirect( web.url_for(controller='admin', action='manage_categories', message=message, status=status)) return trans.fill_template( '/webapps/tool_shed/category/edit_category.mako', category=category, message=message, status=status) @web.expose @web.require_admin def manage_categories(self, trans, **kwd): if 'f-free-text-search' in kwd: # Trick to enable searching repository name, description from the CategoryGrid. # What we've done is rendered the search box for the RepositoryGrid on the grid.mako # template for the CategoryGrid. See ~/templates/webapps/tool_shed/category/grid.mako. # Since we are searching repositories and not categories, redirect to browse_repositories(). return trans.response.send_redirect( web.url_for(controller='admin', action='browse_repositories', **kwd)) if 'operation' in kwd: operation = kwd['operation'].lower() if operation == "create": return trans.response.send_redirect( web.url_for(controller='admin', action='create_category', **kwd)) elif operation == "delete": return trans.response.send_redirect( web.url_for(controller='admin', action='mark_category_deleted', **kwd)) elif operation == "undelete": return trans.response.send_redirect( web.url_for(controller='admin', action='undelete_category', **kwd)) elif operation == "purge": return trans.response.send_redirect( web.url_for(controller='admin', action='purge_category', **kwd)) elif operation == "edit": return trans.response.send_redirect( web.url_for(controller='admin', action='edit_category', **kwd)) return self.manage_category_grid(trans, **kwd) @web.expose @web.require_admin def regenerate_statistics(self, trans, **kwd): message = escape(kwd.get('message', '')) status = kwd.get('status', 'done') if 'regenerate_statistics_button' in kwd: trans.app.shed_counter.generate_statistics() message = "Successfully regenerated statistics" return trans.fill_template('/webapps/tool_shed/admin/statistics.mako', message=message, status=status) @web.expose @web.require_admin def manage_role_associations(self, trans, **kwd): """Manage users, groups and repositories associated with a role.""" role_id = kwd.get('id', None) role = repository_util.get_role_by_id(trans.app, role_id) # We currently only have a single role associated with a repository, the repository admin role. repository_role_association = role.repositories[0] repository = repository_role_association.repository associations_dict = repository_util.handle_role_associations( trans.app, role, repository, **kwd) in_users = associations_dict.get('in_users', []) out_users = associations_dict.get('out_users', []) in_groups = associations_dict.get('in_groups', []) out_groups = associations_dict.get('out_groups', []) message = associations_dict.get('message', '') status = associations_dict.get('status', 'done') return trans.fill_template('/webapps/tool_shed/role/role.mako', in_admin_controller=True, repository=repository, role=role, in_users=in_users, out_users=out_users, in_groups=in_groups, out_groups=out_groups, message=message, status=status) @web.expose @web.require_admin def reset_metadata_on_selected_repositories_in_tool_shed( self, trans, **kwd): rmm = repository_metadata_manager.RepositoryMetadataManager( trans.app, trans.user) if 'reset_metadata_on_selected_repositories_button' in kwd: message, status = rmm.reset_metadata_on_selected_repositories( **kwd) else: message = escape(util.restore_text(kwd.get('message', ''))) status = kwd.get('status', 'done') repositories_select_field = rmm.build_repository_ids_select_field( name='repository_ids', multiple=True, display='checkboxes', my_writable=False) return trans.fill_template( '/webapps/tool_shed/common/reset_metadata_on_selected_repositories.mako', repositories_select_field=repositories_select_field, message=message, status=status) @web.expose @web.require_admin def undelete_repository(self, trans, **kwd): message = escape(kwd.get('message', '')) id = kwd.get('id', None) if id: # Undeleting multiple items is currently not allowed (allow_multiple=False), so there will only be 1 id. ids = util.listify(id) count = 0 undeleted_repositories = "" for repository_id in ids: repository = repository_util.get_repository_in_tool_shed( trans.app, repository_id) if repository: if repository.deleted: # Inspect all repository_metadata records to determine those that are installable, and mark # them accordingly. for repository_metadata in repository.metadata_revisions: metadata = repository_metadata.metadata if metadata: if metadata_util.is_downloadable(metadata): repository_metadata.downloadable = True trans.sa_session.add(repository_metadata) # Mark the repository admin role as not deleted. repository_admin_role = repository.admin_role if repository_admin_role is not None: repository_admin_role.deleted = False trans.sa_session.add(repository_admin_role) repository.deleted = False trans.sa_session.add(repository) trans.sa_session.flush() if not repository.deprecated: # Update the repository registry. trans.app.repository_registry.add_entry(repository) count += 1 undeleted_repositories += " %s" % repository.name if count: message = "Undeleted %d %s: %s" % ( count, inflector.cond_plural( count, "repository"), undeleted_repositories) else: message = "No selected repositories were marked deleted, so they could not be undeleted." else: message = "No repository ids received for undeleting." trans.response.send_redirect( web.url_for(controller='admin', action='browse_repositories', message=util.sanitize_text(message), status='done')) @web.expose @web.require_admin def mark_category_deleted(self, trans, **kwd): # TODO: We should probably eliminate the Category.deleted column since it really makes no # sense to mark a category as deleted (category names and descriptions can be changed instead). # If we do this, and the following 2 methods can be eliminated. message = escape(kwd.get('message', '')) id = kwd.get('id', None) if id: ids = util.listify(id) message = "Deleted %d categories: " % len(ids) for category_id in ids: category = suc.get_category(trans.app, category_id) category.deleted = True trans.sa_session.add(category) trans.sa_session.flush() # Update the Tool Shed's repository registry. trans.app.repository_registry.remove_category_entry(category) message += " %s " % escape(category.name) else: message = "No category ids received for deleting." trans.response.send_redirect( web.url_for(controller='admin', action='manage_categories', 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: # - RepoitoryCategoryAssociations where category_id == Category.id message = escape(kwd.get('message', '')) id = kwd.get('id', None) if id: ids = util.listify(id) count = 0 purged_categories = "" message = "Purged %d categories: " % len(ids) for category_id in ids: category = suc.get_category(trans.app, category_id) if category.deleted: # Delete RepositoryCategoryAssociations for rca in category.repositories: trans.sa_session.delete(rca) trans.sa_session.flush() purged_categories += " %s " % category.name message = "Purged %d categories: %s" % (count, escape(purged_categories)) else: message = "No category ids received for purging." trans.response.send_redirect( web.url_for(controller='admin', action='manage_categories', message=util.sanitize_text(message), status='done')) @web.expose @web.require_admin def undelete_category(self, trans, **kwd): message = escape(kwd.get('message', '')) id = kwd.get('id', None) if id: ids = util.listify(id) count = 0 undeleted_categories = "" for category_id in ids: category = suc.get_category(trans.app, category_id) if category.deleted: category.deleted = False trans.sa_session.add(category) trans.sa_session.flush() # Update the Tool Shed's repository registry. trans.app.repository_registry.add_category_entry(category) count += 1 undeleted_categories += " %s" % category.name message = "Undeleted %d categories: %s" % ( count, escape(undeleted_categories)) else: message = "No category ids received for undeleting." trans.response.send_redirect( web.url_for(controller='admin', action='manage_categories', message=util.sanitize_text(message), status='done'))
class RepositoryMetadataGrid(grids.Grid): class IdColumn(grids.IntegerColumn): def get_value(self, trans, grid, repository_metadata): return repository_metadata.id class NameColumn(grids.TextColumn): def get_value(self, trans, grid, repository_metadata): return escape(repository_metadata.repository.name) class OwnerColumn(grids.TextColumn): def get_value(self, trans, grid, repository_metadata): return escape(repository_metadata.repository.user.username) class RevisionColumn(grids.TextColumn): def get_value(self, trans, grid, repository_metadata): repository = repository_metadata.repository return hg_util.get_revision_label( trans.app, repository, repository_metadata.changeset_revision, include_date=True, include_hash=True) class ToolsColumn(grids.TextColumn): def get_value(self, trans, grid, repository_metadata): tools_str = '0' if repository_metadata: metadata = repository_metadata.metadata if metadata: if 'tools' in metadata: # We used to display the following, but grid was too cluttered. # for tool_metadata_dict in metadata[ 'tools' ]: # tools_str += '%s <b>%s</b><br/>' % ( tool_metadata_dict[ 'id' ], tool_metadata_dict[ 'version' ] ) return '%d' % len(metadata['tools']) return tools_str class DatatypesColumn(grids.TextColumn): def get_value(self, trans, grid, repository_metadata): datatypes_str = '0' if repository_metadata: metadata = repository_metadata.metadata if metadata: if 'datatypes' in metadata: # We used to display the following, but grid was too cluttered. # for datatype_metadata_dict in metadata[ 'datatypes' ]: # datatypes_str += '%s<br/>' % datatype_metadata_dict[ 'extension' ] return '%d' % len(metadata['datatypes']) return datatypes_str class WorkflowsColumn(grids.TextColumn): def get_value(self, trans, grid, repository_metadata): workflows_str = '0' if repository_metadata: metadata = repository_metadata.metadata if metadata: if 'workflows' in metadata: # We used to display the following, but grid was too cluttered. # workflows_str += '<b>Workflows:</b><br/>' # metadata[ 'workflows' ] is a list of tuples where each contained tuple is # [ <relative path to the .ga file in the repository>, <exported workflow dict> ] # workflow_tups = metadata[ 'workflows' ] # workflow_metadata_dicts = [ workflow_tup[1] for workflow_tup in workflow_tups ] # for workflow_metadata_dict in workflow_metadata_dicts: # workflows_str += '%s<br/>' % workflow_metadata_dict[ 'name' ] return '%d' % len(metadata['workflows']) return workflows_str class DeletedColumn(grids.BooleanColumn): def get_value(self, trans, grid, repository_metadata): if repository_metadata.repository.deleted: return 'yes' return '' class DeprecatedColumn(grids.BooleanColumn): def get_value(self, trans, grid, repository_metadata): if repository_metadata.repository.deprecated: return 'yes' return '' class MaliciousColumn(grids.BooleanColumn): def get_value(self, trans, grid, repository_metadata): if repository_metadata.malicious: return 'yes' return '' # Grid definition title = "Repository Metadata" model_class = model.RepositoryMetadata default_sort_key = "name" use_hide_message = False columns = [ IdColumn("Id", visible=False, attach_popup=False), NameColumn( "Name", key="name", model_class=model.Repository, link=(lambda item: dict( operation="view_or_manage_repository_revision", id=item.id)), attach_popup=True), OwnerColumn("Owner", attach_popup=False), RevisionColumn("Revision", attach_popup=False), ToolsColumn("Tools", attach_popup=False), DatatypesColumn("Datatypes", attach_popup=False), WorkflowsColumn("Workflows", attach_popup=False), DeletedColumn("Deleted", attach_popup=False), DeprecatedColumn("Deprecated", attach_popup=False), MaliciousColumn("Malicious", attach_popup=False) ] columns.append( grids.MulticolFilterColumn("Search repository name", cols_to_filter=[columns[1]], key="free-text-search", visible=False, filterable="standard")) operations = [ grids.GridOperation( "Delete", allow_multiple=False, allow_popup=True, async_compatible=False, confirm= "Repository metadata records cannot be recovered after they are deleted. Click OK to delete the selected items." ) ] standard_filters = [] default_filter = {} use_paging = False def build_initial_query(self, trans, **kwd): return trans.sa_session.query(model.RepositoryMetadata) \ .join(model.Repository.table)
class UserGrid(grids.Grid): class UserLoginColumn(grids.TextColumn): def get_value(self, trans, grid, user): return escape(user.email) class UserNameColumn(grids.TextColumn): def get_value(self, trans, grid, user): if user.username: return escape(user.username) return 'not set' class GroupsColumn(grids.GridColumn): def get_value(self, trans, grid, user): if user.groups: return len(user.groups) return 0 class RolesColumn(grids.GridColumn): def get_value(self, trans, grid, user): if user.roles: return len(user.roles) return 0 class ExternalColumn(grids.GridColumn): def get_value(self, trans, grid, user): if user.external: return 'yes' return 'no' class LastLoginColumn(grids.GridColumn): def get_value(self, trans, grid, user): if user.galaxy_sessions: return self.format(user.galaxy_sessions[0].update_time) return 'never' class StatusColumn(grids.GridColumn): def get_value(self, trans, grid, user): if user.purged: return "purged" elif user.deleted: return "deleted" return "" class EmailColumn(grids.GridColumn): def filter(self, trans, user, query, column_filter): if column_filter == 'All': return query return query.filter( and_(model.Tool.table.c.user_id == model.User.table.c.id, model.User.table.c.email == column_filter)) title = "Users" model_class = model.User default_sort_key = "email" columns = [ UserLoginColumn( "Email", key="email", link=(lambda item: dict(operation="information", id=item.id)), attach_popup=True, filterable="advanced"), UserNameColumn("User Name", key="username", attach_popup=False, filterable="advanced"), GroupsColumn("Groups", attach_popup=False), RolesColumn("Roles", attach_popup=False), ExternalColumn("External", attach_popup=False), LastLoginColumn("Last Login", format=time_ago), StatusColumn("Status", attach_popup=False), # Columns that are valid for filtering but are not visible. EmailColumn("Email", key="email", visible=False) ] columns.append( grids.MulticolFilterColumn("Search", cols_to_filter=[columns[0], columns[1]], key="free-text-search", visible=False, filterable="standard")) global_actions = [ grids.GridAction( "Create new user", dict(controller='admin', action='users', operation='create')) ] operations = [ grids.GridOperation( "Manage Roles and Groups", condition=(lambda item: not item.deleted), allow_multiple=False, url_args=dict(action="manage_roles_and_groups_for_user")), grids.GridOperation("Reset Password", condition=(lambda item: not item.deleted), allow_multiple=True, allow_popup=False, url_args=dict(action="reset_user_password")) ] standard_filters = [ grids.GridColumnFilter("Active", args=dict(deleted=False)), grids.GridColumnFilter("Deleted", args=dict(deleted=True, purged=False)), grids.GridColumnFilter("Purged", args=dict(purged=True)), grids.GridColumnFilter("All", args=dict(deleted='All')) ] use_paging = False def get_current_item(self, trans, **kwargs): return trans.user
class RoleGrid(grids.Grid): class NameColumn(grids.TextColumn): def get_value(self, trans, grid, role): return escape(str(role.name)) class DescriptionColumn(grids.TextColumn): def get_value(self, trans, grid, role): if role.description: return str(role.description) return '' class TypeColumn(grids.TextColumn): def get_value(self, trans, grid, role): return str(role.type) class StatusColumn(grids.GridColumn): def get_value(self, trans, grid, role): if role.deleted: return "deleted" return "" class GroupsColumn(grids.GridColumn): def get_value(self, trans, grid, role): if role.groups: return len(role.groups) return 0 class RepositoriesColumn(grids.GridColumn): def get_value(self, trans, grid, role): if role.repositories: return len(role.repositories) return 0 class UsersColumn(grids.GridColumn): def get_value(self, trans, grid, role): if role.users: return len(role.users) return 0 title = "Roles" model_class = model.Role default_sort_key = "name" columns = [ NameColumn("Name", key="name", link=(lambda item: dict( operation="Manage role associations", id=item.id)), attach_popup=True, filterable="advanced"), DescriptionColumn("Description", key='description', attach_popup=False, filterable="advanced"), GroupsColumn("Groups", attach_popup=False), RepositoriesColumn("Repositories", attach_popup=False), UsersColumn("Users", 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]], key="free-text-search", visible=False, filterable="standard")) global_actions = [ grids.GridAction( "Add new role", dict(controller='admin', action='roles', operation='create')) ] # Repository admin roles currently do not have any operations since they are managed automatically based # on other events. For example, if a repository is renamed, its associated admin role is automatically # renamed accordingly and if a repository is deleted its associated admin role is automatically deleted. operations = [ grids.GridOperation("Rename", condition=(lambda item: not item.deleted and not item.is_repository_admin_role), allow_multiple=False, url_args=dict(action="rename_role")), grids.GridOperation("Delete", condition=(lambda item: not item.deleted and not item.is_repository_admin_role), allow_multiple=True, url_args=dict(action="mark_role_deleted")), grids.GridOperation("Undelete", condition=(lambda item: item.deleted and not item. is_repository_admin_role), allow_multiple=True, url_args=dict(action="undelete_role")), grids.GridOperation("Purge", condition=(lambda item: item.deleted and not item. is_repository_admin_role), allow_multiple=True, url_args=dict(action="purge_role")) ] standard_filters = [ grids.GridColumnFilter("Active", args=dict(deleted=False)), grids.GridColumnFilter("Deleted", args=dict(deleted=True)), grids.GridColumnFilter("All", args=dict(deleted='All')) ] use_paging = False def apply_query_filter(self, trans, query, **kwd): return query.filter(model.Role.type != model.Role.types.PRIVATE)