diff --git a/lib/galaxy/web/controllers/tracks.py b/lib/galaxy/web/controllers/tracks.py index 0e840667a54..8a1558c050f 100644 --- a/lib/galaxy/web/controllers/tracks.py +++ b/lib/galaxy/web/controllers/tracks.py @@ -1,113 +1,96 @@ +""" +Support for constructing and viewing custom "track" browsers within Galaxy. + +Track browsers are currently transient -- nothing is stored to the database +when a browser is created. Building a browser consists of selecting a set +of datasets associated with the same dbkey to display. Once selected, jobs +are started to create any neccesary indexes in the background, and the user +is redirected to the browser interface, which loads the appropriate datasets. + +Problems +-------- + - Assumes that the only indexing type in Galaxy is for this particular + application. Thus, datatypes can only have one indexer, and the presence + of an indexer results in assuming that datatype can be displayed as a track. + +""" + import math -import mimeparse from galaxy.tracks import messages from galaxy.util.json import to_json_string from galaxy.web.base.controller import * from galaxy.web.framework import simplejson - -class MultiResponse(object): +class TracksController( BaseController ): """ - Shamelessly ripped off of a django snippet. + Controller for track browser interface. Handles building a new browser from + datasets in the current history, and display of the resulting browser. """ - def __init__(self, handlers): - self.handlers = handlers - - def __call__(self, view_func): - def wrapper(that, trans, *args, **kwargs): - data_resource = view_func(that, trans, *args, **kwargs) - content_type = mimeparse.best_match(self.handlers.keys(), - trans.request.environ['HTTP_ACCEPT']) - response = self.handlers[content_type](data_resource, trans) - trans.response.headers['Content-Type'] = "%s" % content_type - return response - return wrapper - - @classmethod - def JSON( cls, data_resource, trans ): - return simplejson.dumps( data_resource ) - - class XML( object ): - def __call__(self, data_resource, trans ): - raise NotImplementedError( "XML MultiResponse handler is not implemented." ) - - class AMF( object ): - def __call__(self, data_resource, trans ): - raise NotImplementedError( "XML MultiResponse handler is not implemented." ) - - class HTML( object ): - def __init__(self, template ): - self.template = template - - def __call__(self, data_resource, trans ): - return trans.fill_template( self.template, data_resource=data_resource, trans=trans ) - -class WebRoot( BaseController ): - - @web.expose - @MultiResponse( {'text/html': MultiResponse.HTML( "tracks/dbkeys.mako"), - 'text/javascript':MultiResponse.JSON} ) - def dbkeys(self, trans ): - return list(set([x.metadata.dbkey for x in trans.get_history().datasets if not x.deleted])) @web.expose - @MultiResponse( {'text/html':MultiResponse.HTML( "tracks/chroms.mako" ), - 'text/javascript':MultiResponse.JSON} ) - def chroms(self, trans, dbkey=None): - return self.chroms_handler( trans, dbkey ) - - @web.expose - @MultiResponse( {'text/html':MultiResponse.HTML( "tracks/datasets.mako" ), - 'text/javascript':MultiResponse.JSON} ) - def list(self, trans, dbkey=None ): - trans.session["track_dbkey"] = dbkey - trans.session.save() - datasets = trans.app.model.HistoryDatasetAssociation.filter_by(deleted=False, history_id=trans.history.id).all() - dataset_list = {} - for dataset in datasets: - if dataset.metadata.dbkey == dbkey and trans.app.datatypes_registry.get_indexers_by_datatype( dataset.extension ): - dataset_list[dataset.id] = dataset.name - return dataset_list - - @web.expose - @MultiResponse( {'text/html':MultiResponse.JSON, - 'text/javascript':MultiResponse.JSON} ) - def data(self, trans, dataset_id=None, chr="", low="", high=""): - return self.data_handler( trans, dataset_id, chrom=chr, low=low, high=high ) - - @web.expose - def build( self, trans, **kwargs ): - trans.session["track_sets"] = list(kwargs.keys()) - trans.session.save() - #waiting = False - #for id, value in kwargs.items(): - # status = self.data_handler( trans, id ) - # if status == messages.PENDING: - # waiting = True - #if not waiting: - return trans.response.send_redirect( web.url_for( controller='tracks/', action='index', chrom="" ) ) - #return trans.fill_template( 'tracks/build.mako' ) + def index( self, trans ): + return trans.fill_template( "tracks/index.mako" ) @web.expose - def index(self, trans, **kwargs): + def new_browser( self, trans, dbkey=None, dataset_ids=None, browse=None ): + """ + Build a new browser from datasets in the current history. Redirects + to 'index' once datasets to browse have been selected. + """ + session = trans.sa_session + # If the user clicked the submit button explicately, try to build the browser + if browse and dataset_ids: + dataset_ids = ",".join( map( str, dataset_ids ) ) + trans.response.send_redirect( web.url_for( controller='tracks', action='browser', chrom="", dataset_ids=dataset_ids ) ) + return + # Determine the set of all dbkeys that are used in the current history + dbkeys = [ d.metadata.dbkey for d in trans.get_history().datasets if not d.deleted ] + dbkey_set = set( dbkeys ) + # If a dbkey argument was not provided, or is no longer valid, default + # to the first one + if dbkey is None or dbkey not in dbkey_set: + dbkey = dbkeys[0] + # Find all datasets in the current history that are of that dbkey and + # have an indexer. + datasets = {} + for dataset in session.query( model.HistoryDatasetAssociation ).filter_by( deleted=False, history_id=trans.history.id ): + if dataset.metadata.dbkey == dbkey and trans.app.datatypes_registry.get_indexers_by_datatype( dataset.extension ): + datasets[dataset.id] = dataset.name + # Render the template + return trans.fill_template( "tracks/new_browser.mako", dbkey=dbkey, dbkey_set=dbkey_set, datasets=datasets ) + + @web.expose + def browser(self, trans, dataset_ids, chrom=""): + """ + Display browser for the datasets listed in `dataset_ids`. + """ tracks = [] dbkey = "" - for track in trans.session["track_sets"]: - dataset = trans.app.model.HistoryDatasetAssociation.get( track ) - tracks.append({ - "type": dataset.datatype.get_track_type(), - "name": dataset.name, - "id": dataset.id - }) + for dataset_id in dataset_ids.split( "," ): + dataset = trans.app.model.HistoryDatasetAssociation.get( dataset_id ) + tracks.append( { + "type": dataset.datatype.get_track_type(), + "name": dataset.name, + "id": dataset.id + } ) dbkey = dataset.dbkey - chrom = kwargs.get("chrom","") - LEN = self.chroms_handler(trans, trans.session["track_dbkey"]).get(chrom,0) - return trans.fill_template( 'tracks/index.mako', - tracks=tracks, chrom=chrom, dbkey=dbkey, + LEN = self._chroms(trans, dbkey ).get(chrom,0) + return trans.fill_template( 'tracks/browser.mako', + dataset_ids=dataset_ids, + tracks=tracks, + chrom=chrom, + dbkey=dbkey, LEN=LEN ) - - def chroms_handler(self, trans, dbkey ): + + @web.json + def chroms(self, trans, dbkey=None ): + return self._chroms( trans, dbkey ) + + def _chroms( self, trans, dbkey ): + """ + Called by the browser to get a list of valid chromosomes and lengths + """ db_manifest = trans.db_dataset_for( dbkey ) if not db_manifest: db_manifest = os.path.join( trans.app.config.tool_data_path, 'shared','ucsc','chrom', "%s.len" % dbkey ) @@ -134,7 +117,11 @@ class WebRoot( BaseController ): pass return manifest - def data_handler( self, trans, dataset_id, chrom="", low="", high="" ): + @web.json + def data( self, trans, dataset_id, chrom="", low="", high="" ): + """ + Called by the browser to request a block of data + """ dataset = trans.app.model.HistoryDatasetAssociation.get( dataset_id ) if not dataset: return messages.NO_DATA if dataset.state == trans.app.model.Job.states.ERROR: diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index e349a19fa42..3680074d873 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -155,7 +155,7 @@ $.extend( DataCache.prototype, { // use closure to preserve this and parameters for getJSON var fetcher = function (ref) { return function () { - $.getJSON( "data" + ref.type, { chr: ref.view.chr, low: low, high: high, dataset_id: ref.track.dataset_id }, function ( data ) { + $.getJSON( TRACKSTER_DATA_URL + ref.type, { chrom: ref.view.chr, low: low, high: high, dataset_id: ref.track.dataset_id }, function ( data ) { if( data == "pending" ) { setTimeout( fetcher, 5000 ); } else { @@ -218,6 +218,7 @@ $.extend( LineTrack.prototype, TiledTrack.prototype, { var y1 = data[i][1]; var x2 = data[i+1][0] - tile_low; var y2 = data[i+1][1]; + console.log( x1, y1, x2, y2 ); // Missing data causes us to stop drawing if ( isNaN( y1 ) || isNaN( y2 ) ) { in_path = false; diff --git a/static/trackster.css b/static/trackster.css index b160fea3b71..7c25f724636 100644 --- a/static/trackster.css +++ b/static/trackster.css @@ -1,5 +1,5 @@ body { - margin: 4em 0; + margin: 0 0; padding: 0; font-family: verdana; font-size: 75%; diff --git a/templates/base_panels.mako b/templates/base_panels.mako index 63573788e88..4c32dfeb7ab 100644 --- a/templates/base_panels.mako +++ b/templates/base_panels.mako @@ -148,13 +148,22 @@ ${display} - ## ${tab( "tracks", "View Data", h.url_for( controller='tracks', action='dbkeys' ), target="galaxy_main")} - ${tab( "analysis", "Analyze Data", h.url_for( controller='root', action='index' ))} ${tab( "workflow", "Workflow", h.url_for( controller='workflow', action='index' ))} - ${tab( "libraries", "Libraries", h.url_for( controller='library', action='index' ))} + ${tab( "libraries", "Libraries", h.url_for( controller='library', action='index' ))} + + %if app.config.get_bool( 'enable_tracks', False ): + + Visualization + + + %endif ${tab( "admin", "Admin", h.url_for( controller='admin', action='index' ), extra_class="admin-only", visible=( trans.user and app.config.is_admin_user( trans.user ) ) )} diff --git a/templates/tracks/view.mako b/templates/tracks/browser.mako similarity index 51% rename from templates/tracks/view.mako rename to templates/tracks/browser.mako index 6cf448f3015..b551a4ea09d 100644 --- a/templates/tracks/view.mako +++ b/templates/tracks/browser.mako @@ -1,23 +1,30 @@ - +<%inherit file="/base.mako"/> - +<%def name="stylesheets()"> +${parent.stylesheets()} + + - - - +<%def name="javascripts()"> +${parent.javascripts()} - - + +
@@ -59,14 +101,23 @@
+ +
+
+ + -
-
- - - - - - diff --git a/templates/tracks/build.mako b/templates/tracks/build.mako deleted file mode 100644 index 63a7f9a678d..00000000000 --- a/templates/tracks/build.mako +++ /dev/null @@ -1,22 +0,0 @@ -<%inherit file="/base.mako"/> - -<%def name="init()"> -<% - self.active_view="view" - self.has_left_panel=False -%> - - - - -
-

