# HG changeset patch -- Bitbucket.org # Project galaxy-dist # URL http://bitbucket.org/galaxy/galaxy-dist/overview # User jeremy goecks <jeremy.goecks@emory.edu> # Date 1282595659 14400 # Node ID 5d3e57e28fb7fa524a47d3a9b7e99f73ebca6676 # Parent 60448575467fef8f6a42d83906ee2a622511c541 Enhance grid framework to enable custom column sorting. Sort criteria are now mapped to a column, and the column defines the sorting to be done on the grid query. Default sorting behavior has not changed. In addition, column's model_class attribute now defaults to the grid's model_class; this should make column definitions more intuitive and shorter. Used custom sorting functionality to enable (a) case-insensitive sorting of text fields and (b) case-insensitive sorting of published item by username. --- a/templates/history/list_published.mako +++ b/templates/history/list_published.mako @@ -28,7 +28,7 @@ <div style="overflow: auto; height: 100%;"><div class="page-container" style="padding: 10px;"> - ${unicode( grid, 'utf-8' )} + ${h.to_unicode( grid )} </div></div> --- a/lib/galaxy/web/controllers/dataset.py +++ b/lib/galaxy/web/controllers/dataset.py @@ -107,14 +107,14 @@ class HistoryDatasetAssociationListGrid( title = "Saved Datasets" model_class = model.HistoryDatasetAssociation template='/dataset/grid.mako' - default_sort_key = "-create_time" + default_sort_key = "-update_time" columns = [ - grids.TextColumn( "Name", key="name", model_class=model.HistoryDatasetAssociation, + grids.TextColumn( "Name", key="name", # Link name to dataset's history. - link=( lambda item: iff( item.history.deleted, None, dict( operation="switch", id=item.id ) ) ), filterable="advanced", attach_popup=True ), + link=( lambda item: iff( item.history.deleted, None, dict( operation="switch", id=item.id ) ) ), filterable="advanced", attach_popup=True ), HistoryColumn( "History", key="history", link=( lambda item: iff( item.history.deleted, None, dict( operation="switch_history", id=item.id ) ) ) ), - grids.IndividualTagsColumn( "Tags", key="tags", model_class=model.HistoryDatasetAssociation, model_tag_association_class=model.HistoryDatasetAssociationTagAssociation, filterable="advanced", grid_name="HistoryDatasetAssocationListGrid" ), + grids.IndividualTagsColumn( "Tags", key="tags", model_tag_association_class=model.HistoryDatasetAssociationTagAssociation, filterable="advanced", grid_name="HistoryDatasetAssocationListGrid" ), StatusColumn( "Status", key="deleted", attach_popup=False ), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), ] --- a/lib/galaxy/web/controllers/history.py +++ b/lib/galaxy/web/controllers/history.py @@ -38,16 +38,15 @@ class HistoryListGrid( grids.Grid ): title = "Saved Histories" model_class = model.History template='/history/grid.mako' - default_sort_key = "-create_time" + default_sort_key = "-update_time" columns = [ - NameColumn( "Name", key="name", model_class=model.History, + NameColumn( "Name", key="name", link=( lambda history: iff( history.deleted, None, dict( operation="Switch", id=history.id ) ) ), attach_popup=True, filterable="advanced" ), DatasetsByStateColumn( "Datasets (by state)", ncells=4 ), - grids.IndividualTagsColumn( "Tags", key="tags", model_class=model.History, \ - model_tag_association_class=model.HistoryTagAssociation, \ + grids.IndividualTagsColumn( "Tags", key="tags", model_tag_association_class=model.HistoryTagAssociation, \ filterable="advanced", grid_name="HistoryListGrid" ), - grids.SharingStatusColumn( "Sharing", key="sharing", model_class=model.History, filterable="advanced", sortable=False ), + 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 that are valid for filtering but are not visible. @@ -130,10 +129,10 @@ class HistoryAllPublishedGrid( grids.Gri default_filter = dict( public_url="All", username="All", tags="All" ) use_async = True columns = [ - NameURLColumn( "Name", key="name", model_class=model.History, filterable="advanced" ), - grids.OwnerAnnotationColumn( "Annotation", key="annotation", model_class=model.History, model_annotation_association_class=model.HistoryAnnotationAssociation, filterable="advanced" ), - grids.OwnerColumn( "Owner", key="username", model_class=model.User, filterable="advanced", sortable=False ), - grids.CommunityTagsColumn( "Community Tags", key="tags", model_class=model.History, model_tag_association_class=model.HistoryTagAssociation, filterable="advanced", grid_name="PublicHistoryListGrid" ), + NameURLColumn( "Name", key="name", filterable="advanced" ), + grids.OwnerAnnotationColumn( "Annotation", key="annotation", model_annotation_association_class=model.HistoryAnnotationAssociation, filterable="advanced" ), + grids.OwnerColumn( "Owner", key="owner", model_class=model.User, filterable="advanced" ), + grids.CommunityTagsColumn( "Community Tags", key="tags", model_tag_association_class=model.HistoryTagAssociation, filterable="advanced", grid_name="PublicHistoryListGrid" ), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ) ] columns.append( --- a/lib/galaxy/web/controllers/workflow.py +++ b/lib/galaxy/web/controllers/workflow.py @@ -37,8 +37,8 @@ class StoredWorkflowListGrid( grids.Grid default_filter = { "name" : "All", "tags": "All" } default_sort_key = "-update_time" columns = [ - grids.TextColumn( "Name", key="name", model_class=model.StoredWorkflow, attach_popup=True, filterable="advanced" ), - grids.IndividualTagsColumn( "Tags", "tags", model.StoredWorkflow, model.StoredWorkflowTagAssociation, filterable="advanced", grid_name="StoredWorkflowListGrid" ), + 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 ), @@ -67,10 +67,10 @@ class StoredWorkflowAllPublishedGrid( gr default_filter = dict( public_url="All", username="All", tags="All" ) use_async = True columns = [ - grids.PublicURLColumn( "Name", key="name", model_class=model.StoredWorkflow, filterable="advanced" ), - grids.OwnerAnnotationColumn( "Annotation", key="annotation", model_class=model.StoredWorkflow, model_annotation_association_class=model.StoredWorkflowAnnotationAssociation, filterable="advanced" ), - grids.OwnerColumn( "Owner", key="username", model_class=model.User, filterable="advanced", sortable=False ), - grids.CommunityTagsColumn( "Community Tags", key="tags", model_class=model.StoredWorkflow, model_tag_association_class=model.StoredWorkflowTagAssociation, filterable="advanced", grid_name="PublicWorkflowListGrid" ), + grids.PublicURLColumn( "Name", key="name", filterable="advanced" ), + grids.OwnerAnnotationColumn( "Annotation", key="annotation", model_annotation_association_class=model.StoredWorkflowAnnotationAssociation, filterable="advanced" ), + grids.OwnerColumn( "Owner", key="owner", model_class=model.User, filterable="advanced" ), + grids.CommunityTagsColumn( "Community Tags", key="tags", model_tag_association_class=model.StoredWorkflowTagAssociation, filterable="advanced", grid_name="PublicWorkflowListGrid" ), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ) ] columns.append( --- a/templates/visualization/list_published.mako +++ b/templates/visualization/list_published.mako @@ -28,7 +28,7 @@ <div style="overflow: auto; height: 100%;"><div class="page-container" style="padding: 10px;"> - ${unicode( grid, 'utf-8' )} + ${h.to_unicode( grid )} </div></div> --- a/templates/workflow/list_published.mako +++ b/templates/workflow/list_published.mako @@ -28,7 +28,7 @@ <div style="overflow: auto; height: 100%;"><div class="page-container" style="padding: 10px;"> - ${unicode( grid, 'utf-8' )} + ${h.to_unicode( grid )} </div></div> --- a/lib/galaxy/web/controllers/visualization.py +++ b/lib/galaxy/web/controllers/visualization.py @@ -12,11 +12,11 @@ class VisualizationListGrid( grids.Grid default_sort_key = "-update_time" default_filter = dict( title="All", deleted="False", tags="All", sharing="All" ) columns = [ - grids.TextColumn( "Title", key="title", model_class=model.Visualization, attach_popup=True, + grids.TextColumn( "Title", key="title", attach_popup=True, link=( lambda item: dict( controller="tracks", action="browser", id=item.id ) ) ), - grids.TextColumn( "Dbkey", key="dbkey", model_class=model.Visualization ), - grids.IndividualTagsColumn( "Tags", key="tags", model_class=model.Visualization, model_tag_association_class=model.VisualizationTagAssociation, filterable="advanced", grid_name="VisualizationListGrid" ), - grids.SharingStatusColumn( "Sharing", key="sharing", model_class=model.Visualization, filterable="advanced", sortable=False ), + 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 ), ] @@ -47,10 +47,10 @@ class VisualizationAllPublishedGrid( gri default_sort_key = "-update_time" default_filter = dict( title="All", username="All" ) columns = [ - grids.PublicURLColumn( "Title", key="title", model_class=model.Visualization, filterable="advanced" ), - grids.OwnerAnnotationColumn( "Annotation", key="annotation", model_class=model.Visualization, model_annotation_association_class=model.VisualizationAnnotationAssociation, filterable="advanced" ), - grids.OwnerColumn( "Owner", key="username", model_class=model.User, filterable="advanced", sortable=False ), - grids.CommunityTagsColumn( "Community Tags", key="tags", model_class=model.Visualization, model_tag_association_class=model.VisualizationTagAssociation, filterable="advanced", grid_name="VisualizationAllPublishedGrid" ), + grids.PublicURLColumn( "Title", key="title", filterable="advanced" ), + grids.OwnerAnnotationColumn( "Annotation", key="annotation", model_annotation_association_class=model.VisualizationAnnotationAssociation, filterable="advanced" ), + grids.OwnerColumn( "Owner", key="owner", model_class=model.User, filterable="advanced" ), + grids.CommunityTagsColumn( "Community Tags", key="tags", model_tag_association_class=model.VisualizationTagAssociation, filterable="advanced", grid_name="VisualizationAllPublishedGrid" ), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ) ] columns.append( --- a/templates/page/index.mako +++ b/templates/page/index.mako @@ -15,7 +15,7 @@ <div style="overflow: auto; height: 100%;"><div class="page-container" style="padding: 10px;"> - ${grid} + ${h.to_unicode( grid )} <br><br><h2>Pages shared with you by others</h2> --- a/lib/galaxy/web/controllers/page.py +++ b/lib/galaxy/web/controllers/page.py @@ -25,11 +25,11 @@ class PageListGrid( grids.Grid ): default_filter = { "published" : "All", "tags" : "All", "title" : "All", "sharing" : "All" } default_sort_key = "-create_time" columns = [ - grids.TextColumn( "Title", key="title", model_class=model.Page, attach_popup=True, filterable="advanced" ), + grids.TextColumn( "Title", key="title", attach_popup=True, filterable="advanced" ), URLColumn( "Public URL" ), - grids.OwnerAnnotationColumn( "Annotation", key="annotation", model_class=model.Page, model_annotation_association_class=model.PageAnnotationAssociation, filterable="advanced" ), - grids.IndividualTagsColumn( "Tags", key="tags", model_class=model.Page, model_tag_association_class=model.PageTagAssociation, filterable="advanced", grid_name="PageListGrid" ), - grids.SharingStatusColumn( "Sharing", key="sharing", model_class=model.Page, filterable="advanced", sortable=False ), + 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 ), ] @@ -61,10 +61,10 @@ class PageAllPublishedGrid( grids.Grid ) default_sort_key = "-update_time" default_filter = dict( title="All", username="All" ) columns = [ - grids.PublicURLColumn( "Title", key="title", model_class=model.Page, filterable="advanced" ), - grids.OwnerAnnotationColumn( "Annotation", key="annotation", model_class=model.Page, model_annotation_association_class=model.PageAnnotationAssociation, filterable="advanced" ), - grids.OwnerColumn( "Owner", key="username", model_class=model.User, filterable="advanced", sortable=False ), - grids.CommunityTagsColumn( "Community Tags", key="tags", model_class=model.Page, model_tag_association_class=model.PageTagAssociation, filterable="advanced", grid_name="PageAllPublishedGrid" ), + grids.PublicURLColumn( "Title", key="title", filterable="advanced" ), + grids.OwnerAnnotationColumn( "Annotation", key="annotation", model_annotation_association_class=model.PageAnnotationAssociation, filterable="advanced" ), + grids.OwnerColumn( "Owner", key="owner", model_class=model.User, filterable="advanced" ), + grids.CommunityTagsColumn( "Community Tags", key="tags", model_tag_association_class=model.PageTagAssociation, filterable="advanced", grid_name="PageAllPublishedGrid" ), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ) ] columns.append( @@ -107,12 +107,12 @@ class HistorySelectionGrid( ItemSelectio title = "Saved Histories" model_class = model.History columns = [ - ItemSelectionGrid.NameColumn( "Name", key="name", model_class=model.History, filterable="advanced" ), - grids.IndividualTagsColumn( "Tags", key="tags", model_class=model.History, model_tag_association_class=model.HistoryTagAssociation, filterable="advanced"), + ItemSelectionGrid.NameColumn( "Name", key="name", filterable="advanced" ), + grids.IndividualTagsColumn( "Tags", key="tags", model_tag_association_class=model.HistoryTagAssociation, filterable="advanced"), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), # Columns that are valid for filtering but are not visible. grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ), - grids.SharingStatusColumn( "Sharing", key="sharing", model_class=model.History, filterable="advanced", sortable=False, visible=False ), + grids.SharingStatusColumn( "Sharing", key="sharing", filterable="advanced", sortable=False, visible=False ), ] columns.append( grids.MulticolFilterColumn( @@ -130,12 +130,12 @@ class HistoryDatasetAssociationSelection title = "Saved Datasets" model_class = model.HistoryDatasetAssociation columns = [ - ItemSelectionGrid.NameColumn( "Name", key="name", model_class=model.HistoryDatasetAssociation, filterable="advanced" ), - grids.IndividualTagsColumn( "Tags", key="tags", model_class=model.HistoryDatasetAssociation, model_tag_association_class=model.HistoryDatasetAssociationTagAssociation, filterable="advanced"), + ItemSelectionGrid.NameColumn( "Name", key="name", filterable="advanced" ), + grids.IndividualTagsColumn( "Tags", key="tags", model_tag_association_class=model.HistoryDatasetAssociationTagAssociation, filterable="advanced"), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), # Columns that are valid for filtering but are not visible. grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ), - grids.SharingStatusColumn( "Sharing", key="sharing", model_class=model.HistoryDatasetAssociation, filterable="advanced", sortable=False, visible=False ), + grids.SharingStatusColumn( "Sharing", key="sharing", filterable="advanced", sortable=False, visible=False ), ] columns.append( grids.MulticolFilterColumn( @@ -155,12 +155,12 @@ class WorkflowSelectionGrid( ItemSelecti title = "Saved Workflows" model_class = model.StoredWorkflow columns = [ - ItemSelectionGrid.NameColumn( "Name", key="name", model_class=model.StoredWorkflow, filterable="advanced" ), - grids.IndividualTagsColumn( "Tags", key="tags", model_class=model.StoredWorkflow, model_tag_association_class=model.StoredWorkflowTagAssociation, filterable="advanced"), + ItemSelectionGrid.NameColumn( "Name", key="name", filterable="advanced" ), + grids.IndividualTagsColumn( "Tags", key="tags", model_tag_association_class=model.StoredWorkflowTagAssociation, filterable="advanced"), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), # Columns that are valid for filtering but are not visible. grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ), - grids.SharingStatusColumn( "Sharing", key="sharing", model_class=model.StoredWorkflow, filterable="advanced", sortable=False, visible=False ), + grids.SharingStatusColumn( "Sharing", key="sharing", filterable="advanced", sortable=False, visible=False ), ] columns.append( grids.MulticolFilterColumn( @@ -175,12 +175,12 @@ class PageSelectionGrid( ItemSelectionGr title = "Saved Pages" model_class = model.Page columns = [ - grids.TextColumn( "Title", key="title", model_class=model.Page, filterable="advanced" ), - grids.IndividualTagsColumn( "Tags", key="tags", model_class=model.Page, model_tag_association_class=model.PageTagAssociation, filterable="advanced"), + grids.TextColumn( "Title", key="title", filterable="advanced" ), + grids.IndividualTagsColumn( "Tags", key="tags", model_tag_association_class=model.PageTagAssociation, filterable="advanced"), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), # Columns that are valid for filtering but are not visible. grids.DeletedColumn( "Deleted", key="deleted", visible=False, filterable="advanced" ), - grids.SharingStatusColumn( "Sharing", key="sharing", model_class=model.Page, filterable="advanced", sortable=False, visible=False ), + grids.SharingStatusColumn( "Sharing", key="sharing", filterable="advanced", sortable=False, visible=False ), ] columns.append( grids.MulticolFilterColumn( @@ -195,10 +195,10 @@ class VisualizationSelectionGrid( ItemSe title = "Saved Visualizations" model_class = model.Visualization columns = [ - grids.TextColumn( "Title", key="title", model_class=model.Visualization, filterable="advanced" ), - grids.TextColumn( "Type", key="type", model_class=model.Visualization ), - grids.IndividualTagsColumn( "Tags", key="tags", model_class=model.Visualization, model_tag_association_class=model.VisualizationTagAssociation, filterable="advanced", grid_name="VisualizationListGrid" ), - grids.SharingStatusColumn( "Sharing", key="sharing", model_class=model.Visualization, filterable="advanced", sortable=False ), + grids.TextColumn( "Title", key="title", filterable="advanced" ), + grids.TextColumn( "Type", key="type" ), + 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( "Last Updated", key="update_time", format=time_ago ), ] columns.append( @@ -299,8 +299,8 @@ class PageController( BaseController, Sh return self.sharing( trans, **kwargs ) session.flush() - # Build grid HTML and make sure to encode in utf-8 to support unicode characters. - grid = unicode( self._page_list( trans, *args, **kwargs ), 'utf-8' ) + # Build grid HTML. + grid = self._page_list( trans, *args, **kwargs ) # Build list of pages shared with user. shared_by_others = trans.sa_session \ @@ -316,7 +316,7 @@ class PageController( BaseController, Sh @web.expose def list_published( self, trans, *args, **kwargs ): - grid = unicode( self._all_published_list( trans, *args, **kwargs ), 'utf-8' ) + grid = self._all_published_list( trans, *args, **kwargs ) if 'async' in kwargs: return grid else: --- a/lib/galaxy/web/framework/helpers/grids.py +++ b/lib/galaxy/web/framework/helpers/grids.py @@ -4,6 +4,7 @@ from galaxy.web.framework.helpers import from galaxy.web import url_for from galaxy.util.json import from_json_string, to_json_string from galaxy.util.odict import odict +from galaxy.web.framework.helpers import to_unicode import sys, logging, math @@ -41,12 +42,25 @@ class Grid( object ): if operation.allow_multiple: self.has_multiple_item_operations = True break + + # If a column does not have a model class, set the column's model class + # to be the grid's model class. + for column in self.columns: + if not column.model_class: + column.model_class = self.model_class + def __call__( self, trans, **kwargs ): + # + # Get basics. + # webapp = kwargs.get( 'webapp', 'galaxy' ) status = kwargs.get( 'status', None ) message = kwargs.get( 'message', None ) + + # # Build a base filter and sort key that is the combination of the saved state and defaults. # Saved state takes preference over defaults. + # base_filter = {} if self.default_filter: # default_filter is a dictionary that provides a default set of filters based on the grid's columns. @@ -70,8 +84,11 @@ class Grid( object ): use_default_filter = False if use_default_filter_str: use_default_filter = ( use_default_filter_str.lower() == 'true' ) + + # # Process filtering arguments to (a) build a query that represents the filter and (b) builds a - # dictionary that denotes the current filter. + # dictionary that denotes the current filter. + # cur_filter_dict = {} for column in self.columns: if column.key: @@ -134,23 +151,39 @@ class Grid( object ): if not isinstance( column_filter, basestring ): column_filter = unicode(column_filter) extra_url_args[ "f-" + column.key ] = column_filter.encode("utf-8") + + # # Process sort arguments. + # sort_key = None if 'sort' in kwargs: sort_key = kwargs['sort'] elif base_sort_key: sort_key = base_sort_key - if sort_key: + + if sort_key: if sort_key.startswith( "-" ): - # Can't use lower() on timestamp or integer objects, so func.lower() is not used here... - query = query.order_by( self.model_class.table.c.get( sort_key[1:] ).desc() ) + ascending = False + column_key = sort_key[1:] else: - # See reason for not using lower() to do case-insensitive sorting. - query = query.order_by( self.model_class.table.c.get( sort_key ).asc() ) - extra_url_args['sort'] = sort_key + ascending = True + column_key = sort_key + + # Sort key is a column key. + for column in self.columns: + if column.key == column_key: + query = column.sort( query, ascending ) + break + extra_url_args['sort'] = sort_key + + # # There might be a current row + # current_item = self.get_current_item( trans, **kwargs ) + + # # Process page number. + # if self.use_paging: if 'page' in kwargs: if kwargs['page'] == 'all': @@ -175,7 +208,10 @@ class Grid( object ): # Defaults. page_num = 1 num_pages = 1 + + # # Preserve grid state: save current filter and sort key. + # if self.preserve_state: pref_name = unicode( self.__class__.__name__ + self.cur_filter_pref_name ) trans.get_user().preferences[pref_name] = unicode( to_json_string( cur_filter_dict ) ) @@ -191,7 +227,10 @@ class Grid( object ): params['async'] = ( 'async' in kwargs ) params['webapp'] = webapp trans.log_action( trans.get_user(), unicode( "grid.view" ), context, params ) + + # # Render grid. + # def url( *args, **kwargs ): # Only include sort/filter arguments if not linking to another # page. This is a bit of a hack. @@ -211,9 +250,14 @@ class Grid( object ): else: new_kwargs[ 'id' ] = trans.security.encode_id( id ) return url_for( **new_kwargs ) - use_panels = ( 'use_panels' in kwargs ) and ( kwargs['use_panels'] in [ True, 'True', 'true' ] ) - async_request = ( ( self.use_async ) and ( 'async' in kwargs ) and ( kwargs['async'] in [ True, 'True', 'true'] ) ) - return trans.fill_template( iff( async_request, self.async_template, self.template), + use_panels = ( kwargs.get( 'use_panels', False ) in [ True, 'True', 'true' ] ) + async_request = ( ( self.use_async ) and ( kwargs.get( 'async', False ) in [ True, 'True', 'true'] ) ) + # Currently, filling the template returns a str object; this requires decoding the string into a + # unicode object within mako templates. What probably should be done is to return the template as + # utf-8 unicode; however, this would require encoding the object as utf-8 before returning the grid + # results via a controller method, which is require substantial changes. Hence, for now, return grid + # as str. + return trans.fill_template( iff( async_request, self.async_template, self.template ), grid=self, query=query, cur_page_num = page_num, @@ -258,11 +302,14 @@ class Grid( object ): return query class GridColumn( object ): - def __init__( self, label, grid=None, key=None, model_class=None, method=None, format=None, link=None, attach_popup=False, visible=True, ncells=1, - # Valid values for filterable are ['standard', 'advanced', None] - filterable=None, sortable=True ): + def __init__( self, label, key=None, model_class=None, method=None, format=None, \ + link=None, attach_popup=False, visible=True, ncells=1, \ + # Valid values for filterable are ['standard', 'advanced', None] + filterable=None, sortable=True ): + """ + Create a grid column. + """ self.label = label - self.grid = grid self.key = key self.model_class = model_class self.method = method @@ -272,12 +319,9 @@ class GridColumn( object ): self.visible = visible self.ncells = ncells self.filterable = filterable - # Currently can only sort of columns that have a database - # representation, not purely derived. - if self.key and sortable: - self.sortable = True - else: - self.sortable = False + # Column must have a key to be sortable. + self.sortable = ( self.key is not None and sortable ) + def get_value( self, trans, grid, item ): if self.method: value = getattr( grid, self.method )( trans, item ) @@ -288,10 +332,12 @@ class GridColumn( object ): if self.format: value = self.format( value ) return value + def get_link( self, trans, grid, item ): if self.link and self.link( item ): return self.link( item ) return None + def filter( self, trans, user, query, column_filter ): """ Modify query to reflect the column filter. """ if column_filter == "All": @@ -301,6 +347,7 @@ class GridColumn( object ): elif column_filter == "False": query = query.filter_by( **{ self.key: False } ) return query + def get_accepted_filters( self ): """ Returns a list of accepted filters for this column. """ accepted_filters_vals = [ "False", "True", "All" ] @@ -309,6 +356,15 @@ class GridColumn( object ): args = { self.key: val } accepted_filters.append( GridColumnFilter( val, args) ) return accepted_filters + + def sort( self, query, ascending ): + """ Sort query using this column. """ + if ascending: + query = query.order_by( self.model_class.table.c.get( self.key ).asc() ) + else: + query = query.order_by( self.model_class.table.c.get( self.key ).desc() ) + return query + class TextColumn( GridColumn ): """ Generic column that employs freetext and, hence, supports freetext, case-independent filtering. """ @@ -332,6 +388,14 @@ class TextColumn( GridColumn ): """ Returns a SQLAlchemy criterion derived for a single filter. Single filter is the most basic filter--usually a string--and cannot be a list. """ model_class_key_field = getattr( self.model_class, self.key ) return func.lower( model_class_key_field ).like( "%" + a_filter.lower() + "%" ) + + def sort( self, query, ascending ): + """ Sort column using case-insensitive alphabetical sorting. """ + if ascending: + query = query.order_by( func.lower ( self.model_class.table.c.get( self.key ) ).asc() ) + else: + query = query.order_by( func.lower( self.model_class.table.c.get( self.key ) ).desc() ) + return query class IntegerColumn( TextColumn ): """ @@ -357,7 +421,7 @@ class IntegerColumn( TextColumn ): class OwnerAnnotationColumn( TextColumn, UsesAnnotations ): """ Column that displays and filters item owner's annotations. """ - def __init__( self, col_name, key, model_class, model_annotation_association_class, filterable ): + def __init__( self, col_name, key, model_class=None, model_annotation_association_class=None, filterable=None ): GridColumn.__init__( self, col_name, key=key, model_class=model_class, filterable=filterable ) self.sortable = False self.model_annotation_association_class = model_annotation_association_class @@ -375,11 +439,9 @@ class OwnerAnnotationColumn( TextColumn, class CommunityTagsColumn( TextColumn ): """ Column that supports community tags. """ - def __init__( self, col_name, key, model_class, model_tag_association_class, filterable, grid_name=None ): - GridColumn.__init__( self, col_name, key=key, model_class=model_class, filterable=filterable ) + def __init__( self, col_name, key, model_class=None, model_tag_association_class=None, filterable=None, grid_name=None ): + GridColumn.__init__( self, col_name, key=key, model_class=model_class, filterable=filterable, sortable=False ) self.model_tag_association_class = model_tag_association_class - # Tags cannot be sorted. - self.sortable = False # Column-specific attributes. self.grid_name = grid_name def get_value( self, trans, grid, item ): @@ -464,6 +526,15 @@ class OwnerColumn( TextColumn ): """ Column that lists item's owner. """ def get_value( self, trans, grid, item ): return item.user.username + + def sort( self, query, ascending ): + """ Sort column using case-insensitive alphabetical sorting on item's username. """ + if ascending: + query = query.order_by( func.lower ( self.model_class.username ).asc() ) + else: + query = query.order_by( func.lower( self.model_class.username ).desc() ) + return query + class PublicURLColumn( TextColumn ): """ Column displays item's public URL based on username and slug. """ @@ -474,7 +545,7 @@ class PublicURLColumn( TextColumn ): # TODO: provide link to set username. return None elif not item.user.slug: - # TODO: provide link to set slg + # TODO: provide link to set slug. return None class DeletedColumn( GridColumn ): --- a/templates/page/list_published.mako +++ b/templates/page/list_published.mako @@ -28,6 +28,7 @@ <div style="overflow: auto; height: 100%;"><div class="page-container" style="padding: 10px;"> - ${grid} - + ${h.to_unicode( grid )} + </div> + </div></%def> --- a/templates/visualization/list.mako +++ b/templates/visualization/list.mako @@ -13,7 +13,7 @@ <div style="overflow: auto; height: 100%;"><div class="page-container" style="padding: 10px;"> - ${grid} + ${h.to_unicode( grid )} <br><br><h2>Visualizations shared with you by others</h2>