details: http://www.bx.psu.edu/hg/galaxy/rev/2cf1ad5edaa6 changeset: 3732:2cf1ad5edaa6 user: jeremy goecks <jeremy.goecks@emory.edu> date: Mon May 03 14:02:08 2010 -0400 description: Enable viewing of shared/accessible/published visualizations. diffstat: lib/galaxy/web/base/controller.py | 38 ++++ lib/galaxy/web/controllers/visualization.py | 8 +- static/scripts/trackster.js | 8 +- templates/visualization/display.mako | 219 +++++++++++++++++++++++++++- 4 files changed, 263 insertions(+), 10 deletions(-) diffs (349 lines): diff -r 5f93447d642d -r 2cf1ad5edaa6 lib/galaxy/web/base/controller.py --- a/lib/galaxy/web/base/controller.py Mon May 03 09:42:51 2010 -0400 +++ b/lib/galaxy/web/base/controller.py Mon May 03 14:02:08 2010 -0400 @@ -7,6 +7,7 @@ from galaxy.web import error, form, url_for from galaxy.model.orm import * from galaxy.workflow.modules import * +from galaxy.web.framework import simplejson from Cheetah.Template import Template @@ -164,6 +165,7 @@ class UsesVisualization( SharableItemSecurity ): """ Mixin for controllers that use Visualization objects. """ + def get_visualization( self, trans, id, check_ownership=True, check_accessible=False ): """ Get a Visualization from the database by id, verifying ownership. """ # Load workflow from database @@ -173,6 +175,42 @@ error( "Visualization not found" ) else: return self.security_check( trans.get_user(), visualization, check_ownership, check_accessible ) + + def get_visualization_config( self, trans, visualization ): + """ Returns a visualization's configuration. Only works for trackster visualizations right now. """ + + config = None + if visualization.type == 'trackster': + # Trackster config; taken from tracks/browser + latest_revision = visualization.latest_revision + tracks = [] + + try: + dbkey = latest_revision.config['dbkey'] + except KeyError: + dbkey = None + + hda_query = trans.sa_session.query( trans.model.HistoryDatasetAssociation ) + for t in visualization.latest_revision.config['tracks']: + dataset_id = t['dataset_id'] + try: + prefs = t['prefs'] + except KeyError: + prefs = {} + dataset = hda_query.get( dataset_id ) + track_type, _ = dataset.datatype.get_track_type() + tracks.append( { + "track_type": track_type, + "name": dataset.name, + "dataset_id": dataset.id, + "prefs": simplejson.dumps(prefs), + } ) + if dbkey is None: dbkey = dataset.dbkey # Hack for backward compat + + ## TODO: chrom needs to be able to be set; right now it's empty. + config = { "title": visualization.title, "vis_id": id, "tracks": tracks, "chrom": "", "dbkey": dbkey } + + return config class UsesStoredWorkflow( SharableItemSecurity ): """ Mixin for controllers that use StoredWorkflow objects. """ diff -r 5f93447d642d -r 2cf1ad5edaa6 lib/galaxy/web/controllers/visualization.py --- a/lib/galaxy/web/controllers/visualization.py Mon May 03 09:42:51 2010 -0400 +++ b/lib/galaxy/web/controllers/visualization.py Mon May 03 14:02:08 2010 -0400 @@ -256,9 +256,13 @@ visualization = trans.sa_session.query( model.Visualization ).filter_by( user=user, slug=slug, deleted=False ).first() if visualization is None: raise web.httpexceptions.HTTPNotFound() + # Security check raises error if user cannot access visualization. - self.security_check( trans.get_user(), visualization, False, True) - return trans.fill_template_mako( "visualization/display.mako", item=visualization, item_data=None, content_only=True ) + self.security_check( trans.get_user(), visualization, False, True) + + # Display. + visualization_config = self.get_visualization_config( trans, visualization ) + return trans.fill_template_mako( "visualization/display.mako", item=visualization, item_data=visualization_config, content_only=True ) @web.expose @web.json diff -r 5f93447d642d -r 2cf1ad5edaa6 static/scripts/trackster.js --- a/static/scripts/trackster.js Mon May 03 09:42:51 2010 -0400 +++ b/static/scripts/trackster.js Mon May 03 14:02:08 2010 -0400 @@ -17,22 +17,22 @@ RIGHT_STRAND, LEFT_STRAND; var right_img = new Image(); -right_img.src = "../images/visualization/strand_right.png"; +right_img.src = "/static/images/visualization/strand_right.png"; right_img.onload = function() { RIGHT_STRAND = CONTEXT.createPattern(right_img, "repeat"); }; var left_img = new Image(); -left_img.src = "../images/visualization/strand_left.png"; +left_img.src = "/static/images/visualization/strand_left.png"; left_img.onload = function() { LEFT_STRAND = CONTEXT.createPattern(left_img, "repeat"); }; var right_img_inv = new Image(); -right_img_inv.src = "../images/visualization/strand_right_inv.png"; +right_img_inv.src = "/static/images/visualization/strand_right_inv.png"; right_img_inv.onload = function() { RIGHT_STRAND_INV = CONTEXT.createPattern(right_img_inv, "repeat"); }; var left_img_inv = new Image(); -left_img_inv.src = "../images/visualization/strand_left_inv.png"; +left_img_inv.src = "/static/images/visualization/strand_left_inv.png"; left_img_inv.onload = function() { LEFT_STRAND_INV = CONTEXT.createPattern(left_img_inv, "repeat"); }; diff -r 5f93447d642d -r 2cf1ad5edaa6 templates/visualization/display.mako --- a/templates/visualization/display.mako Mon May 03 09:42:51 2010 -0400 +++ b/templates/visualization/display.mako Mon May 03 14:02:08 2010 -0400 @@ -2,18 +2,229 @@ <%def name="javascripts()"> ${parent.javascripts()} - ## Need visualization JS. + ${h.js( "jquery.event.drag", "jquery.autocomplete", "jquery.mousewheel", "trackster", "ui.core", "ui.sortable" )} + + ## HACK: set config as item_data. + <% config = item_data %> + + ## TODO: Copied from browser.mako -- probably should create JS file for visualization code and include visualization JS above. + <script type="text/javascript"> + + ## JG: add controller name. + var data_url = "${h.url_for( controller='/tracks', action='data' )}"; + var view; + + $(function() { + + %if config: + view = new View( "${config.get('chrom')}", "${config.get('title') | h}", "${config.get('vis_id')}", "${config.get('dbkey')}" ); + %for track in config.get('tracks'): + view.add_track( + new ${track["track_type"]}( "${track['name'] | h}", ${track['dataset_id']}, ${track['prefs']} ) + ); + %endfor + init(); + %else: + continue_fn = function() { + view = new View( undefined, $("#new-title").val(), undefined, $("#new-dbkey").val() ); + init(); + hide_modal(); + }; + $.ajax({ + url: "${h.url_for( action='new_browser' )}", + data: {}, + error: function() { alert( "Couldn't create new browser" ) }, + success: function(form_html) { + show_modal("New Track Browser", form_html, { + "Cancel": function() { window.location = "/"; }, + "Continue": function() { $(document).trigger("convert_dbkeys"); continue_fn(); } + }); + $("#new-title").focus(); + replace_big_select_inputs(); + } + }); + %endif + + // Execute this when everything is ready + function init() { + $("ul#sortable-ul").sortable({ + update: function(event, ui) { + for (var track_id in view.tracks) { + var track = view.tracks[track_id]; + } + } + }); + + $(document).bind( "redraw", function( e ) { + view.redraw(); + }); + + $("#content").bind("mousewheel", function( e, delta ) { + if (delta > 0) { + view.zoom_in(e.pageX, $("#viewport-container")); + } else { + view.zoom_out(); + } + e.preventDefault(); + }); + + $("#content").bind("dblclick", function( e ) { + view.zoom_in(e.pageX, $("#viewport-container")); + }); + + // To let the overview box be draggable + $("#overview-box").bind("dragstart", function( e ) { + this.current_x = e.offsetX; + }).bind("drag", function( e ) { + var delta = e.offsetX - this.current_x; + this.current_x = e.offsetX; + + var delta_chrom = Math.round(delta / $(document).width() * view.span); + view.center += delta_chrom; + view.redraw(); + }); + + // To adjust the size of the viewport to fit the fixed-height footer + var refresh = function( e ) { + $("#viewport-container").height( $(window).height() - 120 ); + $("#nav-container").width( $("#center").width() ); + view.redraw(); + }; + $(window).bind( "resize", function(e) { refresh(e); } ); + $("#right-border").bind( "click", function(e) { refresh(e); } ); + $("#right-border").bind( "dragend", function(e) { refresh(e); } ); + $(window).trigger( "resize" ); + + $("#viewport-container").bind( "dragstart", function( e ) { + this.original_low = view.low; + this.current_height = e.clientY; + this.current_x = e.offsetX; + }).bind( "drag", function( e ) { + var container = $(this); + var delta = e.offsetX - this.current_x; + var new_scroll = container.scrollTop() - (e.clientY - this.current_height); + if ( new_scroll < container.get(0).scrollHeight - container.height() ) { + container.scrollTop(new_scroll); + } + this.current_height = e.clientY; + this.current_x = e.offsetX; + + var delta_chrom = Math.round(delta / $(document).width() * (view.high - view.low)); + view.center -= delta_chrom; + view.redraw(); + }); + + ## JG: Removed 'add-track' init code. + + ## JG: Removed 'save-button' init code + + view.add_label_track( new LabelTrack( $("#top-labeltrack" ) ) ); + view.add_label_track( new LabelTrack( $("#nav-labeltrack" ) ) ); + + $.ajax({ + ## JG: added controller name + url: "${h.url_for( controller='/tracks', action='chroms' )}", + data: { dbkey: view.dbkey }, + dataType: "json", + success: function ( data ) { + view.chrom_data = data; + var chrom_options = '<option value="">Select Chrom/Contig</option>'; + for (i in data) { + var chrom = data[i]['chrom'] + chrom_options += '<option value="' + chrom + '">' + chrom + '</option>'; + } + $("#chrom").html(chrom_options); + $("#chrom").bind( "change", function () { + view.chrom = $("#chrom").val(); + var found = $.grep(view.chrom_data, function(v, i) { + return v.chrom === view.chrom; + })[0]; + view.max_high = found.len; + view.reset(); + view.redraw(true); + + for (var track_id in view.tracks) { + var track = view.tracks[track_id]; + if (track.init) { + track.init(); + } + } + view.redraw(); + }); + }, + error: function() { + alert( "Could not load chroms for this dbkey:", view.dbkey ); + } + }); + + ## JG: Removed function sidebar_box() and sidebar init code. + + $(window).trigger("resize"); + }; + + }); + + </script> + </%def> <%def name="stylesheets()"> ${parent.stylesheets()} - ## Need visualization CSS. + + ${h.css( "history" )} + <link rel="stylesheet" type="text/css" href="${h.url_for('/static/trackster.css')}" /> + + <style type="text/css"> + ul#sortable-ul { + list-style: none; + padding: 0; + margin: 5px; + } + ul#sortable-ul li { + display: block; + margin: 5px 0; + background: #eee; + } + </style> </%def> <%def name="render_item_links( visualization )"> ## TODO </%def> -<%def name="render_item( visualization, visualization_data )"> - ## TODO +<%def name="render_item( visualization, config )"> + <br><br> + ## Copied from center_panel() in browser.mako -- probably need to create visualization_common.mako to render view. + <div id="content"> + <div id="top-labeltrack"></div> + <div id="viewport-container" style="overflow-x: hidden; overflow-y: auto;"> + <div id="viewport"></div> + </div> + </div> + <div id="nav-container" style="width:100%;"> + <div id="nav-labeltrack"></div> + <div id="nav"> + <div id="overview"> + <div id="overview-viewport"> + <div id="overview-box"></div> + </div> + </div> + <div id="nav-controls"> + <form action="#"> + <select id="chrom" name="chrom" style="width: 15em;"> + <option value="">Loading</option> + </select> + <input id="low" size="12" />:<input id="high" size="12" /> + <input type="hidden" name="id" value="${config.get('vis_id', '')}" /> + <a href="#" onclick="javascript:view.zoom_in();view.redraw();"> + <img src="${h.url_for('/static/images/fugue/magnifier-zoom.png')}" /> + </a> + <a href="#" onclick="javascript:view.zoom_out();view.redraw();"> + <img src="${h.url_for('/static/images/fugue/magnifier-zoom-out.png')}" /> + </a> + </form> + <div id="debug" style="float: right"></div> + </div> + </div> + </div> </%def> \ No newline at end of file