-Please wait while we index your tracks for viewing. You will be -automatically redirected to choose a chromosome to view after indices -are built. -

-
diff --git a/templates/tracks/chroms.mako b/templates/tracks/chroms.mako deleted file mode 100644 index ef69623782c..00000000000 --- a/templates/tracks/chroms.mako +++ /dev/null @@ -1,30 +0,0 @@ -<%inherit file="/base.mako"/> - -<%def name="init()"> -<% - self.active_view="view" - self.has_left_panel=False -%> - - -
-
Select Chromosome/Contig/Scaffold/etc.
-
-
-
- -
- -
-
-
-
- -
-
-
-
diff --git a/templates/tracks/datasets.mako b/templates/tracks/datasets.mako deleted file mode 100644 index 73ef39a4f8d..00000000000 --- a/templates/tracks/datasets.mako +++ /dev/null @@ -1,28 +0,0 @@ -<%inherit file="/base.mako"/> - -<%def name="init()"> -<% - self.active_view="view" - self.has_left_panel=False -%> - - -
-
Select Datasets to View
-
-
- %for key,value in data_resource.items(): -
- -
- -
-
-
- %endfor -
- -
-
-
-
diff --git a/templates/tracks/dbkeys.mako b/templates/tracks/dbkeys.mako deleted file mode 100644 index f7e1c21dd1e..00000000000 --- a/templates/tracks/dbkeys.mako +++ /dev/null @@ -1,30 +0,0 @@ -<%inherit file="/base.mako"/> - -<%def name="init()"> -<% - self.active_view="view" - self.has_left_panel=False -%> - - -
-
Select DBKey
-
-
-
- -
- -
-
-
-
- -
-
-
-
diff --git a/templates/tracks/debug.mako b/templates/tracks/debug.mako deleted file mode 100644 index 4627aff0af2..00000000000 --- a/templates/tracks/debug.mako +++ /dev/null @@ -1 +0,0 @@ -${data_resource} \ No newline at end of file diff --git a/templates/tracks/index.mako b/templates/tracks/index.mako index 3df253c5d52..cb00986c5c5 100644 --- a/templates/tracks/index.mako +++ b/templates/tracks/index.mako @@ -2,149 +2,15 @@ <%def name="init()"> <% - self.active_view="tracks" self.has_left_panel=False self.has_right_panel=False + self.active_view="visualization" + self.message_box_visible=False %> -<%def name="stylesheets()"> -${parent.stylesheets()} - - - -<%def name="late_javascripts()"> -${parent.late_javascripts()} - - - - - <%def name="center_panel()"> -
-
-
-
-
-
+ - -
-
- -
- - - -<%def name="right_panel()"> -
-
-
- Options -
-
History
-
-
-
- -
- + \ No newline at end of file diff --git a/templates/tracks/new_browser.mako b/templates/tracks/new_browser.mako new file mode 100644 index 00000000000..166b73628cd --- /dev/null +++ b/templates/tracks/new_browser.mako @@ -0,0 +1,50 @@ +<%inherit file="/base.mako"/> + +<%def name="javascripts()"> +${parent.javascripts()} + + + +
+
Select datasets to include in browser
+
+
+
+ +
+ +
+
+
+
+ + %for key,value in datasets.items(): +
+ + ${value} +
+ %endfor + +
+
+
+
+ +
+ +
+