details: http://www.bx.psu.edu/hg/galaxy/rev/83dc9981e3c4 changeset: 2783:83dc9981e3c4 user: jeremy goecks <jeremy.goecks@emory.edu> date: Fri Sep 25 17:06:45 2009 -0400 description: Grid states (filter, sorting) can be preserved. 5 file(s) affected in this change: lib/galaxy/model/__init__.py lib/galaxy/model/mapping.py lib/galaxy/model/migrate/versions/0020_user_prefs.py lib/galaxy/web/framework/helpers/grids.py templates/history/grid.mako diffs (252 lines): diff -r 6a86a558f405 -r 83dc9981e3c4 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py Fri Sep 25 11:32:47 2009 -0400 +++ b/lib/galaxy/model/__init__.py Fri Sep 25 17:06:45 2009 -0400 @@ -1306,6 +1306,12 @@ class PageTagAssociation ( ItemTagAssociation ): pass + +class UserPreference ( object ): + def __init( self, user_id=None, name=None, value=None ): + self.user_id = user_id + self.name = name + self.value = value ## ---- Utility methods ------------------------------------------------------- diff -r 6a86a558f405 -r 83dc9981e3c4 lib/galaxy/model/mapping.py --- a/lib/galaxy/model/mapping.py Fri Sep 25 11:32:47 2009 -0400 +++ b/lib/galaxy/model/mapping.py Fri Sep 25 17:06:45 2009 -0400 @@ -585,7 +585,13 @@ Column( "user_tname", TrimmedString(255), index=True), Column( "value", TrimmedString(255), index=True), Column( "user_value", TrimmedString(255), index=True) ) - + +UserPreference.table = Table( "user_preference", metadata, + Column( "id", Integer, primary_key=True ), + Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), + Column( "name", Unicode( 255 ), index=True), + Column( "value", Unicode( 1024 ) ) ) + # With the tables defined we can define the mappers and setup the # relationships between the model objects. @@ -741,6 +747,7 @@ stored_workflow_menu_entries=relation( StoredWorkflowMenuEntry, backref="user", cascade="all, delete-orphan", collection_class=ordering_list( 'order_index' ) ), + preferences=relation( UserPreference, backref="user", order_by=UserPreference.table.c.id), # addresses=relation( UserAddress, # primaryjoin=( User.table.c.id == UserAddress.table.c.user_id ) ) ) ) @@ -1010,6 +1017,10 @@ properties=dict( tag=relation(Tag, backref="tagged_pages") ), primary_key=[PageTagAssociation.table.c.page_id, PageTagAssociation.table.c.tag_id] ) + +assign_mapper( context, UserPreference, UserPreference.table, + properties = {} + ) def db_next_hid( self ): """ diff -r 6a86a558f405 -r 83dc9981e3c4 lib/galaxy/model/migrate/versions/0020_user_prefs.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/galaxy/model/migrate/versions/0020_user_prefs.py Fri Sep 25 17:06:45 2009 -0400 @@ -0,0 +1,45 @@ +""" +This migration script adds a user preferences table to Galaxy. +""" + +from sqlalchemy import * +from migrate import * + +import datetime +now = datetime.datetime.utcnow + +import logging +log = logging.getLogger( __name__ ) + +metadata = MetaData( migrate_engine ) + +def display_migration_details(): + print "" + print "This migration script adds a user preferences table to Galaxy." + print "" + + +# New table to support user preferences. + +UserPreference_table = Table( "user_preference", metadata, + Column( "id", Integer, primary_key=True ), + Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), + Column( "name", Unicode( 255 ), index=True), + Column( "value", Unicode( 1024 ) ) ) + +def upgrade(): + display_migration_details() + metadata.reflect() + try: + UserPreference_table.create() + except Exception, e: + print str(e) + log.debug( "Creating user_preference table failed: %s" % str( e ) ) + +def downgrade(): + metadata.reflect() + try: + UserPreference_table.drop() + except Exception, e: + print str(e) + log.debug( "Dropping user_preference table failed: %s" % str( e ) ) \ No newline at end of file diff -r 6a86a558f405 -r 83dc9981e3c4 lib/galaxy/web/framework/helpers/grids.py --- a/lib/galaxy/web/framework/helpers/grids.py Fri Sep 25 11:32:47 2009 -0400 +++ b/lib/galaxy/web/framework/helpers/grids.py Fri Sep 25 17:06:45 2009 -0400 @@ -2,6 +2,7 @@ from galaxy.model.orm import * from galaxy.web import url_for +from galaxy.util.json import from_json_string, to_json_string import sys, logging @@ -21,6 +22,10 @@ standard_filters = [] default_filter = None default_sort_key = None + preserve_state = True + # Set preference names. + cur_filter_pref_name = ".filter" + cur_sort_key_pref_name = ".sort_key" pass_through_operations = {} def __init__( self ): # Determine if any multiple row operations are defined @@ -29,26 +34,47 @@ if operation.allow_multiple: self.has_multiple_item_operations = True break + def __call__( self, trans, **kwargs ): status = kwargs.get( 'status', None ) message = kwargs.get( 'message', None ) session = trans.sa_session + + # 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: + base_filter = self.default_filter.copy() + base_sort_key = self.default_sort_key + if self.preserve_state: + saved_filter_pref = trans.sa_session.query( UserPreference ).\ + filter_by( name=self.__class__.__name__ + self.cur_filter_pref_name, user_id=trans.get_user().id ).first() + if saved_filter_pref: + saved_filter = from_json_string( saved_filter_pref.value ) + base_filter.update( saved_filter ) + + saved_sort_key_pref = trans.sa_session.query( UserPreference ).\ + filter_by( name=self.__class__.__name__ + self.cur_sort_key_pref_name, user_id=trans.get_user().id ).first() + if saved_sort_key_pref: + base_sort_key = from_json_string( saved_sort_key_pref.value ) + # Build initial query query = self.build_initial_query( session ) query = self.apply_default_filter( trans, query, **kwargs ) + # Maintain sort state in generated urls extra_url_args = {} - # Process filtering arguments to (a) build a query that actuates the filter and (b) builds a + + # Process filtering arguments to (a) build a query that represents the filter and (b) builds a # dictionary that denotes the current filter. cur_filter_dict = {} for column in self.columns: if column.key: - # Look for filter criterion in kwargs; if not found, look in default filter. + # Look for filter criterion in kwargs; if not found, look in base filter. column_filter = None if "f-" + column.key in kwargs: column_filter = kwargs.get( "f-" + column.key ) - elif ( self.default_filter ) and ( column.key in self.default_filter ): - column_filter = self.default_filter.get( column.key ) + elif column.key in base_filter: + column_filter = base_filter.get( column.key ) # If column filter found, apply it. if column_filter is not None: @@ -61,13 +87,13 @@ 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 = sort_order = None if 'sort' in kwargs: sort_key = kwargs['sort'] - elif self.default_sort_key: - sort_key = self.default_sort_key + elif base_sort_key: + sort_key = base_sort_key encoded_sort_key = sort_key if sort_key: if sort_key.startswith( "-" ): @@ -78,9 +104,26 @@ sort_order = 'asc' query = query.order_by( self.model_class.c.get( sort_key ).asc() ) extra_url_args['sort'] = encoded_sort_key + # There might be a current row current_item = self.get_current_item( trans ) - # Render + + # Save current filter and sort key. + if self.preserve_state: + pref_name = self.__class__.__name__ + self.cur_filter_pref_name + if not saved_filter_pref: + saved_filter_pref = UserPreference( name=pref_name ) + trans.get_user().preferences.append( saved_filter_pref ) + saved_filter_pref.value = to_json_string( cur_filter_dict ) + if not saved_sort_key_pref: + pref_name = self.__class__.__name__ + self.cur_sort_key_pref_name + if not saved_sort_key_pref: + saved_sort_key_pref = UserPreference( name=pref_name ) + trans.get_user().preferences.append( saved_sort_key_pref ) + saved_sort_key_pref.value = to_json_string( sort_key ) + trans.sa_session.flush() + + # Render grid. def url( *args, **kwargs ): # Only include sort/filter arguments if not linking to another # page. This is a bit of a hack. diff -r 6a86a558f405 -r 83dc9981e3c4 templates/history/grid.mako --- a/templates/history/grid.mako Fri Sep 25 11:32:47 2009 -0400 +++ b/templates/history/grid.mako Fri Sep 25 17:06:45 2009 -0400 @@ -167,7 +167,7 @@ ## Print grid filter. <form name="history_actions" action="javascript:add_tag_to_grid_filter($('#input-tag-filter').attr('value'))" method="get" > - <strong>Filter: </strong> + <strong>Filter: </strong> %for column in grid.columns: %if column.filterable: <span> by ${column.label.lower()}:</span> @@ -194,14 +194,14 @@ <span class="filter"><a href="${url( filter.get_url_args() )}">${filter.label}</a></span> %endif %endfor - <span> </span> + <span> </span> %endif %endfor - ## Link to clear all filters. + ## Link to clear all filters. TODO: this should be the default filter or an empty filter. <% args = { "deleted" : "False", "tags" : "All" } - no_filter = GridColumnFilter("Clear", args) + no_filter = GridColumnFilter("Clear Filter", args) %> <span><a href="${url( no_filter.get_url_args() )}">${no_filter.label}</a></span> </form>