1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/changeset/fdc3f20a46d3/ changeset: fdc3f20a46d3 user: jgoecks date: 2012-05-27 18:34:52 summary: Provide generic support for saving visualizations. affected #: 3 files diff -r f4c4ba7be3d17fbf7924e9f8d6ae005960fcc4c0 -r fdc3f20a46d3af6ac89bd02baef3db09eed3f235 lib/galaxy/web/base/controller.py --- a/lib/galaxy/web/base/controller.py +++ b/lib/galaxy/web/base/controller.py @@ -280,41 +280,91 @@ viz_types = [ "trackster", "circos" ] len_files = None - + def create_visualization( self, trans, title, slug, type, dbkey, annotation=None, config={} ): - user = trans.get_user() + """ Create visualiation and first revision. """ + visualization = self._create_visualization( trans, title, type, dbkey, slug, annotation ) - # Error checking. - title_err = slug_err = "" - if not title: - title_err = "visualization name is required" - elif not slug: - slug_err = "visualization id is required" - elif not VALID_SLUG_RE.match( slug ): - slug_err = "visualization identifier must consist of only lowercase letters, numbers, and the '-' character" - elif trans.sa_session.query( trans.model.Visualization ).filter_by( user=user, slug=slug, deleted=False ).first(): - slug_err = "visualization id must be unique" - - if title_err or slug_err: - return { 'title_err': title_err, 'slug_err': slug_err } - - # Create visualization - visualization = trans.model.Visualization( user=user, title=title, slug=slug, dbkey=dbkey, type=type ) - if annotation: - annotation = sanitize_html( annotation, 'utf-8', 'text/html' ) - self.add_item_annotation( trans.sa_session, trans.user, visualization, annotation ) - - # And the first visualization revision - revision = trans.model.VisualizationRevision( visualization=visualization, title=title, config={}, dbkey=dbkey ) + # Create and save first visualization revision + revision = trans.model.VisualizationRevision( visualization=visualization, title=title, config=config, dbkey=dbkey ) visualization.latest_revision = revision - - # Persist session = trans.sa_session - session.add(visualization) - session.add(revision) + session.add( revision ) session.flush() return visualization + + def save_visualization( self, trans, config, type, id=None, title=None, dbkey=None, slug=None, annotation=None ): + session = trans.sa_session + + # Create/get visualization. + if not id: + # Create new visualization. + vis = self._create_visualization( trans, title, type, dbkey, slug, annotation ) + else: + decoded_id = trans.security.decode_id( id ) + vis = session.query( trans.model.Visualization ).get( decoded_id ) + + # Decode the payload + decoded_payload = config + # Create new VisualizationRevision that will be attached to the viz + vis_rev = trans.model.VisualizationRevision() + vis_rev.visualization = vis + vis_rev.title = vis.title + vis_rev.dbkey = dbkey + + def unpack_track( track_json ): + """ Unpack a track from its json. """ + return { + "dataset_id": trans.security.decode_id( track_json['dataset_id'] ), + "hda_ldda": track_json.get('hda_ldda', 'hda'), + "name": track_json['name'], + "track_type": track_json['track_type'], + "prefs": track_json['prefs'], + "mode": track_json['mode'], + "filters": track_json['filters'], + "tool_state": track_json['tool_state'] + } + + def unpack_collection( collection_json ): + """ Unpack a collection from its json. """ + unpacked_drawables = [] + drawables = collection_json[ 'drawables' ] + for drawable_json in drawables: + if 'track_type' in drawable_json: + drawable = unpack_track( drawable_json ) + else: + drawable = unpack_collection( drawable_json ) + unpacked_drawables.append( drawable ) + return { + "name": collection_json.get( 'name', '' ), + "obj_type": collection_json[ 'obj_type' ], + "drawables": unpacked_drawables, + "prefs": collection_json.get( 'prefs' , [] ), + "filters": collection_json.get( 'filters', None ) + } + + # TODO: unpack and validate bookmarks: + def unpack_bookmarks( bookmarks_json ): + return bookmarks_json + + # Unpack and validate view content. + view_content = unpack_collection( decoded_payload[ 'view' ] ) + bookmarks = unpack_bookmarks( decoded_payload[ 'bookmarks' ] ) + vis_rev.config = { "view": view_content, "bookmarks": bookmarks } + # Viewport from payload + if 'viewport' in decoded_payload: + chrom = decoded_payload['viewport']['chrom'] + start = decoded_payload['viewport']['start'] + end = decoded_payload['viewport']['end'] + overview = decoded_payload['viewport']['overview'] + vis_rev.config[ "viewport" ] = { 'chrom': chrom, 'start': start, 'end': end, 'overview': overview } + + vis.latest_revision = vis_rev + session.add( vis_rev ) + session.flush() + encoded_id = trans.security.encode_id( vis.id ) + return { "vis_id": encoded_id, "url": url_for( action='browser', id=encoded_id ) } def _get_dbkeys( self, trans ): """ Returns all valid dbkeys that a user can use in a visualization. """ @@ -432,6 +482,41 @@ config['viewport'] = latest_revision.config['viewport'] return config + + # -- Helper functions -- + + def _create_visualization( self, trans, title, type, dbkey, slug=None, annotation=None ): + """ Create visualization but not first revision. Returns Visualization object. """ + user = trans.get_user() + + # Error checking. + title_err = slug_err = "" + if not title: + title_err = "visualization name is required" + elif slug and not VALID_SLUG_RE.match( slug ): + slug_err = "visualization identifier must consist of only lowercase letters, numbers, and the '-' character" + elif slug and trans.sa_session.query( trans.model.Visualization ).filter_by( user=user, slug=slug, deleted=False ).first(): + slug_err = "visualization identifier must be unique" + + if title_err or slug_err: + return { 'title_err': title_err, 'slug_err': slug_err } + + + # Create visualization + visualization = trans.model.Visualization( user=user, title=title, dbkey=dbkey, type=type ) + if slug: + visualization.slug = slug + else: + self.create_item_slug( trans.sa_session, visualization ) + if annotation: + annotation = sanitize_html( annotation, 'utf-8', 'text/html' ) + self.add_item_annotation( trans.sa_session, trans.user, visualization, annotation ) + + session = trans.sa_session + session.add( visualization ) + session.flush() + + return visualization class UsesStoredWorkflow( SharableItemSecurity ): """ Mixin for controllers that use StoredWorkflow objects. """ @@ -1255,7 +1340,9 @@ class Sharable: """ Mixin for a controller that manages an item that can be shared. """ - # Implemented methods. + + # -- Implemented methods. -- + @web.expose @web.require_login( "share Galaxy items" ) def set_public_username( self, trans, id, username, **kwargs ): @@ -1268,42 +1355,50 @@ trans.sa_session.flush return self.sharing( trans, id, **kwargs ) - # Abstract methods. + # -- Abstract methods. -- + @web.expose @web.require_login( "modify Galaxy items" ) def set_slug_async( self, trans, id, new_slug ): """ Set item slug asynchronously. """ raise "Unimplemented Method" + @web.expose @web.require_login( "share Galaxy items" ) def sharing( self, trans, id, **kwargs ): """ Handle item sharing. """ raise "Unimplemented Method" + @web.expose @web.require_login( "share Galaxy items" ) def share( self, trans, id=None, email="", **kwd ): """ Handle sharing an item with a particular user. """ raise "Unimplemented Method" + @web.expose def display_by_username_and_slug( self, trans, username, slug ): """ Display item by username and slug. """ raise "Unimplemented Method" - @web.expose + @web.json @web.require_login( "get item name and link" ) def get_name_and_link_async( self, trans, id=None ): """ Returns item's name and link. """ raise "Unimplemented Method" + @web.expose @web.require_login("get item content asynchronously") def get_item_content_async( self, trans, id ): """ Returns item content in HTML format. """ raise "Unimplemented Method" - # Helper methods. + + # -- Helper methods. -- + def _make_item_accessible( self, sa_session, item ): """ Makes item accessible--viewable and importable--and sets item's slug. Does not flush/commit changes, however. Item must have name, user, importable, and slug attributes. """ item.importable = True self.create_item_slug( sa_session, item ) + def create_item_slug( self, sa_session, item ): """ Create item slug. Slug is unique among user's importable items for item's class. Returns true if item's slug was set; false otherwise. """ if item.slug is None or item.slug == "": @@ -1323,12 +1418,13 @@ slug = slug_base count = 1 while sa_session.query( item.__class__ ).filter_by( user=item.user, slug=slug, importable=True ).count() != 0: - # Slug taken; choose a new slug based on count. This approach can handle numerous histories with the same name gracefully. + # Slug taken; choose a new slug based on count. This approach can handle numerous items with the same name gracefully. slug = '%s-%i' % ( slug_base, count ) count += 1 item.slug = slug return True return False + def get_item( self, trans, id ): """ Return item based on id. """ raise "Unimplemented Method" diff -r f4c4ba7be3d17fbf7924e9f8d6ae005960fcc4c0 -r fdc3f20a46d3af6ac89bd02baef3db09eed3f235 lib/galaxy/web/controllers/tracks.py --- a/lib/galaxy/web/controllers/tracks.py +++ b/lib/galaxy/web/controllers/tracks.py @@ -163,7 +163,7 @@ def apply_query_filter( self, trans, query, **kwargs ): return query.filter( self.model_class.user_id == trans.user.id ) -class TracksController( BaseUIController, UsesVisualization, UsesHistoryDatasetAssociation ): +class TracksController( BaseUIController, UsesVisualization, UsesHistoryDatasetAssociation, Sharable ): """ Controller for track browser interface. Handles building a new browser from datasets in the current history, and display of the resulting browser. @@ -411,83 +411,8 @@ return result @web.json - def save( self, trans, **kwargs ): - session = trans.sa_session - vis_id = "undefined" - if 'vis_id' in kwargs: - vis_id = kwargs['vis_id'].strip('"') - dbkey = kwargs['dbkey'] - # Lookup or create Visualization object - if vis_id == "undefined": # new vis - vis = model.Visualization() - vis.user = trans.user - vis.title = kwargs['title'] - vis.type = "trackster" - vis.dbkey = dbkey - session.add( vis ) - else: - decoded_id = trans.security.decode_id( vis_id ) - vis = session.query( model.Visualization ).get( decoded_id ) - # Decode the payload - decoded_payload = simplejson.loads( kwargs['payload'] ) - # Create new VisualizationRevision that will be attached to the viz - vis_rev = model.VisualizationRevision() - vis_rev.visualization = vis - vis_rev.title = vis.title - vis_rev.dbkey = dbkey - - def unpack_track( track_json ): - """ Unpack a track from its json. """ - return { - "dataset_id": trans.security.decode_id( track_json['dataset_id'] ), - "hda_ldda": track_json.get('hda_ldda', "hda"), - "name": track_json['name'], - "track_type": track_json['track_type'], - "prefs": track_json['prefs'], - "mode": track_json['mode'], - "filters": track_json['filters'], - "tool_state": track_json['tool_state'] - } - - def unpack_collection( collection_json ): - """ Unpack a collection from its json. """ - unpacked_drawables = [] - drawables = collection_json[ 'drawables' ] - for drawable_json in drawables: - if 'track_type' in drawable_json: - drawable = unpack_track( drawable_json ) - else: - drawable = unpack_collection( drawable_json ) - unpacked_drawables.append( drawable ) - return { - "name": collection_json.get( 'name', '' ), - "obj_type": collection_json[ 'obj_type' ], - "drawables": unpacked_drawables, - "prefs": collection_json.get( 'prefs' , [] ), - "filters": collection_json.get( 'filters', None ) - } - - # TODO: unpack and validate bookmarks: - def unpack_bookmarks( bookmarks_json ): - return bookmarks_json - - # Unpack and validate view content. - view_content = unpack_collection( decoded_payload[ 'view' ] ) - bookmarks = unpack_bookmarks( decoded_payload[ 'bookmarks' ] ) - vis_rev.config = { "view": view_content, "bookmarks": bookmarks } - # Viewport from payload - if 'viewport' in decoded_payload: - chrom = decoded_payload['viewport']['chrom'] - start = decoded_payload['viewport']['start'] - end = decoded_payload['viewport']['end'] - overview = decoded_payload['viewport']['overview'] - vis_rev.config[ "viewport" ] = { 'chrom': chrom, 'start': start, 'end': end, 'overview': overview } - - vis.latest_revision = vis_rev - session.add( vis_rev ) - session.flush() - encoded_id = trans.security.encode_id(vis.id) - return { "vis_id": encoded_id, "url": url_for( action='browser', id=encoded_id ) } + def save( self, trans, config, type, id=None, title=None, dbkey=None, annotation=None ): + return self.save_visualization( trans, from_json_string( config ), type, id, title, dbkey, annotation ) @web.expose @web.require_login( "see all available libraries" ) diff -r f4c4ba7be3d17fbf7924e9f8d6ae005960fcc4c0 -r fdc3f20a46d3af6ac89bd02baef3db09eed3f235 templates/tracks/browser.mako --- a/templates/tracks/browser.mako +++ b/templates/tracks/browser.mako @@ -209,10 +209,11 @@ url: "${h.url_for( action='save' )}", type: "POST", data: { - 'vis_id': view.vis_id, + 'id': view.vis_id, 'title': view.name, 'dbkey': view.dbkey, - 'payload': JSON.stringify(payload) + 'type': 'trackster', + 'config': JSON.stringify(payload) }, dataType: "json", success: function(vis_info) { Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.