mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-21 13:50:20 +08:00
Visualizations framework: allow users to associate custom visualizations with models, datatypes, etc. via an xml configuration file (visualizations_conf.xml)
This commit is contained in:
@@ -60,7 +60,7 @@ shed_tool_data_table_conf.xml
|
||||
job_conf.xml
|
||||
data_manager_conf.xml
|
||||
shed_data_manager_conf.xml
|
||||
|
||||
visualizations_conf.xml
|
||||
static/welcome.html.*
|
||||
static/welcome.html
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import galaxy.quota
|
||||
from galaxy.tags.tag_handler import GalaxyTagHandler
|
||||
from galaxy.visualization.genomes import Genomes
|
||||
from galaxy.visualization.data_providers.registry import DataProviderRegistry
|
||||
from galaxy.visualization.registry import VisualizationsRegistry
|
||||
from galaxy.tools.imp_exp import load_history_imp_exp_tools
|
||||
from galaxy.tools.genome_index import load_genome_index_tools
|
||||
from galaxy.sample_tracking import external_service_types
|
||||
@@ -120,6 +121,9 @@ class UniverseApplication( object ):
|
||||
load_history_imp_exp_tools( self.toolbox )
|
||||
# Load genome indexer tool.
|
||||
load_genome_index_tools( self.toolbox )
|
||||
# visualizations registry: associates resources with visualizations, controls how to render
|
||||
self.visualizations_registry = ( VisualizationsRegistry( self.config.root, self.config.visualizations_conf_path )
|
||||
if self.config.visualizations_conf_path else None )
|
||||
# Load security policy.
|
||||
self.security_agent = self.model.security_agent
|
||||
self.host_security_agent = galaxy.security.HostAgent( model=self.security_agent.model, permitted_actions=self.security_agent.permitted_actions )
|
||||
|
||||
@@ -275,6 +275,8 @@ class Configuration( object ):
|
||||
self.fluent_log = string_as_bool( kwargs.get( 'fluent_log', False ) )
|
||||
self.fluent_host = kwargs.get( 'fluent_host', 'localhost' )
|
||||
self.fluent_port = int( kwargs.get( 'fluent_port', 24224 ) )
|
||||
# visualizations registry config path
|
||||
self.visualizations_conf_path = kwargs.get( 'visualizations_conf_path', None )
|
||||
|
||||
@property
|
||||
def sentry_dsn_public( self ):
|
||||
|
||||
@@ -379,6 +379,36 @@ class Registry( object ):
|
||||
if not included:
|
||||
self.sniff_order.append(datatype)
|
||||
append_to_sniff_order()
|
||||
|
||||
def get_datatype_class_by_name( self, name ):
|
||||
"""
|
||||
Return the datatype class where the datatype's `type` attribute
|
||||
(as defined in the datatype_conf.xml file) contains `name`.
|
||||
"""
|
||||
#TODO: too roundabout - would be better to generate this once as a map and store in this object
|
||||
found_class = None
|
||||
for ext, datatype_obj in self.datatypes_by_extension.items():
|
||||
datatype_obj_class = datatype_obj.__class__
|
||||
datatype_obj_class_str = str( datatype_obj_class )
|
||||
#print datatype_obj_class_str
|
||||
if name in datatype_obj_class_str:
|
||||
return datatype_obj_class
|
||||
return None
|
||||
# these seem to be connected to the dynamic classes being generated in this file, lines 157-158
|
||||
# they appear when a one of the three are used in inheritance with subclass="True"
|
||||
#TODO: a possible solution is to def a fn in datatypes __init__ for creating the dynamic classes
|
||||
|
||||
#remap = {
|
||||
# 'galaxy.datatypes.registry.Tabular' : galaxy.datatypes.tabular.Tabular,
|
||||
# 'galaxy.datatypes.registry.Text' : galaxy.datatypes.data.Text,
|
||||
# 'galaxy.datatypes.registry.Binary' : galaxy.datatypes.binary.Binary
|
||||
#}
|
||||
#datatype_str = str( datatype )
|
||||
#if datatype_str in remap:
|
||||
# datatype = remap[ datatype_str ]
|
||||
#
|
||||
#return datatype
|
||||
|
||||
def get_available_tracks(self):
|
||||
return self.available_tracks
|
||||
def get_mimetype_by_extension(self, ext, default = 'application/octet-stream' ):
|
||||
|
||||
@@ -30,7 +30,9 @@ class DataProviderRegistry( object ):
|
||||
"bai": genome.BamDataProvider,
|
||||
"bam": genome.SamDataProvider,
|
||||
"bigwig": genome.BigWigDataProvider,
|
||||
"bigbed": genome.BigBedDataProvider
|
||||
"bigbed": genome.BigBedDataProvider,
|
||||
|
||||
"column": ColumnDataProvider
|
||||
}
|
||||
|
||||
def get_data_provider( self, trans, name=None, source='data', raw=False, original_dataset=None ):
|
||||
@@ -105,4 +107,4 @@ class DataProviderRegistry( object ):
|
||||
except NoConverterException:
|
||||
pass
|
||||
|
||||
return data_provider
|
||||
return data_provider
|
||||
|
||||
@@ -0,0 +1,808 @@
|
||||
"""
|
||||
Lower level of visualization framework which does three main things:
|
||||
- associate visualizations with objects
|
||||
- create urls to visualizations based on some target object(s)
|
||||
- unpack a query string into the desired objects needed for rendering
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from galaxy import util
|
||||
import galaxy.model
|
||||
from galaxy.web import url_for
|
||||
|
||||
import logging
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
__TODO__ = """
|
||||
BUGS:
|
||||
anon users clicking a viz link gets 'must be' msg in galaxy_main (w/ masthead)
|
||||
should not show visualizations (no icon)?
|
||||
newick files aren't being sniffed prop? - datatype is txt
|
||||
|
||||
have parsers create objects instead of dicts
|
||||
allow data_sources with no model_class but have tests (isAdmin, etc.)
|
||||
maybe that's an instance of User model_class?
|
||||
some confused vocabulary in docs, var names
|
||||
tests:
|
||||
anding, grouping, not
|
||||
data_sources:
|
||||
lists of
|
||||
add description element to visualization.
|
||||
"""
|
||||
|
||||
class VisualizationsRegistry( object ):
|
||||
"""
|
||||
Main responsibilities are:
|
||||
- testing if an object has a visualization that can be applied to it
|
||||
- generating a link to controllers.visualization.render with
|
||||
the appropriate params
|
||||
- validating and parsing params into resources (based on a context)
|
||||
used in the visualization template
|
||||
"""
|
||||
# these should be handled somewhat differently - and be passed onto their resp. methods in ctrl.visualization
|
||||
#TODO: change/remove if/when they can be updated to use this system
|
||||
BUILT_IN_VISUALIZATIONS = [
|
||||
'trackster',
|
||||
'circster',
|
||||
'sweepster',
|
||||
'phyloviz'
|
||||
]
|
||||
# where to search for visualiztion templates (relative to templates/webapps/galaxy)
|
||||
# this can be overridden individually in the config entries
|
||||
TEMPLATE_ROOT = 'visualization'
|
||||
|
||||
def __str__( self ):
|
||||
listings_keys_str = ','.join( self.listings.keys() ) if self.listings else ''
|
||||
return 'VisualizationsRegistry(%s)' %( listings_keys_str )
|
||||
|
||||
def __init__( self, galaxy_root, configuration_filepath ):
|
||||
# load the registry from the given xml file using the given parser
|
||||
configuration_filepath = os.path.join( galaxy_root, configuration_filepath )
|
||||
configuration_filepath = self.check_conf_filepath( configuration_filepath )
|
||||
self.configuration_filepath = configuration_filepath
|
||||
self.load()
|
||||
|
||||
# what to use to parse query strings into resources/vars for the template
|
||||
self.resource_parser = ResourceParser()
|
||||
|
||||
def check_conf_filepath( self, configuration_filepath ):
|
||||
"""
|
||||
If given file at filepath exists, return that filepath. If not,
|
||||
see if filepath + '.sample' exists and, if so, copy that into filepath.
|
||||
|
||||
If neither original or sample exist, throw an IOError (currently,
|
||||
this is a requireed file).
|
||||
"""
|
||||
if os.path.exists( configuration_filepath ):
|
||||
return configuration_filepath
|
||||
else:
|
||||
sample_file = configuration_filepath + '.sample'
|
||||
if os.path.exists( sample_file ):
|
||||
shutil.copy2( sample_file, configuration_filepath )
|
||||
return configuration_filepath
|
||||
raise IOError( 'visualization configuration file (%s) not found' %( configuration_filepath ) )
|
||||
|
||||
def load( self ):
|
||||
"""
|
||||
Builds the registry by parsing the xml in `self.configuration_filepath`
|
||||
and stores the results in `self.listings`.
|
||||
|
||||
Provided as separate method from `__init__` in order to re-load a
|
||||
new configuration without restarting the instance.
|
||||
"""
|
||||
self.listings = VisualizationsConfigParser.parse( self.configuration_filepath )
|
||||
|
||||
# -- building links to visualizations from objects --
|
||||
def get_visualizations( self, trans, target_object ):
|
||||
"""
|
||||
Get the names of visualizations usable on the `target_object` and
|
||||
the urls to call in order to render the visualizations.
|
||||
"""
|
||||
#TODO:?? a list of objects? YAGNI?
|
||||
# a little weird to pass trans because this registry is part of the trans.app
|
||||
applicable_visualizations = []
|
||||
for vis_name, listing_data in self.listings.items():
|
||||
|
||||
data_sources = listing_data[ 'data_sources' ]
|
||||
for data_source in data_sources:
|
||||
# currently a model class is required
|
||||
model_class = data_source[ 'model_class' ]
|
||||
if not isinstance( target_object, model_class ):
|
||||
continue
|
||||
|
||||
# tests are optional - default is the above class test
|
||||
tests = data_source[ 'tests' ]
|
||||
if tests and not self.is_object_applicable( trans, target_object, tests ):
|
||||
continue
|
||||
|
||||
param_data = data_source[ 'to_params' ]
|
||||
url = self.get_visualization_url( trans, target_object, vis_name, param_data )
|
||||
link_text = listing_data.get( 'link_text', None )
|
||||
if not link_text:
|
||||
# default to visualization name, titlecase, and replace underscores
|
||||
link_text = vis_name.title().replace( '_', ' ' )
|
||||
render_location = listing_data.get( 'render_location' )
|
||||
# remap some of these vars for direct use in ui.js, PopupMenu (e.g. text->html)
|
||||
applicable_visualizations.append({
|
||||
'href' : url,
|
||||
'html' : link_text,
|
||||
'target': render_location
|
||||
})
|
||||
|
||||
return applicable_visualizations
|
||||
|
||||
def is_object_applicable( self, trans, target_object, data_source_tests ):
|
||||
"""
|
||||
Run a visualization's data_source tests to find out if
|
||||
it be applied to the target_object.
|
||||
"""
|
||||
for test in data_source_tests:
|
||||
test_type = test[ 'type' ]
|
||||
result_type = test[ 'result_type' ]
|
||||
test_result = test[ 'result' ]
|
||||
test_fn = test[ 'fn' ]
|
||||
#log.debug( '%s %s: %s, %s, %s, %s', str( target_object ), 'is_object_applicable',
|
||||
# test_type, result_type, test_result, test_fn )
|
||||
|
||||
if test_type == 'isinstance':
|
||||
# parse test_result based on result_type (curr: only datatype has to do this)
|
||||
if result_type == 'datatype':
|
||||
# convert datatypes to their actual classes (for use with isinstance)
|
||||
test_result = trans.app.datatypes_registry.get_datatype_class_by_name( test_result )
|
||||
if not test_result:
|
||||
# warn if can't find class, but continue
|
||||
log.warn( 'visualizations_registry cannot find class (%s) for applicability test', test_result )
|
||||
continue
|
||||
|
||||
if test_fn( target_object, test_result ):
|
||||
#log.debug( 'test passed' )
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_visualization_url( self, trans, target_object, visualization_name, param_data ):
|
||||
"""
|
||||
Generates a url for the visualization with `visualization_name`
|
||||
for use with the given `target_object` with a query string built
|
||||
from the configuration data in `param_data`.
|
||||
"""
|
||||
#precondition: the target_object should be usable by the visualization (accrd. to data_sources)
|
||||
# convert params using vis.data_source.to_params
|
||||
params = self.get_url_params( trans, target_object, param_data )
|
||||
|
||||
# we want existing visualizations to work as normal but still be part of the registry (without mod'ing)
|
||||
# so generate their urls differently
|
||||
url = None
|
||||
if visualization_name in self.BUILT_IN_VISUALIZATIONS:
|
||||
url = url_for( controller='visualization', action=visualization_name, **params )
|
||||
else:
|
||||
url = url_for( controller='visualization', action='render',
|
||||
visualization_name=visualization_name, **params )
|
||||
|
||||
#TODO:?? not sure if embedded would fit/used here? or added in client...
|
||||
return url
|
||||
|
||||
def get_url_params( self, trans, target_object, param_data ):
|
||||
"""
|
||||
Convert the applicable objects and assoc. data into a param dict
|
||||
for a url query string to add to the url that loads the visualization.
|
||||
"""
|
||||
params = {}
|
||||
for to_param_name, to_param_data in param_data.items():
|
||||
#TODO??: look into params as well? what is required, etc.
|
||||
target_attr = to_param_data.get( 'param_attr', None )
|
||||
assign = to_param_data.get( 'assign', None )
|
||||
# one or the other is needed
|
||||
# assign takes precedence (goes last, overwrites)?
|
||||
#NOTE this is only one level
|
||||
if target_attr and hasattr( target_object, target_attr ):
|
||||
params[ to_param_name ] = getattr( target_object, target_attr )
|
||||
if assign:
|
||||
params[ to_param_name ] = assign
|
||||
|
||||
#NOTE!: don't expose raw ids: encode id, _id
|
||||
if params:
|
||||
params = trans.security.encode_dict_ids( params )
|
||||
return params
|
||||
|
||||
# -- getting resources for visualization templates from link query strings --
|
||||
def get_resource_params_and_modifiers( self, visualization_name ):
|
||||
"""
|
||||
Get params and modifiers for the given visualization as a 2-tuple.
|
||||
|
||||
Both `params` and `param_modifiers` default to an empty dictionary.
|
||||
"""
|
||||
visualization = self.listings.get( visualization_name )
|
||||
expected_params = visualization.get( 'params', {} )
|
||||
param_modifiers = visualization.get( 'param_modifiers', {} )
|
||||
return ( expected_params, param_modifiers )
|
||||
|
||||
def query_dict_to_resources( self, trans, controller, visualization_name, query_dict ):
|
||||
"""
|
||||
Use a resource parser, controller, and a visualization's param configuration
|
||||
to convert a query string into the resources and variables a visualization
|
||||
template needs to start up.
|
||||
"""
|
||||
param_confs, param_modifiers = self.get_resource_params_and_modifiers( visualization_name )
|
||||
resources = self.resource_parser.parse_parameter_dictionary(
|
||||
trans, controller, param_confs, query_dict, param_modifiers )
|
||||
return resources
|
||||
|
||||
|
||||
# ------------------------------------------------------------------- parsing the config file
|
||||
class ParsingException( ValueError ):
|
||||
"""
|
||||
An exception class for errors that occur during parsing of the visualizations
|
||||
framework configuration XML file.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class VisualizationsConfigParser( object ):
|
||||
"""
|
||||
Class that parses a visualizations configuration XML file.
|
||||
|
||||
Each visualization will get the following info:
|
||||
- how to load a visualization:
|
||||
-- how to find the proper template
|
||||
-- how to convert query string into DB models
|
||||
- when/how to generate a link to the visualization
|
||||
-- what provides the data
|
||||
-- what information needs to be added to the query string
|
||||
"""
|
||||
VALID_RENDER_LOCATIONS = [ 'galaxy_main', '_top', '_blank' ]
|
||||
|
||||
@classmethod
|
||||
def parse( cls, xml_filepath, debug=True ):
|
||||
"""
|
||||
Static class interface
|
||||
"""
|
||||
return cls( debug ).parse_file( xml_filepath )
|
||||
|
||||
def __init__( self, debug=False ):
|
||||
self.debug = debug
|
||||
|
||||
# what parsers should be used for sub-components
|
||||
self.data_source_parser = DataSourceParser()
|
||||
self.param_parser = ParamParser()
|
||||
self.param_modifier_parser = ParamModifierParser()
|
||||
|
||||
def parse_file( self, xml_filepath ):
|
||||
"""
|
||||
Parse the given XML file for visualizations data.
|
||||
|
||||
If an error occurs while parsing a visualizations entry it is skipped.
|
||||
"""
|
||||
returned = {}
|
||||
try:
|
||||
xml_tree = galaxy.util.parse_xml( xml_filepath )
|
||||
for visualization_conf in xml_tree.getroot().findall( 'visualization' ):
|
||||
visualization = None
|
||||
visualization_name = visualization_conf.get( 'name' )
|
||||
|
||||
try:
|
||||
visualization = self.parse_visualization( visualization_conf )
|
||||
# skip vis' with parsing errors - don't shutdown the startup
|
||||
except ParsingException, parse_exc:
|
||||
log.error( 'Skipped visualization configuration "%s" due to parsing errors: %s',
|
||||
visualization_name, str( parse_exc ), exc_info=self.debug )
|
||||
|
||||
if visualization:
|
||||
returned[ visualization_name ] = visualization
|
||||
|
||||
except Exception, exc:
|
||||
log.error( 'Error parsing visualization configuration file %s: %s',
|
||||
xml_filepath, str( exc ), exc_info=( not self.debug ) )
|
||||
#TODO: change when this is required
|
||||
if self.debug:
|
||||
raise
|
||||
|
||||
return returned
|
||||
|
||||
def parse_visualization( self, xml_tree ):
|
||||
"""
|
||||
Parse the template, name, and any data_sources and params from the
|
||||
given `xml_tree` for a visualization.
|
||||
"""
|
||||
returned = {}
|
||||
|
||||
# data_sources are the kinds of objects/data associated with the visualization
|
||||
# e.g. views on HDAs can use this to find out what visualizations are applicable to them
|
||||
data_sources = []
|
||||
data_sources_confs = xml_tree.find( 'data_sources' )
|
||||
for data_source_conf in data_sources_confs.findall( 'data_source' ):
|
||||
data_source = self.data_source_parser.parse( data_source_conf )
|
||||
if data_source:
|
||||
data_sources.append( data_source )
|
||||
# data_sources are not required
|
||||
if not data_sources:
|
||||
raise ParsingException( 'No valid data_sources for visualization' )
|
||||
returned[ 'data_sources' ] = data_sources
|
||||
|
||||
# parameters spell out how to convert query string params into resources and data
|
||||
# that will be parsed, fetched, etc. and passed to the template
|
||||
# list or dict? ordered or not?
|
||||
params = {}
|
||||
param_confs = xml_tree.find( 'params' )
|
||||
for param_conf in param_confs.findall( 'param' ):
|
||||
param = self.param_parser.parse( param_conf )
|
||||
if param:
|
||||
params[ param_conf.text ]= param
|
||||
# params are not required
|
||||
if params:
|
||||
returned[ 'params' ] = params
|
||||
|
||||
# param modifiers provide extra information for other params (e.g. hda_ldda='hda' -> dataset_id is an hda id)
|
||||
# store these modifiers in a 2-level dictionary { target_param: { param_modifier_key: { param_mod_data }
|
||||
# ugh - wish we didn't need these
|
||||
param_modifiers = {}
|
||||
for param_modifier_conf in param_confs.findall( 'param_modifier' ):
|
||||
param_modifier = self.param_modifier_parser.parse( param_modifier_conf )
|
||||
# param modifiers map accrd. to the params they modify (for faster lookup)
|
||||
target_param = param_modifier_conf.get( 'modifies' )
|
||||
param_modifier_key = param_modifier_conf.text
|
||||
if param_modifier and target_param in params:
|
||||
# multiple params can modify a single, other param,
|
||||
# so store in a sub-dict, initializing if this is the first
|
||||
if target_param not in param_modifiers:
|
||||
param_modifiers[ target_param ] = {}
|
||||
param_modifiers[ target_param ][ param_modifier_key ] = param_modifier
|
||||
|
||||
# not required
|
||||
if param_modifiers:
|
||||
returned[ 'param_modifiers' ] = param_modifiers
|
||||
|
||||
# the template to use in rendering the visualization (required)
|
||||
template = xml_tree.find( 'template' )
|
||||
if template == None or not template.text:
|
||||
raise ParsingException( 'template filename required' )
|
||||
returned[ 'template' ] = template.text
|
||||
|
||||
# link_text: the string to use for the text of any links/anchors to this visualization
|
||||
link_text = xml_tree.find( 'link_text' )
|
||||
if link_text != None and link_text.text:
|
||||
returned[ 'link_text' ] = link_text
|
||||
|
||||
# render_location: where in the browser to open the rendered visualization
|
||||
# defaults to: galaxy_main
|
||||
render_location = xml_tree.find( 'render_location' )
|
||||
if( ( render_location != None and render_location.text )
|
||||
and ( render_location.text in self.VALID_RENDER_LOCATIONS ) ):
|
||||
returned[ 'render_location' ] = render_location.text
|
||||
else:
|
||||
returned[ 'render_location' ] = 'galaxy_main'
|
||||
# consider unifying the above into it's own element and parsing method
|
||||
|
||||
return returned
|
||||
|
||||
|
||||
# ------------------------------------------------------------------- parsing a query string into resources
|
||||
class DataSourceParser( object ):
|
||||
"""
|
||||
Component class of VisualizationsConfigParser that parses data_source elements
|
||||
within visualization elements.
|
||||
|
||||
data_sources are (in the extreme) any object that can be used to produce
|
||||
data for the visualization to consume (e.g. HDAs, LDDAs, Jobs, Users, etc.).
|
||||
There can be more than one data_source associated with a visualization.
|
||||
"""
|
||||
# these are the allowed classes to associate visualizations with (as strings)
|
||||
# any model_class element not in this list will throw a parsing ParsingExcepion
|
||||
ALLOWED_MODEL_CLASSES = [
|
||||
'HistoryDatasetAssociation',
|
||||
'LibraryDatasetDatasetAssociation'
|
||||
]
|
||||
ATTRIBUTE_SPLIT_CHAR = '.'
|
||||
# these are the allowed object attributes to use in data source tests
|
||||
# any attribute element not in this list will throw a parsing ParsingExcepion
|
||||
ALLOWED_DATA_SOURCE_ATTRIBUTES = [
|
||||
'datatype'
|
||||
]
|
||||
|
||||
def parse( self, xml_tree ):
|
||||
"""
|
||||
Return a visualization data_source dictionary parsed from the given
|
||||
XML element.
|
||||
"""
|
||||
returned = {}
|
||||
# model_class (required, only one) - look up and convert model_class to actual galaxy model class
|
||||
model_class = self.parse_model_class( xml_tree.find( 'model_class' ) )
|
||||
if not model_class:
|
||||
raise ParsingException( 'data_source needs a model class' )
|
||||
returned[ 'model_class' ] = model_class
|
||||
|
||||
# tests (optional, 0 or more) - data for boolean test: 'is the visualization usable by this object?'
|
||||
tests = self.parse_tests( xml_tree.findall( 'test' ) )
|
||||
# when no tests are given, default to isinstance( object, model_class )
|
||||
if tests:
|
||||
returned[ 'tests' ] = tests
|
||||
|
||||
# to_params (optional, 0 or more) - tells the registry to set certain params based on the model_clas, tests
|
||||
to_params = self.parse_to_params( xml_tree.findall( 'to_param' ) )
|
||||
if to_params:
|
||||
returned[ 'to_params' ] = to_params
|
||||
|
||||
return returned
|
||||
|
||||
def parse_model_class( self, xml_tree ):
|
||||
"""
|
||||
Convert xml model_class element to a galaxy model class
|
||||
(or None if model class is not found).
|
||||
|
||||
This element is required and only the first element is used.
|
||||
The model_class string must be in ALLOWED_MODEL_CLASSES.
|
||||
"""
|
||||
if xml_tree is None or not xml_tree.text:
|
||||
raise ParsingException( 'data_source entry requires a model_class' )
|
||||
|
||||
if xml_tree.text not in self.ALLOWED_MODEL_CLASSES:
|
||||
log.debug( 'available data_source model_classes: %s' %( str( self.ALLOWED_MODEL_CLASSES ) ) )
|
||||
raise ParsingException( 'Invalid data_source model_class: %s' %( xml_tree.text ) )
|
||||
|
||||
# look up the model from the model module returning an empty data_source if not found
|
||||
model_class = getattr( galaxy.model, xml_tree.text, None )
|
||||
return model_class
|
||||
|
||||
def _build_getattr_lambda( self, attr_name_list ):
|
||||
"""
|
||||
Recursively builds a compound lambda function of getattr's
|
||||
from the attribute names given in `attr_name_list`.
|
||||
"""
|
||||
if len( attr_name_list ) == 0:
|
||||
# identity - if list is empty, return object itself
|
||||
return lambda o: o
|
||||
|
||||
next_attr_name = attr_name_list[-1]
|
||||
if len( attr_name_list ) == 1:
|
||||
# recursive base case
|
||||
return lambda o: getattr( o, next_attr_name )
|
||||
|
||||
# recursive case
|
||||
return lambda o: getattr( self._build_getattr_lambda( attr_name_list[:-1] ), next_attr_name )
|
||||
|
||||
def parse_tests( self, xml_tree_list ):
|
||||
"""
|
||||
Returns a list of test dictionaries that the registry can use
|
||||
against a given object to determine if the visualization can be
|
||||
used with the object.
|
||||
"""
|
||||
# tests should NOT include expensive operations: reading file data, running jobs, etc.
|
||||
# do as much here as possible to reduce the overhead of seeing if a visualization is applicable
|
||||
# currently tests are or'd only (could be and'd or made into compound boolean tests)
|
||||
tests = []
|
||||
if not xml_tree_list:
|
||||
return tests
|
||||
|
||||
for test_elem in xml_tree_list:
|
||||
test_type = test_elem.get( 'type' )
|
||||
test_result = test_elem.text
|
||||
if not test_type or not test_result:
|
||||
log.warn( 'Skipping test. Needs both type attribute and text node to be parsed: '
|
||||
+ '%s, %s' %( test_type, test_elem.text ) )
|
||||
continue
|
||||
|
||||
# test_attr can be a dot separated chain of object attributes (e.g. dataset.datatype) - convert to list
|
||||
#TODO: too dangerous - constrain these to some allowed list
|
||||
test_attr = test_elem.get( 'test_attr' )
|
||||
test_attr = test_attr.split( self.ATTRIBUTE_SPLIT_CHAR ) if isinstance( test_attr, str ) else []
|
||||
# build a lambda function that gets the desired attribute to test
|
||||
getter = self._build_getattr_lambda( test_attr )
|
||||
|
||||
# result type should tell the registry how to convert the result before the test
|
||||
test_result_type = test_elem.get( 'result_type' ) or 'string'
|
||||
|
||||
# test functions should be sent an object to test, and the parsed result expected from the test
|
||||
#TODO: currently, isinstance and string equivalance are the only test types supported
|
||||
if test_type == 'isinstance':
|
||||
#TODO: wish we could take this further but it would mean passing in the datatypes_registry
|
||||
test_fn = lambda o, result: isinstance( getter( o ), result )
|
||||
|
||||
# default to simple (string) equilavance (coercing the test_attr to a string)
|
||||
else:
|
||||
test_fn = lambda o, result: str( getter( o ) ) == result
|
||||
|
||||
tests.append({
|
||||
'type' : test_type,
|
||||
'result' : test_result,
|
||||
'result_type' : test_result_type,
|
||||
'fn' : test_fn
|
||||
})
|
||||
|
||||
return tests
|
||||
|
||||
def parse_to_params( self, xml_tree_list ):
|
||||
"""
|
||||
Given a list of `to_param` elements, returns a dictionary that allows
|
||||
the registry to convert the data_source into one or more appropriate
|
||||
params for the visualization.
|
||||
"""
|
||||
to_param_dict = {}
|
||||
if not xml_tree_list:
|
||||
return to_param_dict
|
||||
|
||||
for element in xml_tree_list:
|
||||
# param_name required
|
||||
param_name = element.text
|
||||
if not param_name:
|
||||
raise ParsingException( 'to_param requires text (the param name)' )
|
||||
|
||||
param = {}
|
||||
# assign is a shortcut param_attr that assigns a value to a param (as text)
|
||||
assign = element.get( 'assign' )
|
||||
if assign != None:
|
||||
param[ 'assign' ] = assign
|
||||
|
||||
# param_attr is the attribute of the object (that the visualization will be applied to)
|
||||
# that should be converted into a query param (e.g. param_attr="id" -> dataset_id)
|
||||
#TODO:?? use the build attr getter here?
|
||||
# simple (1 lvl) attrs for now
|
||||
param_attr = element.get( 'param_attr' )
|
||||
if param_attr != None:
|
||||
param[ 'param_attr' ] = param_attr
|
||||
# element must have either param_attr or assign? what about no params (the object itself)
|
||||
if not param_attr and not assign:
|
||||
raise ParsingException( 'to_param requires either assign or param_attr attributes: %s', param_name )
|
||||
|
||||
#TODO: consider making the to_param name an attribute (param="hda_ldda") and the text what would
|
||||
# be used for the conversion - this would allow CDATA values to be passed
|
||||
#<to_param param="json" type="assign"><![CDATA[{ "one": 1, "two": 2 }]]></to_param>
|
||||
|
||||
if param:
|
||||
to_param_dict[ param_name ] = param
|
||||
|
||||
return to_param_dict
|
||||
|
||||
|
||||
class ParamParser( object ):
|
||||
"""
|
||||
Component class of VisualizationsConfigParser that parses param elements
|
||||
within visualization elements.
|
||||
|
||||
params are parameters that will be parsed (based on their `type`, etc.)
|
||||
and sent to the visualization template by controllers.visualization.render.
|
||||
"""
|
||||
DEFAULT_PARAM_TYPE = 'str'
|
||||
|
||||
def parse( self, xml_tree ):
|
||||
"""
|
||||
Parse a visualization parameter from the given `xml_tree`.
|
||||
"""
|
||||
returned = {}
|
||||
|
||||
# don't store key, just check it
|
||||
param_key = xml_tree.text
|
||||
if not param_key:
|
||||
raise ParsingException( 'Param entry requires text' )
|
||||
|
||||
returned[ 'type' ] = self.parse_param_type( xml_tree )
|
||||
|
||||
# is the parameter required in the template and,
|
||||
# if not, what is the default value?
|
||||
required = xml_tree.get( 'required' ) == "true"
|
||||
returned[ 'required' ] = required
|
||||
if not required:
|
||||
# default defaults to None
|
||||
default = None
|
||||
if 'default' in xml_tree.attrib:
|
||||
default = xml_tree.get( 'default' )
|
||||
# convert default based on param_type here
|
||||
returned[ 'default' ] = default
|
||||
|
||||
# does the param have to be within a list of certain values
|
||||
# NOTE: the interpretation of this list is deferred till parsing and based on param type
|
||||
# e.g. it could be 'val in constrain_to', or 'constrain_to is min, max for number', etc.
|
||||
#TODO: currently unused
|
||||
constrain_to = xml_tree.get( 'constrain_to' )
|
||||
if constrain_to:
|
||||
returned[ 'constrain_to' ] = constrain_to.split( ',' )
|
||||
|
||||
# is the param a comma-separated-value list?
|
||||
returned[ 'csv' ] = xml_tree.get( 'csv' ) == "true"
|
||||
|
||||
# remap keys in the params/query string to the var names used in the template
|
||||
var_name_in_template = xml_tree.get( 'var_name_in_template' )
|
||||
if var_name_in_template:
|
||||
returned[ 'var_name_in_template' ] = var_name_in_template
|
||||
|
||||
return returned
|
||||
|
||||
def parse_param_type( self, xml_tree ):
|
||||
"""
|
||||
Parse a param type from the given `xml_tree`.
|
||||
"""
|
||||
# default to string as param_type
|
||||
param_type = xml_tree.get( 'type' ) or self.DEFAULT_PARAM_TYPE
|
||||
#TODO: set parsers and validaters, convert here
|
||||
return param_type
|
||||
|
||||
|
||||
class ParamModifierParser( ParamParser ):
|
||||
"""
|
||||
Component class of VisualizationsConfigParser that parses param_modifier
|
||||
elements within visualization elements.
|
||||
|
||||
param_modifiers are params from a dictionary (such as a query string)
|
||||
that are not standalone but modify the parsing/conversion of a separate
|
||||
(normal) param (e.g. 'hda_ldda' can equal 'hda' or 'ldda' and control
|
||||
whether a visualizations 'dataset_id' param is for an HDA or LDDA).
|
||||
"""
|
||||
def parse( self, element ):
|
||||
# modifies is required
|
||||
modifies = element.get( 'modifies' )
|
||||
if not modifies:
|
||||
raise ParsingException( 'param_modifier entry requires a target param key (attribute "modifies")' )
|
||||
returned = super( ParamModifierParser, self).parse( element )
|
||||
return returned
|
||||
|
||||
|
||||
class ResourceParser( object ):
|
||||
"""
|
||||
Given a parameter dictionary (often a converted query string) and a
|
||||
configuration dictionary (curr. only VisualizationsRegistry uses this),
|
||||
convert the entries in the parameter dictionary into resources (Galaxy
|
||||
models, primitive types, lists of either, etc.) and return
|
||||
in a new dictionary.
|
||||
|
||||
The keys used to store the new values can optionally be re-mapped to
|
||||
new keys (e.g. dataset_id="NNN" -> hda=<HistoryDatasetAsscoation>).
|
||||
"""
|
||||
#TODO: kinda torn as to whether this belongs here or in controllers.visualization
|
||||
# taking the (questionable) design path of passing a controller in
|
||||
# (which is the responsible party for getting model, etc. resources )
|
||||
# consider making this a base controller? use get_object for the model resources
|
||||
# don't like passing in the app, tho
|
||||
def parse_parameter_dictionary( self, trans, controller, param_config_dict, query_params, param_modifiers=None ):
|
||||
"""
|
||||
Parse all expected params from the query dictionary `query_params`.
|
||||
|
||||
If param is required and not present, raises a `KeyError`.
|
||||
"""
|
||||
# parse the modifiers first since they modify the params coming next
|
||||
#TODO: this is all really for hda_ldda - which we could replace with model polymorphism
|
||||
params_that_modify_other_params = self.parse_parameter_modifiers(
|
||||
trans, controller, param_modifiers, query_params )
|
||||
|
||||
resources = {}
|
||||
for param_name, param_config in param_config_dict.items():
|
||||
# optionally rename the variable returned, defaulting to the original name
|
||||
var_name_in_template = param_config.get( 'var_name_in_template', param_name )
|
||||
|
||||
# if the param is present, get it's value, any param modifiers for that param, and parse it into a resource
|
||||
# use try catch here and not caller to fall back on the default value or re-raise if required
|
||||
resource = None
|
||||
query_val = query_params.get( param_name, None )
|
||||
if query_val is not None:
|
||||
try:
|
||||
target_param_modifiers = params_that_modify_other_params.get( param_name, None )
|
||||
resource = self.parse_parameter( trans, controller, param_config,
|
||||
query_val, param_modifiers=target_param_modifiers )
|
||||
|
||||
except Exception, exception:
|
||||
log.warn( 'Exception parsing visualization param from query: '
|
||||
+ '%s, %s, (%s) %s' %( param_name, query_val, str( type( exception ) ), str( exception ) ))
|
||||
resource = None
|
||||
|
||||
# here - we've either had no value in the query_params or there was a failure to parse
|
||||
# so: error if required, otherwise get a default (which itself defaults to None)
|
||||
if resource == None:
|
||||
if param_config[ 'required' ]:
|
||||
raise KeyError( 'required param %s not found in URL' %( param_name ) )
|
||||
resource = self.parse_parameter_default( trans, param_config )
|
||||
|
||||
resources[ var_name_in_template ] = resource
|
||||
|
||||
return resources
|
||||
|
||||
#TODO: I would LOVE to rip modifiers out completely
|
||||
def parse_parameter_modifiers( self, trans, controller, param_modifiers, query_params ):
|
||||
"""
|
||||
Parse and return parameters that are meant to modify other parameters,
|
||||
be grouped with them, or are needed to successfully parse other parameters.
|
||||
"""
|
||||
# only one level of modification - down that road lies madness
|
||||
# parse the modifiers out of query_params first since they modify the other params coming next
|
||||
parsed_modifiers = {}
|
||||
if not param_modifiers:
|
||||
return parsed_modifiers
|
||||
#precondition: expects a two level dictionary
|
||||
# { target_param_name -> { param_modifier_name -> { param_modifier_data }}}
|
||||
for target_param_name, modifier_dict in param_modifiers.items():
|
||||
parsed_modifiers[ target_param_name ] = target_modifiers = {}
|
||||
|
||||
for modifier_name, modifier_config in modifier_dict.items():
|
||||
query_val = query_params.get( modifier_name, None )
|
||||
if query_val is not None:
|
||||
modifier = self.parse_parameter( trans, controller, modifier_config, query_val )
|
||||
target_modifiers[ modifier_name ] = modifier
|
||||
else:
|
||||
#TODO: required attr?
|
||||
target_modifiers[ modifier_name ] = self.parse_parameter_default( trans, modifier_config )
|
||||
|
||||
return parsed_modifiers
|
||||
|
||||
def parse_parameter_default( self, trans, param_config ):
|
||||
"""
|
||||
Parse any default values for the given param, defaulting the default
|
||||
to `None`.
|
||||
"""
|
||||
# currently, *default* default is None, so this is quaranteed to be part of the dictionary
|
||||
default = param_config[ 'default' ]
|
||||
# if default is None, do not attempt to parse it
|
||||
if default == None:
|
||||
return default
|
||||
# otherwise, parse (currently param_config['default'] is a string just like query param and needs to be parsed)
|
||||
# this saves us the trouble of parsing the default when the config file is read
|
||||
# (and adding this code to the xml parser)
|
||||
return self.parse_parameter( trans, param_config, default )
|
||||
|
||||
def parse_parameter( self, trans, controller, expected_param_data, query_param,
|
||||
recurse=True, param_modifiers=None ):
|
||||
"""
|
||||
Use data in `expected_param_data` to parse `query_param` from a string into
|
||||
a resource usable directly by a template.
|
||||
|
||||
'Primitive' types (string, int, etc.) are parsed here and more complex
|
||||
resources (such as ORM models) are parsed via the `controller` passed
|
||||
in.
|
||||
"""
|
||||
param_type = expected_param_data.get( 'type' )
|
||||
constrain_to = expected_param_data.get( 'constrain_to' )
|
||||
csv = expected_param_data.get( 'csv' )
|
||||
|
||||
parsed_param = None
|
||||
|
||||
# handle recursion for csv values
|
||||
if csv and recurse:
|
||||
parsed_param = []
|
||||
query_param_list = galaxy.util.listify( query_param )
|
||||
for query_param in query_param_list:
|
||||
parsed_param.append( self._parse_param( trans, expected_param_data, query_param, recurse=False ) )
|
||||
return parsed_param
|
||||
|
||||
primitive_parsers = {
|
||||
'str' : lambda param: galaxy.util.sanitize_html.sanitize_html( param, 'utf-8' ),
|
||||
'bool' : lambda param: galaxy.util.string_as_bool( param ),
|
||||
'int' : lambda param: int( param ),
|
||||
'float' : lambda param: float( param ),
|
||||
#'date' : lambda param: ,
|
||||
'json' : ( lambda param: galaxy.util.json.from_json_string(
|
||||
galaxy.util.sanitize_html.sanitize_html( param ) ) ),
|
||||
}
|
||||
parser = primitive_parsers.get( param_type, None )
|
||||
if parser:
|
||||
#TODO: what about param modifiers on primitives?
|
||||
parsed_param = parser( query_param )
|
||||
|
||||
#TODO: constrain_to
|
||||
# this gets complicated - for strings - relatively simple but still requires splitting and using in
|
||||
# for more complicated cases (ints, json) this gets weird quick
|
||||
#TODO:?? remove?
|
||||
|
||||
# db models
|
||||
#TODO: subclass here?
|
||||
elif param_type == 'visualization':
|
||||
encoded_visualization_id = query_param
|
||||
#TODO:?? some fallback if there's no get_X in controller that's passed?
|
||||
parsed_param = controller.get_visualization( trans, encoded_visualization_id,
|
||||
check_ownership=False, check_accessible=True )
|
||||
|
||||
elif param_type == 'dataset':
|
||||
encoded_dataset_id = query_param
|
||||
# really an hda...
|
||||
parsed_param = controller.get_dataset( trans, encoded_dataset_id,
|
||||
check_ownership=False, check_accessible=True )
|
||||
|
||||
elif param_type == 'hda_or_ldda':
|
||||
encoded_dataset_id = query_param
|
||||
# needs info from another param...
|
||||
hda_ldda = param_modifiers.get( 'hda_ldda' )
|
||||
parsed_param = controller.get_hda_or_ldda( trans, hda_ldda, encoded_dataset_id )
|
||||
|
||||
#TODO: ideally this would check v. a list of valid dbkeys
|
||||
elif param_type == 'dbkey':
|
||||
dbkey = query_param
|
||||
parsed_param = galaxy.util.sanitize_html.sanitize_html( dbkey, 'utf-8' )
|
||||
|
||||
#print ( '%s, %s -> %s, %s' %( param_type, query_param, str( type( parsed_param ) ), parsed_param ) )
|
||||
return parsed_param
|
||||
@@ -14,7 +14,7 @@ import routes
|
||||
from sqlalchemy import func, and_, select
|
||||
from paste.httpexceptions import HTTPBadRequest, HTTPInternalServerError, HTTPNotImplemented, HTTPRequestRangeNotSatisfiable
|
||||
|
||||
from galaxy import util, web
|
||||
from galaxy import util, web, model
|
||||
from gettext import gettext
|
||||
from galaxy.datatypes.interval import ChromatinInteractions
|
||||
from galaxy.exceptions import ItemAccessibilityException, ItemDeletionException, ItemOwnershipException, MessageException
|
||||
@@ -28,7 +28,6 @@ from galaxy.workflow.modules import module_factory
|
||||
from galaxy.model.orm import eagerload, eagerload_all
|
||||
from galaxy.datatypes.data import Text
|
||||
|
||||
|
||||
from galaxy.datatypes.display_applications import util as da_util
|
||||
from galaxy.datatypes.metadata import FileParameter
|
||||
|
||||
@@ -486,6 +485,25 @@ class UsesHistoryDatasetAssociationMixin:
|
||||
error( "Please wait until this dataset finishes uploading before attempting to view it." )
|
||||
return hda
|
||||
|
||||
def get_hda_list( self, trans, hda_ids, check_ownership=True, check_accessible=False, check_state=True ):
|
||||
"""
|
||||
Returns one or more datasets in a list.
|
||||
|
||||
If a dataset is not found or is inaccessible to trans.user,
|
||||
add None in its place in the list.
|
||||
"""
|
||||
# precondtion: dataset_ids is a list of encoded id strings
|
||||
hdas = []
|
||||
for id in hda_ids:
|
||||
hda = None
|
||||
try:
|
||||
hda = self.get_dataset( trans, id,
|
||||
check_ownership=check_ownership, check_accesible=check_accesible, check_state=check_state )
|
||||
except Exception, exception:
|
||||
pass
|
||||
hdas.append( hda )
|
||||
return hdas
|
||||
|
||||
def get_data( self, dataset, preview=True ):
|
||||
"""
|
||||
Gets a dataset's data.
|
||||
@@ -552,9 +570,13 @@ class UsesHistoryDatasetAssociationMixin:
|
||||
if meta_files:
|
||||
hda_dict[ 'meta_files' ] = meta_files
|
||||
|
||||
#hda_dict[ 'display_types' ] = self.get_old_display_applications( trans, hda )
|
||||
#hda_dict[ 'display_apps' ] = self.get_display_apps( trans, hda )
|
||||
hda_dict[ 'visualizations' ] = hda.get_visualizations()
|
||||
# currently, the viz reg is optional - handle on/off
|
||||
if trans.app.visualizations_registry:
|
||||
hda_dict[ 'visualizations' ] = trans.app.visualizations_registry.get_visualizations( trans, hda )
|
||||
else:
|
||||
hda_dict[ 'visualizations' ] = hda.get_visualizations()
|
||||
#TODO: it may also be wiser to remove from here and add as API call that loads the visualizations
|
||||
# when the visualizations button is clicked (instead of preloading/pre-checking)
|
||||
|
||||
# ---- return here if deleted
|
||||
if hda.deleted and not purged:
|
||||
@@ -662,18 +684,187 @@ class UsesLibraryMixinItems( SharableItemSecurityMixin ):
|
||||
return self.get_object( trans, id, 'LibraryDataset', check_ownership=False, check_accessible=check_accessible )
|
||||
|
||||
|
||||
class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin,
|
||||
UsesLibraryMixinItems ):
|
||||
""" Mixin for controllers that use Visualization objects. """
|
||||
class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin, UsesLibraryMixinItems ):
|
||||
"""
|
||||
Mixin for controllers that use Visualization objects.
|
||||
"""
|
||||
|
||||
DEFAULT_ORDER_BY = [ model.Visualization.title ]
|
||||
viz_types = [ "trackster" ]
|
||||
|
||||
def create_visualization( self, trans, type, title="Untitled Genome Vis", slug=None, dbkey=None, annotation=None, config={}, save=True ):
|
||||
""" Create visualiation and first revision. """
|
||||
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
|
||||
try:
|
||||
visualization = trans.sa_session.query( trans.model.Visualization ).get( trans.security.decode_id( id ) )
|
||||
except TypeError:
|
||||
visualization = None
|
||||
if not visualization:
|
||||
error( "Visualization not found" )
|
||||
else:
|
||||
return self.security_check( trans, visualization, check_ownership, check_accessible )
|
||||
|
||||
def get_visualizations_by_user( self, trans, user, order_by=None, query_only=False ):
|
||||
"""
|
||||
Return query or query results of visualizations filtered by a user.
|
||||
|
||||
Set `order_by` to a column or list of columns to change the order
|
||||
returned. Defaults to `DEFAULT_ORDER_BY`.
|
||||
Set `query_only` to return just the query for further filtering or
|
||||
processing.
|
||||
"""
|
||||
if not order_by:
|
||||
order_by = self.DEFAULT_ORDER_BY
|
||||
if not isinstance( order_by, list ):
|
||||
order_by = [ order_by ]
|
||||
query = trans.sa_session.query( model.Visualization )
|
||||
query = query.filter( model.Visualization.user == user )
|
||||
if order_by:
|
||||
query = query.order_by( *order_by )
|
||||
if query_only:
|
||||
return query
|
||||
return query.all()
|
||||
|
||||
def get_visualizations_shared_with_user( self, trans, user, order_by=None, query_only=False ):
|
||||
"""
|
||||
Return query or query results for visualizations shared with the given user.
|
||||
|
||||
Set `order_by` to a column or list of columns to change the order
|
||||
returned. Defaults to `DEFAULT_ORDER_BY`.
|
||||
Set `query_only` to return just the query for further filtering or
|
||||
processing.
|
||||
"""
|
||||
if not order_by:
|
||||
order_by = self.DEFAULT_ORDER_BY
|
||||
if not isinstance( order_by, list ):
|
||||
order_by = [ order_by ]
|
||||
query = trans.sa_session.query( model.Visualization ).join( model.VisualizationUserShareAssociation )
|
||||
query = query.filter( model.VisualizationUserShareAssociation.user_id == user.id )
|
||||
# remove duplicates when a user shares with themselves?
|
||||
query = query.filter( model.Visualization.user_id != user.id )
|
||||
if order_by:
|
||||
query = query.order_by( *order_by )
|
||||
if query_only:
|
||||
return query
|
||||
return query.all()
|
||||
|
||||
def get_published_visualizations( self, trans, exclude_user=None, order_by=None, query_only=False ):
|
||||
"""
|
||||
Return query or query results for published visualizations optionally excluding
|
||||
the user in `exclude_user`.
|
||||
|
||||
Set `order_by` to a column or list of columns to change the order
|
||||
returned. Defaults to `DEFAULT_ORDER_BY`.
|
||||
Set `query_only` to return just the query for further filtering or
|
||||
processing.
|
||||
"""
|
||||
if not order_by:
|
||||
order_by = self.DEFAULT_ORDER_BY
|
||||
if not isinstance( order_by, list ):
|
||||
order_by = [ order_by ]
|
||||
query = trans.sa_session.query( model.Visualization )
|
||||
query = query.filter( model.Visualization.published == True )
|
||||
if exclude_user:
|
||||
query = query.filter( model.Visualization.user != exclude_user )
|
||||
if order_by:
|
||||
query = query.order_by( *order_by )
|
||||
if query_only:
|
||||
return query
|
||||
return query.all()
|
||||
|
||||
#TODO: move into model (get_api_value)
|
||||
def get_visualization_summary_dict( self, visualization ):
|
||||
"""
|
||||
Return a set of summary attributes for a visualization in dictionary form.
|
||||
NOTE: that encoding ids isn't done here should happen at the caller level.
|
||||
"""
|
||||
#TODO: deleted
|
||||
#TODO: importable
|
||||
return {
|
||||
'id' : visualization.id,
|
||||
'title' : visualization.title,
|
||||
'type' : visualization.type,
|
||||
'dbkey' : visualization.dbkey,
|
||||
}
|
||||
|
||||
def get_visualization_dict( self, visualization ):
|
||||
"""
|
||||
Return a set of detailed attributes for a visualization in dictionary form.
|
||||
The visualization's latest_revision is returned in its own sub-dictionary.
|
||||
NOTE: that encoding ids isn't done here should happen at the caller level.
|
||||
"""
|
||||
return {
|
||||
'model_class': 'Visualization',
|
||||
'id' : visualization.id,
|
||||
'title' : visualization.title,
|
||||
'type' : visualization.type,
|
||||
'user_id' : visualization.user.id,
|
||||
'dbkey' : visualization.dbkey,
|
||||
'slug' : visualization.slug,
|
||||
# dictify only the latest revision (allow older to be fetched elsewhere)
|
||||
'latest_revision' : self.get_visualization_revision_dict( visualization.latest_revision ),
|
||||
'revisions' : [ r.id for r in visualization.revisions ],
|
||||
}
|
||||
|
||||
def get_visualization_revision_dict( self, revision ):
|
||||
"""
|
||||
Return a set of detailed attributes for a visualization in dictionary form.
|
||||
NOTE: that encoding ids isn't done here should happen at the caller level.
|
||||
"""
|
||||
return {
|
||||
'model_class': 'VisualizationRevision',
|
||||
'id' : revision.id,
|
||||
'visualization_id' : revision.visualization.id,
|
||||
'title' : revision.title,
|
||||
'dbkey' : revision.dbkey,
|
||||
'config' : revision.config,
|
||||
}
|
||||
|
||||
def import_visualization( self, trans, id, user=None ):
|
||||
"""
|
||||
Copy the visualization with the given id and associate the copy
|
||||
with the given user (defaults to trans.user).
|
||||
|
||||
Raises `ItemAccessibilityException` if `user` is not passed and
|
||||
the current user is anonymous, and if the visualization is not `importable`.
|
||||
Raises `ItemDeletionException` if the visualization has been deleted.
|
||||
"""
|
||||
# default to trans.user, error if anon
|
||||
if not user:
|
||||
if not trans.user:
|
||||
raise ItemAccessibilityException( "You must be logged in to import Galaxy visualizations" )
|
||||
user = trans.user
|
||||
|
||||
# check accessibility
|
||||
visualization = self.get_visualization( trans, id, check_ownership=False )
|
||||
if not visualization.importable:
|
||||
raise ItemAccessibilityException( "The owner of this visualization has disabled imports via this link." )
|
||||
if visualization.deleted:
|
||||
raise ItemDeletionException( "You can't import this visualization because it has been deleted." )
|
||||
|
||||
# copy vis and alter title
|
||||
#TODO: need to handle custom db keys.
|
||||
imported_visualization = visualization.copy( user=user, title="imported: " + visualization.title )
|
||||
trans.sa_session.add( imported_visualization )
|
||||
trans.sa_session.flush()
|
||||
return imported_visualization
|
||||
|
||||
def create_visualization( self, trans, type, title="Untitled Genome Vis", slug=None,
|
||||
dbkey=None, annotation=None, config={}, save=True ):
|
||||
"""
|
||||
Create visualiation and first revision.
|
||||
"""
|
||||
visualization = self._create_visualization( trans, title, type, dbkey, slug, annotation, save )
|
||||
#TODO: handle this error structure better either in _create or here
|
||||
if isinstance( visualization, dict ):
|
||||
err_dict = visualization
|
||||
raise ValueError( err_dict[ 'title_err' ] or err_dict[ 'slug_err' ] )
|
||||
|
||||
# Create and save first visualization revision
|
||||
revision = trans.model.VisualizationRevision( visualization=visualization, title=title, config=config, dbkey=dbkey )
|
||||
revision = trans.model.VisualizationRevision( visualization=visualization, title=title,
|
||||
config=config, dbkey=dbkey )
|
||||
visualization.latest_revision = revision
|
||||
|
||||
if save:
|
||||
@@ -683,6 +874,21 @@ class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin,
|
||||
|
||||
return visualization
|
||||
|
||||
def add_visualization_revision( self, trans, visualization, config, title, dbkey ):
|
||||
"""
|
||||
Adds a new `VisualizationRevision` to the given `visualization` with
|
||||
the given parameters and set its parent visualization's `latest_revision`
|
||||
to the new revision.
|
||||
"""
|
||||
#precondition: only add new revision on owned vis's
|
||||
#TODO:?? should we default title, dbkey, config? to which: visualization or latest_revision?
|
||||
revision = trans.model.VisualizationRevision( visualization, title, dbkey, config )
|
||||
visualization.latest_revision = revision
|
||||
#TODO:?? does this automatically add revision to visualzation.revisions?
|
||||
trans.sa_session.add( revision )
|
||||
trans.sa_session.flush()
|
||||
return revision
|
||||
|
||||
def save_visualization( self, trans, config, type, id=None, title=None, dbkey=None, slug=None, annotation=None ):
|
||||
session = trans.sa_session
|
||||
|
||||
@@ -697,8 +903,10 @@ class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin,
|
||||
# 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
|
||||
# do NOT alter the dbkey
|
||||
vis_rev.dbkey = vis.dbkey
|
||||
# do alter the title and config
|
||||
vis_rev.title = title
|
||||
|
||||
# -- Validate config. --
|
||||
|
||||
@@ -760,18 +968,6 @@ class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin,
|
||||
encoded_id = trans.security.encode_id( vis.id )
|
||||
return { "vis_id": encoded_id, "url": url_for( controller='visualization', action=vis.type, id=encoded_id ) }
|
||||
|
||||
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
|
||||
try:
|
||||
visualization = trans.sa_session.query( trans.model.Visualization ).get( trans.security.decode_id( id ) )
|
||||
except TypeError:
|
||||
visualization = None
|
||||
if not visualization:
|
||||
error( "Visualization not found" )
|
||||
else:
|
||||
return self.security_check( trans, 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
|
||||
@@ -911,7 +1107,6 @@ class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin,
|
||||
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:
|
||||
@@ -920,6 +1115,8 @@ class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin,
|
||||
self.create_item_slug( trans.sa_session, visualization )
|
||||
if annotation:
|
||||
annotation = sanitize_html( annotation, 'utf-8', 'text/html' )
|
||||
#TODO: if this is to stay in the mixin, UsesAnnotations should be added to the superclasses
|
||||
# right now this is depending on the classes that include this mixin to have UsesAnnotations
|
||||
self.add_item_annotation( trans.sa_session, trans.user, visualization, annotation )
|
||||
|
||||
if save:
|
||||
|
||||
@@ -51,17 +51,20 @@ def app_factory( global_conf, **kwargs ):
|
||||
webapp.add_route( '/async/:tool_id/:data_id/:data_secret', controller='async', action='index', tool_id=None, data_id=None, data_secret=None )
|
||||
webapp.add_route( '/:controller/:action', action='index' )
|
||||
webapp.add_route( '/:action', controller='root', action='index' )
|
||||
|
||||
# allow for subdirectories in extra_files_path
|
||||
webapp.add_route( '/datasets/:dataset_id/display/{filename:.+?}', controller='dataset', action='display', dataset_id=None, filename=None)
|
||||
webapp.add_route( '/datasets/:dataset_id/:action/:filename', controller='dataset', action='index', dataset_id=None, filename=None)
|
||||
webapp.add_route( '/display_application/:dataset_id/:app_name/:link_name/:user_id/:app_action/:action_param', controller='dataset', action='display_application', dataset_id=None, user_id=None, app_name = None, link_name = None, app_action = None, action_param = None )
|
||||
webapp.add_route( '/display_application/:dataset_id/:app_name/:link_name/:user_id/:app_action/:action_param',
|
||||
controller='dataset', action='display_application', dataset_id=None, user_id=None,
|
||||
app_name = None, link_name = None, app_action = None, action_param = None )
|
||||
webapp.add_route( '/u/:username/d/:slug/:filename', controller='dataset', action='display_by_username_and_slug', filename=None )
|
||||
webapp.add_route( '/u/:username/p/:slug', controller='page', action='display_by_username_and_slug' )
|
||||
webapp.add_route( '/u/:username/h/:slug', controller='history', action='display_by_username_and_slug' )
|
||||
webapp.add_route( '/u/:username/w/:slug', controller='workflow', action='display_by_username_and_slug' )
|
||||
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
|
||||
webapp.add_route( '/search', controller='search', action='index' )
|
||||
|
||||
|
||||
# Add the web API
|
||||
webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
|
||||
# The /folders section is experimental at this point:
|
||||
@@ -144,6 +147,10 @@ def app_factory( global_conf, **kwargs ):
|
||||
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
|
||||
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
|
||||
|
||||
# visualizations registry generic template renderer
|
||||
webapp.add_route( '/visualization/show/:visualization_name',
|
||||
controller='visualization', action='render', visualization_name=None )
|
||||
|
||||
# "POST /api/workflows/import" => ``workflows.import_workflow()``.
|
||||
# Defines a named route "import_workflow".
|
||||
webapp.mapper.connect("import_workflow", "/api/workflows/upload", controller="workflows", action="import_new_workflow", conditions=dict(method=["POST"]))
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
|
||||
from sqlalchemy import desc, or_, and_
|
||||
from paste.httpexceptions import HTTPNotFound
|
||||
|
||||
from galaxy import model, web
|
||||
from galaxy.model.item_attrs import UsesAnnotations, UsesItemRatings
|
||||
from galaxy.web.base.controller import BaseUIController, SharableMixin, UsesVisualizationMixin
|
||||
@@ -16,6 +20,9 @@ from galaxy.visualization.genomes import GenomeRegion
|
||||
|
||||
from .library import LibraryListGrid
|
||||
|
||||
import logging
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
#
|
||||
# -- Grids --
|
||||
#
|
||||
@@ -688,6 +695,54 @@ class VisualizationController( BaseUIController, SharableMixin, UsesAnnotations,
|
||||
dbkeys=trans.app.genomes.get_dbkeys( trans, chrom_info=True ),
|
||||
default_dbkey=kwargs.get("default_dbkey", None) )
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "use Galaxy visualizations", use_panels=True )
|
||||
def render( self, trans, visualization_name, embedded=None, **kwargs ):
|
||||
"""
|
||||
Render the appropriate visualization template, parsing the `kwargs`
|
||||
into appropriate variables and resources (such as ORM models)
|
||||
based on this visualizations `param` data in visualizations_conf.xml.
|
||||
|
||||
URL: /visualization/show/{visualization_name}
|
||||
"""
|
||||
# validate name vs. registry
|
||||
registry = trans.app.visualizations_registry
|
||||
if not registry:
|
||||
raise HTTPNotFound( 'No visualization registry (possibly disabled in universe_wsgi.ini)')
|
||||
if visualization_name not in registry.listings:
|
||||
raise HTTPNotFound( 'Unknown or invalid visualization: ' + visualization_name )
|
||||
# or redirect to list?
|
||||
registry_listing = registry.listings[ visualization_name ]
|
||||
|
||||
returned = None
|
||||
try:
|
||||
# convert query string to resources for template based on registry config
|
||||
#NOTE: passing in controller to keep resource lookup within the controller's responsibilities
|
||||
# (and not the ResourceParser)
|
||||
resources = registry.query_dict_to_resources( trans, self, visualization_name, kwargs )
|
||||
|
||||
# look up template and render
|
||||
template_root = registry_listing.get( 'template_root', registry.TEMPLATE_ROOT )
|
||||
template = registry_listing[ 'template' ]
|
||||
template_path = os.path.join( template_root, template )
|
||||
#NOTE: passing *unparsed* kwargs as query_args
|
||||
#NOTE: shared_vars is a dictionary for shared data in the template
|
||||
# this feels hacky to me but it's what mako recommends:
|
||||
# http://docs.makotemplates.org/en/latest/runtime.html
|
||||
#TODO: embedded
|
||||
returned = trans.fill_template( template_path, visualization_name=visualization_name,
|
||||
embedded=embedded, query_args=kwargs, shared_vars={}, **resources )
|
||||
|
||||
except Exception, exception:
|
||||
log.exception( 'error rendering visualization (%s): %s', visualization_name, str( exception ) )
|
||||
if trans.debug: raise
|
||||
returned = trans.show_error_message(
|
||||
"There was an error rendering the visualization. " +
|
||||
"Contact your Galaxy administrator if the problem persists." +
|
||||
"<br/>Details: " + str( exception ), use_panels=False )
|
||||
|
||||
return returned
|
||||
|
||||
@web.expose
|
||||
@web.require_login()
|
||||
def trackster(self, trans, id=None, **kwargs):
|
||||
@@ -792,6 +847,8 @@ class VisualizationController( BaseUIController, SharableMixin, UsesAnnotations,
|
||||
get the visualization with the given id; otherwise, create a new visualization using
|
||||
a given dataset and regions.
|
||||
"""
|
||||
print 'sweepster:', id, hda_ldda, dataset_id, regions
|
||||
regions = regions or '{}'
|
||||
# Need to create history if necessary in order to create tool form.
|
||||
trans.get_history( create=True )
|
||||
|
||||
@@ -803,6 +860,7 @@ class VisualizationController( BaseUIController, SharableMixin, UsesAnnotations,
|
||||
else:
|
||||
# Loading new visualization.
|
||||
dataset = self.get_hda_or_ldda( trans, hda_ldda, dataset_id )
|
||||
print 'dataset:', dataset
|
||||
job = get_dataset_job( dataset )
|
||||
viz_config = {
|
||||
'dataset_id': dataset_id,
|
||||
|
||||
@@ -239,8 +239,25 @@ var HDAEditView = HDABaseView.extend( LoggableMixin ).extend(
|
||||
* @returns {jQuery} rendered DOM
|
||||
*/
|
||||
_render_visualizationsButton : function(){
|
||||
var visualizations = this.model.get( 'visualizations' );
|
||||
if( ( !this.model.hasData() )
|
||||
|| ( _.isEmpty( visualizations ) ) ){
|
||||
this.visualizationsButton = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
//TODO: this is a bridge to allow the framework to be switched off
|
||||
// remove this fn and use the other when fully integrated
|
||||
if( _.isObject( visualizations[0] ) ){
|
||||
return this._render_visualizationsFrameworkButton( visualizations );
|
||||
}
|
||||
|
||||
if( !this.urls.visualization ){
|
||||
this.visualizationsButton = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
var dbkey = this.model.get( 'dbkey' ),
|
||||
visualizations = this.model.get( 'visualizations' ),
|
||||
visualization_url = this.urls.visualization,
|
||||
popup_menu_dict = {},
|
||||
params = {
|
||||
@@ -250,17 +267,10 @@ var HDAEditView = HDABaseView.extend( LoggableMixin ).extend(
|
||||
// Add dbkey to params if it exists.
|
||||
if( dbkey ){ params.dbkey = dbkey; }
|
||||
|
||||
if( !( this.model.hasData() )
|
||||
|| !( visualizations && visualizations.length )
|
||||
|| !( visualization_url ) ){
|
||||
this.visualizationsButton = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
// render the icon from template
|
||||
this.visualizationsButton = new IconButtonView({ model : new IconButton({
|
||||
title : _l( 'Visualize' ),
|
||||
href : visualization_url,
|
||||
href : this.urls.visualization,
|
||||
icon_class : 'chart_curve'
|
||||
})});
|
||||
var $icon = this.visualizationsButton.render().$el;
|
||||
@@ -299,6 +309,40 @@ var HDAEditView = HDABaseView.extend( LoggableMixin ).extend(
|
||||
}
|
||||
return $icon;
|
||||
},
|
||||
|
||||
/** Render an icon-button or popupmenu of links based on the applicable visualizations
|
||||
* @returns {jQuery} rendered DOM
|
||||
*/
|
||||
_render_visualizationsFrameworkButton : function( visualizations ){
|
||||
if( !( this.model.hasData() )
|
||||
|| !( visualizations && !_.isEmpty( visualizations ) ) ){
|
||||
this.visualizationsButton = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
// render the icon from template
|
||||
this.visualizationsButton = new IconButtonView({ model : new IconButton({
|
||||
title : _l( 'Visualize' ),
|
||||
icon_class : 'chart_curve'
|
||||
})});
|
||||
var $icon = this.visualizationsButton.render().$el;
|
||||
$icon.addClass( 'visualize-icon' ); // needed?
|
||||
|
||||
// No need for popup menu because there's a single visualization.
|
||||
if( _.keys( visualizations ).length === 1 ) {
|
||||
$icon.attr( 'title', _.keys( visualizations )[0] );
|
||||
$icon.attr( 'href', _.values( visualizations )[0] );
|
||||
|
||||
// >1: Populate menu dict with visualization fns, make the popupmenu
|
||||
} else {
|
||||
var popup_menu_options = [];
|
||||
_.each( visualizations, function( linkData ) {
|
||||
popup_menu_options.push( linkData );
|
||||
});
|
||||
var popup = new PopupMenu( $icon, popup_menu_options );
|
||||
}
|
||||
return $icon;
|
||||
},
|
||||
|
||||
// ......................................................................... secondary actions
|
||||
/** Render secondary actions: currently tagging and annotation (if user is allowed).
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
define(["libs/underscore","viz/trackster/util"],function(b,e){var c=Backbone.Model.extend({initialize:function(f){var g=this.get("key");this.set("id",g);var h=b.find(c.known_settings_defaults,function(i){return i.key===g});if(h){this.set(b.extend({},h,f))}if(this.get("type")==="color"&&!this.get("value")){this.set("value",e.get_random_color())}this.on("change:value",this.cast_value,this)},cast_value:function(){var f=this.get("type"),g=this.get("value");if(f==="float"){g=parseFloat(g)}else{if(f==="int"){g=parseInt(g,10)}}this.set("value")}},{known_settings_defaults:[{key:"name",label:"Name",type:"text",default_value:""},{key:"color",label:"Color",type:"color",default_value:null},{key:"min_value",label:"Min Value",type:"float",default_value:null},{key:"max_value",label:"Max Value",type:"float",default_value:null},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true},{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"block_color",label:"Block color",type:"color",default_value:null},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:null},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"histogram_max",label:"Histogram maximum",type:"float",default_value:null,help:"Clear value to set automatically"},{key:"mode",type:"string",default_value:this.mode,hidden:true}]});var d=Backbone.Collection.extend({model:c,to_key_value_dict:function(){var f={};this.each(function(g){f[g.get("key")]=g.get("value")});return f},get_value:function(f){var g=this.get(f);if(g){return g.get("value")}return undefined}},{from_config_dict:function(g){var f=b.map(b.keys(g),function(h){return{key:h,value:g[h]}});return new d(f)}});var a=Backbone.View.extend({className:"config-settings-view",render:function(){var i=this.model;var f=this.$el;var h;function g(n,j){for(var r=0;r<n.length;r++){h=n[r];if(h.hidden){continue}var l="param_"+r;var v=i.values[h.key];var x=$("<div class='form-row' />").appendTo(j);x.append($("<label />").attr("for",l).text(h.label+":"));if(type==="bool"){x.append($('<input type="checkbox" />').attr("id",l).attr("name",l).attr("checked",v))}else{if(type==="text"){x.append($('<input type="text"/>').attr("id",l).val(v).click(function(){$(this).select()}))}else{if(type==="select"){var t=$("<select />").attr("id",l);for(var p=0;p<h.options.length;p++){$("<option/>").text(h.options[p].label).attr("value",h.options[p].value).appendTo(t)}t.val(v);x.append(t)}else{if(type==="color"){var w=$("<div/>").appendTo(x),s=$("<input />").attr("id",l).attr("name",l).val(v).css("float","left").appendTo(w).click(function(z){$(".bs-tooltip").removeClass("in");var y=$(this).siblings(".bs-tooltip").addClass("in");y.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(y).height()/2)+($(this).height()/2)}).show();y.click(function(A){A.stopPropagation()});$(document).bind("click.color-picker",function(){y.hide();$(document).unbind("click.color-picker")});z.stopPropagation()}),q=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(w).attr("title","Set new random color").tooltip(),u=$("<div class='bs-tooltip right' style='position: absolute;' />").appendTo(w).hide(),m=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(u),k=$("<div class='tooltip-arrow'></div>").appendTo(u),o=$.farbtastic(m,{width:100,height:100,callback:s,color:v});w.append($("<div/>").css("clear","both"));(function(y){q.click(function(){y.setColor(e.get_random_color())})})(o)}else{x.append($("<input />").attr("id",l).attr("name",l).val(v))}}}}if(h.help){x.append($("<div class='help'/>").text(h.help))}}}g(this.params,f);return this},render_in_modal:function(){var h=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},f=function(){this.update_from_form();hide_modal();$(window).unbind("keypress.check_enter_esc")},g=function(i){if((i.keyCode||i.which)===27){h()}else{if((i.keyCode||i.which)===13){f()}}};$(window).bind("keypress.check_enter_esc",g);if(this.$el.children().length===0){this.render()}show_modal("Configure",drawable.config.build_form(),{Cancel:h,OK:f})},update_from_form:function(){var f=this;this.collection.each(function(h,g){if(!h.get("hidden")){var j="param_"+g;var i=f.$el.find("#"+j).val();if(type==="bool"){i=container.find("#"+j).is(":checked")}h.set("value",i)}})}});return{ConfigSettingCollection:d,ConfigSettingCollectionView:a}});
|
||||
define(["libs/underscore","viz/trackster/util"],function(b,e){var c=Backbone.Model.extend({initialize:function(f){var g=this.get("key");this.set("id",g);var h=b.find(c.known_settings_defaults,function(i){return i.key===g});if(h){this.set(b.extend({},h,f))}if(this.get("type")==="color"&&!this.get("value")){this.set("value",e.get_random_color())}this.on("change:value",this.cast_value,this)},cast_value:function(){var f=this.get("type"),g=this.get("value");if(f==="float"){g=parseFloat(g)}else{if(f==="int"){g=parseInt(g,10)}}this.set("value")}},{known_settings_defaults:[{key:"name",label:"Name",type:"text",default_value:""},{key:"color",label:"Color",type:"color",default_value:null},{key:"min_value",label:"Min Value",type:"float",default_value:null},{key:"max_value",label:"Max Value",type:"float",default_value:null},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true},{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"block_color",label:"Block color",type:"color",default_value:null},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:null},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true}]});var d=Backbone.Collection.extend({model:c,to_key_value_dict:function(){var f={};this.each(function(g){f[g.get("key")]=g.get("value")});return f},get_value:function(f){var g=this.get(f);if(g){return g.get("value")}return undefined}},{from_config_dict:function(g){var f=b.map(b.keys(g),function(h){return{key:h,value:g[h]}});return new d(f)}});var a=Backbone.View.extend({className:"config-settings-view",render:function(){var i=this.model;var f=this.$el;var h;function g(n,j){for(var r=0;r<n.length;r++){h=n[r];if(h.hidden){continue}var l="param_"+r;var v=i.values[h.key];var x=$("<div class='form-row' />").appendTo(j);x.append($("<label />").attr("for",l).text(h.label+":"));if(type==="bool"){x.append($('<input type="checkbox" />').attr("id",l).attr("name",l).attr("checked",v))}else{if(type==="text"){x.append($('<input type="text"/>').attr("id",l).val(v).click(function(){$(this).select()}))}else{if(type==="select"){var t=$("<select />").attr("id",l);for(var p=0;p<h.options.length;p++){$("<option/>").text(h.options[p].label).attr("value",h.options[p].value).appendTo(t)}t.val(v);x.append(t)}else{if(type==="color"){var w=$("<div/>").appendTo(x),s=$("<input />").attr("id",l).attr("name",l).val(v).css("float","left").appendTo(w).click(function(z){$(".bs-tooltip").removeClass("in");var y=$(this).siblings(".bs-tooltip").addClass("in");y.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(y).height()/2)+($(this).height()/2)}).show();y.click(function(A){A.stopPropagation()});$(document).bind("click.color-picker",function(){y.hide();$(document).unbind("click.color-picker")});z.stopPropagation()}),q=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(w).attr("title","Set new random color").tooltip(),u=$("<div class='bs-tooltip right' style='position: absolute;' />").appendTo(w).hide(),m=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(u),k=$("<div class='tooltip-arrow'></div>").appendTo(u),o=$.farbtastic(m,{width:100,height:100,callback:s,color:v});w.append($("<div/>").css("clear","both"));(function(y){q.click(function(){y.setColor(e.get_random_color())})})(o)}else{x.append($("<input />").attr("id",l).attr("name",l).val(v))}}}}if(h.help){x.append($("<div class='help'/>").text(h.help))}}}g(this.params,f);return this},render_in_modal:function(){var h=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},f=function(){this.update_from_form();hide_modal();$(window).unbind("keypress.check_enter_esc")},g=function(i){if((i.keyCode||i.which)===27){h()}else{if((i.keyCode||i.which)===13){f()}}};$(window).bind("keypress.check_enter_esc",g);if(this.$el.children().length===0){this.render()}show_modal("Configure",drawable.config.build_form(),{Cancel:h,OK:f})},update_from_form:function(){var f=this;this.collection.each(function(h,g){if(!h.get("hidden")){var j="param_"+g;var i=f.$el.find("#"+j).val();if(type==="bool"){i=container.find("#"+j).is(":checked")}h.set("value",i)}})}});return{ConfigSettingCollection:d,ConfigSettingCollectionView:a}});
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -222,9 +222,9 @@ ${h.js(
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
|
||||
var hda = ${h.to_json_string( hda )},
|
||||
historyID = '${historyID}',
|
||||
querySettings = ${h.to_json_string( kwargs )},
|
||||
var hda = ${h.to_json_string( hda.get_api_value() )},
|
||||
historyID = '${trans.security.encode_id( hda.history.id )}',
|
||||
querySettings = ${h.to_json_string( query_args )},
|
||||
chartConfig = _.extend( querySettings, {
|
||||
containerSelector : '#chart',
|
||||
//TODO: move to ScatterplotControlForm.initialize
|
||||
@@ -250,8 +250,8 @@ $(function(){
|
||||
<%def name="body()">
|
||||
<!--dataset info-->
|
||||
<div id="chart-header" class="header">
|
||||
<h2 class="title">Scatterplot of '${hda['name']}'</h2>
|
||||
<p class="subtitle">${hda['misc_info']}</p>
|
||||
<h2 class="title">Scatterplot of '${hda.name}'</h2>
|
||||
<p class="subtitle">${hda.info}</p>
|
||||
</div>
|
||||
<div id="scatterplot" class="scatterplot-control-form"></div>
|
||||
</%def>
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%def name="title()">
|
||||
${visualization_name}
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="stylesheets()">
|
||||
${parent.stylesheets()}
|
||||
${h.css(
|
||||
"base",
|
||||
)}
|
||||
|
||||
<style type="text/css">
|
||||
/*TODO: use/move into base.less*/
|
||||
* { margin: 0px; padding: 0px; }
|
||||
</style>
|
||||
|
||||
</%def>
|
||||
|
||||
<%def name="process_hda( hda )">
|
||||
<%
|
||||
hda_dict = hda.get_api_value()
|
||||
hda_dict[ 'id' ] = trans.security.encode_id( hda_dict[ 'id' ] )
|
||||
hda_dict[ 'history_id' ] = trans.security.encode_id( hda_dict[ 'history_id' ] )
|
||||
del hda_dict[ 'peek' ]
|
||||
return hda_dict
|
||||
%>
|
||||
</%def>
|
||||
|
||||
<%def name="javascripts()">
|
||||
${parent.javascripts()}
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
|
||||
//var data = {
|
||||
// title : 'shared with user visualization',
|
||||
// type : 'test',
|
||||
// slug : 'shared',
|
||||
// annotation : 'a visualization shared with a specific user',
|
||||
// config : {
|
||||
// x : 10,
|
||||
// y : 10
|
||||
// }
|
||||
// };
|
||||
|
||||
//var creationPromise = jQuery.ajax( '/api/visualizations', {
|
||||
// type : 'POST',
|
||||
// contentType : 'application/json',
|
||||
// data : JSON.stringify( data )
|
||||
// });
|
||||
//creationPromise.success(function(){
|
||||
// console.debug( 'success' );
|
||||
//});
|
||||
//creationPromise.error(function(){
|
||||
// console.debug( 'error' );
|
||||
//});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="print_var( name, var )">
|
||||
%if var is not None:
|
||||
<% t = str( type( var ) )[1:-1] %>
|
||||
<p>${name}: ${t}, ${var}</p>
|
||||
%else:
|
||||
<p>No ${name}</p>
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="body()">
|
||||
<%
|
||||
import pprint
|
||||
print self
|
||||
print self.context
|
||||
pprint.pprint( self.context.kwargs, indent=4 )
|
||||
%>
|
||||
<%
|
||||
vars_to_print = [
|
||||
( 'default', default ),
|
||||
( 'string', string ),
|
||||
( 'boolean', boolean ),
|
||||
( 'integer', integer ),
|
||||
( 'float', float ),
|
||||
( 'json', json ),
|
||||
]
|
||||
%>
|
||||
%for name, var in vars_to_print:
|
||||
${print_var( name, var )}
|
||||
%endfor
|
||||
|
||||
%if visualization:
|
||||
<h1>${visualization.title}</h1>
|
||||
<p>id: ${trans.security.encode_id( visualization.id )}</p>
|
||||
<p>dbkey: ${visualization.dbkey}</p>
|
||||
<p>config:
|
||||
<pre>${h.to_json_string( visualization.latest_revision.config, sort_keys=True, indent=( 4 * ' ' ) )}</pre>
|
||||
</p>
|
||||
%endif
|
||||
|
||||
%if dataset:
|
||||
<h1>${dataset.name}</h1>
|
||||
<p>id: ${trans.security.encode_id( dataset.id )}</p>
|
||||
<p>history id: ${trans.security.encode_id( dataset.history.id )}</p>
|
||||
<pre>
|
||||
${h.to_json_string( process_hda( dataset ), sort_keys=True, indent=( 4 * ' ' ) )}
|
||||
</pre>
|
||||
%endif
|
||||
|
||||
%if dataset_instance:
|
||||
<h1>${dataset_instance.name}</h1>
|
||||
<p>id: ${trans.security.encode_id( dataset_instance.id )}</p>
|
||||
%if hda_ldda == 'hda':
|
||||
<p>history id: ${trans.security.encode_id( dataset_instance.history.id )}</p>
|
||||
<pre>
|
||||
${h.to_json_string( process_hda( dataset_instance ), sort_keys=True, indent=( 4 * ' ' ) )}
|
||||
</pre>
|
||||
%else:
|
||||
<p>(LibraryDatasetDatasetAssociation)</p>
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if query_args:
|
||||
<ul>
|
||||
%for key, val in query_args.items():
|
||||
<li>${key} : ${val}</li>
|
||||
%endfor
|
||||
</ul>
|
||||
%endif
|
||||
|
||||
</%def>
|
||||
@@ -171,6 +171,10 @@ paste.app_factory = galaxy.web.buildapp:app_factory
|
||||
# Galaxy.
|
||||
#datatypes_config_file = datatypes_conf.xml
|
||||
|
||||
# Visualizations config file, defines what visualizations apply to particular data and how to pass them
|
||||
# the necessary parameters
|
||||
#visualizations_conf_path = visualizations_conf.xml
|
||||
|
||||
# Each job is given a unique empty directory as its current working directory.
|
||||
# This option defines in what parent directory those directories will be
|
||||
# created.
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
This is the xml file to edit to add new visualizations to the framework.
|
||||
NOTE!: this is a work in progress!
|
||||
|
||||
Note: also that visualizations that fail to parse in visualizations/registry will
|
||||
produce an error in the server log, but otherwise will be skipped and not available.
|
||||
If you can't find your visualization in the UI, check the server logs for errors
|
||||
during start up.
|
||||
-->
|
||||
|
||||
<!-- .......................................................................... DTD -->
|
||||
<!-- runnable on NIX with xmllint -->
|
||||
<!DOCTYPE visualizations [
|
||||
<!-- 0 or more visualizations -->
|
||||
<!ELEMENT visualizations (visualization*)>
|
||||
<!-- each visualization must have a template (all other elements are optional) -->
|
||||
<!ELEMENT visualization (data_sources*,params*,template_root*,template,link_text*,render_location*)>
|
||||
<!-- visualization name (e.g. 'trackster', 'scatterplot', etc.) is required -->
|
||||
<!ATTLIST visualization
|
||||
name CDATA #REQUIRED
|
||||
>
|
||||
|
||||
<!ELEMENT data_sources (data_source*)>
|
||||
<!-- data sources are elements that describe what objects (HDAs, LDDAs, Job, User, etc.)
|
||||
are applicable to a visualization. Often these are used to fetch applicable links
|
||||
to the visualizations that use them.
|
||||
-->
|
||||
<!ELEMENT data_source (model_class,(test|to_param)*)>
|
||||
<!ELEMENT model_class (#PCDATA)>
|
||||
<!-- model_class is currently the class name of the object you want to make a visualization
|
||||
applicable to (e.g. HistoryDatasetAssociation). Currently only classes in galaxy.model
|
||||
can be used.
|
||||
REQUIRED and currently limited to: 'HistoryDatasetAssociation', 'LibraryDatasetDatasetAssociation'
|
||||
-->
|
||||
<!ELEMENT test (#PCDATA)>
|
||||
<!-- tests help define what conditions the visualization can be applied to the model_class/target.
|
||||
Currently, all tests are OR'd and there is no logical grouping. Tests are run in order.
|
||||
(text): the text of this element is what the given target will be compared to (REQUIRED)
|
||||
type: what type of test to run (e.g. when the target is an HDA the test will often be of type 'isinstance'
|
||||
and test whether the HDA's datatype isinstace of a class)
|
||||
DEFAULT: string comparison.
|
||||
test_attr: what attribute of the target object should be used in the test. For instance, 'datatype'
|
||||
will attempt to get the HDA.datatype from a target HDA. If the given object doesn't have
|
||||
that attribute the test will fail (with no error). test_attr can be dot separated attributes,
|
||||
looking up each in turn. For example, if the target was a history, one could access the
|
||||
history.user.email by setting test_attr to 'user.email' (why you would want that, I don't know)
|
||||
DEFAULT: to comparing the object itself (and not any of it's attributes)
|
||||
result_type: if the result (the text of the element mentioned above) needs to be parsed into
|
||||
something other than a string, result_type will tell the registry how to do this. E.g.
|
||||
if result_type is 'datatype' the registry will assume the text is a datatype class name
|
||||
and parse it into the proper class before the test (often 'isinstance') is run.
|
||||
DEFAULT: no parsing (result should be a string)
|
||||
-->
|
||||
<!ATTLIST test
|
||||
type CDATA #IMPLIED
|
||||
test_attr CDATA #IMPLIED
|
||||
result_type CDATA #IMPLIED
|
||||
>
|
||||
|
||||
<!ELEMENT to_param (#PCDATA)>
|
||||
<!-- to_param tells the registry how to parse the data_source into a query string param.
|
||||
For example, HDA data_sources can set param_to text to 'dataset_id' and param_attr to 'id' and the
|
||||
the target HDA (if it passes the tests) will be passed as "dataset_id=HDA.id"
|
||||
(text): the query string param key this source will be parsed into (e.g. dataset_id)
|
||||
REQUIRED
|
||||
param_attr: the attribute of the data_source object to use as the value in the query string param.
|
||||
E.g. param_attr='id' for an HDA data_source would use the (encoded) id.
|
||||
NOTE: a to_param MUST have either a param_attr or assign
|
||||
assign: you can use this to directly assign a value to a query string's param. E.g. if the
|
||||
data_source is a LDDA we can set 'hda_or_ldda=ldda' using assign='ldda'.
|
||||
NOTE: a to_param MUST have either a param_attr or assign
|
||||
-->
|
||||
<!ATTLIST to_param
|
||||
param_attr CDATA #IMPLIED
|
||||
assign CDATA #IMPLIED
|
||||
>
|
||||
|
||||
<!ELEMENT params ((param|param_modifier)*)>
|
||||
<!-- params describe what data will be sent to a visualization template and
|
||||
how to convert them from a query string in a URL into variables usable in a template.
|
||||
For example,
|
||||
param_modifiers are a special class of parameters that modify other params
|
||||
(e.g. hda_ldda can be 'hda' or 'ldda' and modifies/informs dataset_id to fetch an HDA or LDDA)
|
||||
-->
|
||||
<!ELEMENT param (#PCDATA)>
|
||||
<!-- param tells the registry how to parse the query string param back into a resource/data_source.
|
||||
For example, if a query string has "dataset_id=NNN" and the type is 'dataset', the registry
|
||||
will attempt to fetch the hda with id of NNN from the database and pass it to the template.
|
||||
(text): the query string param key this source will be parsed from (e.g. dataset_id)
|
||||
REQUIRED
|
||||
type: the type of the resource.
|
||||
Can be: str (DEFAULT), bool, int, float, json, visualization, dbkey, dataset, or hda_ldda.
|
||||
default: if a param is not passed on the query string (and is not required) OR the given param
|
||||
fails to parse, this value is used instead.
|
||||
DEFAULT: None
|
||||
required: set this to true if the param is required for the template. Rendering will with an error
|
||||
if the param hasn't been sent.
|
||||
DEFAULT: false
|
||||
csv: set this to true if the param is a comma separated list. The registry will attempt to
|
||||
parse each value as the given type and send the result as a list to the template.
|
||||
DEFAULT: false
|
||||
constrain_to: (currently unused) constain a param to a set of values, error if not valid.
|
||||
DEFAULT: don't constrain
|
||||
var_name_in_template: a new name for the resource/variable to use in the template. E.g. an initial
|
||||
query string param key might be 'dataset_id' in the URL, the registry parses it into an HDA,
|
||||
and if var_name_in_template is set to 'hda', the template will be able to access the HDA
|
||||
with the variable name 'hda' (as in hda.title).
|
||||
DEFAULT: keep the original query string name
|
||||
-->
|
||||
<!ATTLIST param
|
||||
type CDATA #IMPLIED
|
||||
default CDATA #IMPLIED
|
||||
required CDATA #IMPLIED
|
||||
csv CDATA #IMPLIED
|
||||
constrain_to CDATA #IMPLIED
|
||||
var_name_in_template CDATA #IMPLIED
|
||||
>
|
||||
<!-- param_modifiers are the same as param but have a REQUIRED 'modifies' attribute.
|
||||
'modifies' must point to the param name (the text part of param element) that it will modify.
|
||||
E.g. <param_modifier modifies="dataset_id">hda_ldda</param_modifier>
|
||||
-->
|
||||
<!ELEMENT param_modifier (#PCDATA)>
|
||||
<!ATTLIST param_modifier
|
||||
modifies CDATA #REQUIRED
|
||||
type CDATA #IMPLIED
|
||||
default CDATA #IMPLIED
|
||||
required CDATA #IMPLIED
|
||||
csv CDATA #IMPLIED
|
||||
constrain_to CDATA #IMPLIED
|
||||
var_name_in_template CDATA #IMPLIED
|
||||
>
|
||||
|
||||
<!-- template_root: the directory to search for the template relative to templates/webapps/galaxy
|
||||
(optional) DEFAULT: visualizations
|
||||
-->
|
||||
<!ELEMENT template_root (#PCDATA)>
|
||||
<!-- template: the template used to render the visualization. REQUIRED -->
|
||||
<!ELEMENT template (#PCDATA)>
|
||||
<!-- link_text: the text component of an html anchor displayed when the registry builds the link information -->
|
||||
<!ELEMENT link_text (#PCDATA)>
|
||||
<!-- render_location: used as the target attribute of the link to the visualization.
|
||||
Can be 'galaxy_main', '_top', '_blank'. DEFAULT: 'galaxy_main'
|
||||
-->
|
||||
<!-- TODO: rename -> render_target -->
|
||||
<!ELEMENT render_location (#PCDATA)>
|
||||
]>
|
||||
|
||||
<!-- .......................................................................... configuration xml -->
|
||||
<visualizations>
|
||||
<visualization name="trackster">
|
||||
<!--not tested yet -->
|
||||
<data_sources>
|
||||
<data_source>
|
||||
<model_class>HistoryDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="hda">hda_ldda</to_param>
|
||||
<to_param param_attr="dbkey">dbkey</to_param>
|
||||
</data_source>
|
||||
<data_source>
|
||||
<model_class>LibraryDatasetDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="ldda">hda_ldda</to_param>
|
||||
</data_source>
|
||||
</data_sources>
|
||||
<params>
|
||||
<param type="visualization">id</param>
|
||||
<param type="dataset">dataset_id</param>
|
||||
<param type="genome_region">genome_region</param>
|
||||
<param type="dbkey">dbkey</param>
|
||||
</params>
|
||||
<template_root>tracks</template_root>
|
||||
<template>browser.mako</template>
|
||||
<render_location>_top</render_location>
|
||||
</visualization>
|
||||
|
||||
<visualization name="circster">
|
||||
<data_sources>
|
||||
<data_source>
|
||||
<model_class>HistoryDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="hda">hda_ldda</to_param>
|
||||
</data_source>
|
||||
<data_source>
|
||||
<model_class>LibraryDatasetDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="ldda">hda_ldda</to_param>
|
||||
</data_source>
|
||||
</data_sources>
|
||||
<params>
|
||||
<param type="visualization">id</param>
|
||||
<param type="hda_or_ldda">dataset_id</param>
|
||||
<param_modifier type="string" modifies="dataset_id">hda_ldda</param_modifier>
|
||||
<param type="dbkey">dbkey</param>
|
||||
</params>
|
||||
<template>circster.mako</template>
|
||||
<render_location>_top</render_location>
|
||||
</visualization>
|
||||
|
||||
<!--
|
||||
<visualization name="sweepster">
|
||||
<data_sources>
|
||||
<data_source>
|
||||
<model_class>HistoryDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="hda">hda_ldda</to_param>
|
||||
</data_source>
|
||||
<data_source>
|
||||
<model_class>LibraryDatasetDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="ldda">hda_ldda</to_param>
|
||||
</data_source>
|
||||
</data_sources>
|
||||
<params>
|
||||
<param type="visualization" var_name_in_template="viz">visualization</param>
|
||||
<param type="hda_or_ldda" var_name_in_template="dataset">dataset_id</param>
|
||||
<param_modifier type="string" modifies="dataset_id">hda_ldda</param_modifier>
|
||||
</params>
|
||||
<template>sweepster.mako</template>
|
||||
<render_location>_top</render_location>
|
||||
</visualization>
|
||||
-->
|
||||
|
||||
<visualization name="phyloviz">
|
||||
<data_sources>
|
||||
<data_source>
|
||||
<model_class>HistoryDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Newick</test>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Nexus</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
</data_source>
|
||||
</data_sources>
|
||||
<params>
|
||||
<param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
|
||||
<param type="integer" default="0">tree_index</param>
|
||||
</params>
|
||||
<template>phyloviz.mako</template>
|
||||
<render_location>_top</render_location>
|
||||
</visualization>
|
||||
|
||||
<visualization name="scatterplot">
|
||||
<data_sources>
|
||||
<data_source>
|
||||
<model_class>HistoryDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">tabular.Tabular</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
</data_source>
|
||||
</data_sources>
|
||||
<params>
|
||||
<param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
|
||||
</params>
|
||||
<template>scatterplot.mako</template>
|
||||
</visualization>
|
||||
|
||||
<!--
|
||||
<visualization name="test">
|
||||
<data_sources>
|
||||
<data_source>
|
||||
<model_class>HistoryDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="bler">string</to_param>
|
||||
<to_param assign="False">boolean</to_param>
|
||||
<to_param assign="-5">integer</to_param>
|
||||
<to_param assign="3.14">float</to_param>
|
||||
<to_param assign="{}">json</to_param>
|
||||
</data_source>
|
||||
</data_sources>
|
||||
<params>
|
||||
<param>default</param>
|
||||
<param type="str">string</param>
|
||||
<param type="bool">boolean</param>
|
||||
<param type="int">integer</param>
|
||||
<param type="float">float</param>
|
||||
<param type="json">json</param>
|
||||
<param type="str" required="true">string</param>
|
||||
<param type="visualization" var_name_in_template="visualization">visualization_id</param>
|
||||
<param type="dataset" var_name_in_template="dataset">dataset_id</param>
|
||||
<param type="hda_or_ldda">dataset_instance</param>
|
||||
<param_modifier type="str" modifies="dataset_instance">hda_ldda</param_modifier>
|
||||
</params>
|
||||
<template>v_fwork_test.mako</template>
|
||||
</visualization>
|
||||
-->
|
||||
|
||||
</visualizations>
|
||||
Reference in New Issue
Block a user