Visualizations framework: PluginFramework class, serving static and template files from plugins

This commit is contained in:
Carl Eberhard
2013-08-06 15:02:10 -04:00
parent 92e4dbfcdd
commit 17addb206f
18 changed files with 356 additions and 211 deletions
+1 -1
View File
@@ -60,7 +60,7 @@ shed_tool_data_table_conf.xml
job_conf.xml
data_manager_conf.xml
shed_data_manager_conf.xml
config/visualizations/*.xml
config/*
static/welcome.html.*
static/welcome.html
+34
View File
@@ -0,0 +1,34 @@
Custom visualization plugins
----------------------------
Visualizations can be added to your Galaxy instance by creating
sub-directories, templates, and static files here.
Properly configured and written visualizations will be accessible to
the user when they click the 'visualizations' icon for a dataset
in their history panel.
The framework must be enabled in your 'universe_wsgi.ini' file by
uncommenting (and having a valid path for) the
'visualizations_plugin_directory' entry.
For more information, see http://wiki.galaxyproject.org/VisualizationsRegistry
Sub-directory structure
-----------------------
In general, sub-directories should follow the pattern:
my_visualization/
config/
my_visualization.xml
static/
... any static files the visualization needs (if any)
templates/
... any Mako templates the visualization needs
The XML config file for a visualization plugin can be validated on the command
line using (from your plugin directory):
xmllint my_visualization/config/my_visualization.xml --valid --noout
@@ -1,5 +1,3 @@
<!-- runnable on NIX with xmllint -->
<!-- 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 -->
-26
View File
@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE visualization SYSTEM "visualization.dtd">
<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>
-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE visualization SYSTEM "visualization.dtd">
<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>
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE visualization SYSTEM "visualization.dtd">
<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>
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE visualization SYSTEM "visualization.dtd">
<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>
@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE visualization SYSTEM "visualization.dtd">
<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>
+2 -4
View File
@@ -123,10 +123,8 @@ class UniverseApplication( object ):
# Load genome indexer tool.
load_genome_index_tools( self.toolbox )
# visualizations registry: associates resources with visualizations, controls how to render
self.visualizations_registry = None
if self.config.visualizations_config_directory:
self.visualizations_registry = VisualizationsRegistry( self.config.root,
self.config.visualizations_config_directory )
self.visualizations_registry = VisualizationsRegistry.from_config(
self.config.visualizations_plugins_directory, self.config )
# 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 )
+4 -2
View File
@@ -291,8 +291,10 @@ 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 ) )
# visualization registries config directory
self.visualizations_config_directory = kwargs.get( 'visualizations_config_directory', None )
# PLUGINS:
self.plugin_frameworks = []
# visualization framework
self.visualizations_plugins_directory = kwargs.get( 'visualizations_plugins_directory', None )
@property
def sentry_dsn_public( self ):
+58 -64
View File
@@ -12,6 +12,8 @@ from galaxy import util
import galaxy.model
from galaxy.web import url_for
from galaxy.web.base import pluginframework
import logging
log = logging.getLogger( __name__ )
@@ -28,17 +30,16 @@ some confused vocabulary in docs, var names
tests:
anding, grouping, not
has_dataprovider
user is admin
data_sources:
lists of
add description element to visualization.
TESTS to add:
has dataprovider
user is admin
user_pref for ordering/ex/inclusion of particular visualizations
"""
# ------------------------------------------------------------------- the registry
class VisualizationsRegistry( object ):
class VisualizationsRegistry( pluginframework.PluginFramework ):
"""
Main responsibilities are:
- testing if an object has a visualization that can be applied to it
@@ -47,6 +48,7 @@ class VisualizationsRegistry( object ):
- validating and parsing params into resources (based on a context)
used in the visualization template
"""
#: any built in visualizations that have their own render method in ctrls/visualization
# 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 = [
@@ -55,57 +57,37 @@ class VisualizationsRegistry( object ):
'sweepster',
'phyloviz'
]
# where to search for visualiztion templates (relative to templates/webapps/galaxy)
#: where to search for visualiztion templates (relative to templates/webapps/galaxy)
# this can be overridden individually in the config entries
TEMPLATE_ROOT = 'visualization'
#: directories under plugin_directory that aren't plugins
non_plugin_directories = [ 'bler' ]
def __str__( self ):
listings_keys_str = ','.join( self.listings.keys() ) if self.listings else ''
return 'VisualizationsRegistry(%s)' %( listings_keys_str )
return 'VisualizationsRegistry(%s)' %( self.plugin_directory )
def __init__( self, galaxy_root, configuration_filepath ):
# load the registry from the xml files located in configuration_filepath using the given parser
configuration_filepath = os.path.join( galaxy_root, configuration_filepath )
self.configuration_filepath = self.check_conf_filepath( configuration_filepath )
self.move_sample_conf_files()
self.load()
def __init__( self, registry_filepath, template_cache_dir ):
super( VisualizationsRegistry, self ).__init__( registry_filepath, template_cache_dir )
# what to use to parse query strings into resources/vars for the template
self.resource_parser = ResourceParser()
log.debug( '%s loaded', str( self ) )
def check_conf_filepath( self, configuration_filepath ):
def load_configuration( self ):
"""
Checks for the existence of the given filepath.
:param configurarion_filepath: full filepath to the visualization config directory
:raises IOError: if the given directory doesn't exist
"""
if not os.path.exists( configuration_filepath ):
raise IOError( 'visualization configuration directory (%s) not found' %( configuration_filepath ) )
return configuration_filepath
Builds the registry by parsing the `config/*.xml` files for every plugin
in ``get_plugin_directories`` and stores the results in ``self.listings``.
def move_sample_conf_files( self ):
..note::
This could be used to re-load a new configuration without restarting
the instance.
"""
Copies any `*.xml.sample` files in `configuration_filepath` to
`.xml` files of the same names if no file with that name already exists.
try:
self.listings = VisualizationsConfigParser.parse( self.get_plugin_directories() )
:returns: a list of the files moved
"""
files_moved = []
for sample_file in glob.glob( os.path.join( self.configuration_filepath, '*.sample' ) ):
new_name = os.path.splitext( sample_file )[0]
if not os.path.exists( new_name ):
shutil.copy2( sample_file, new_name )
files_moved.append( new_name )
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 )
except Exception, exc:
log.exception( 'Error parsing visualizations plugins %s', self.plugin_directory )
raise
def get_visualization( self, trans, visualization_name, target_object ):
"""
@@ -283,11 +265,11 @@ class VisualizationsConfigParser( object ):
VALID_RENDER_LOCATIONS = [ 'galaxy_main', '_top', '_blank' ]
@classmethod
def parse( cls, config_dir, debug=True ):
def parse( cls, plugin_directories, debug=False ):
"""
Static class interface.
"""
return cls( debug ).parse_files( config_dir )
return cls( debug ).parse_plugins( plugin_directories )
def __init__( self, debug=False ):
self.debug = debug
@@ -297,33 +279,45 @@ class VisualizationsConfigParser( object ):
self.param_parser = ParamParser()
self.param_modifier_parser = ParamModifierParser()
def parse_files( self, config_dir ):
def parse_plugins( self, plugin_directories ):
"""
Parse each XML file in `config_dir` for visualizations config data.
Parses the config files for each plugin sub-dir in `base_path`.
:param plugin_directories: a list of paths to enabled plugins.
If an error occurs while parsing a visualizations entry, it is skipped.
:returns: registry data in dictionary form
"""
returned = {}
try:
for xml_filepath in glob.glob( os.path.join( config_dir, '*.xml' ) ):
try:
visualization_name, visualization = self.parse_file( xml_filepath )
# skip vis' with parsing errors - don't shutdown the startup
except ParsingException, parse_exc:
log.error( 'Skipped visualization config "%s" due to parsing errors: %s',
xml_filepath, str( parse_exc ), exc_info=self.debug )
for plugin_path in plugin_directories:
returned.update( self.parse_plugin( plugin_path ) )
return returned
if visualization:
returned[ visualization_name ] = visualization
log.debug( 'Visualization config loaded for: %s', visualization_name )
def parse_plugin( self, plugin_path ):
"""
Parses any XML files in ``<plugin_path>/config``.
except Exception, exc:
log.error( 'Error parsing visualizations configuration directory %s: %s',
config_dir, str( exc ), exc_info=( not self.debug ) )
#TODO: change when this framework is on by default
if self.debug:
raise
If an error occurs while parsing a visualizations entry, it is skipped.
:returns: registry data in dictionary form
..note::
assumes config files are in a 'config' sub-dir of each plugin
"""
returned = {}
plugin_config_path = os.path.join( plugin_path, 'config' )
if not os.path.isdir( plugin_config_path ):
return returned
for xml_filepath in glob.glob( os.path.join( plugin_config_path, '*.xml' ) ):
try:
visualization_name, visualization = self.parse_file( xml_filepath )
# skip vis' with parsing errors - don't shutdown the startup
except ParsingException, parse_exc:
log.error( 'Skipped visualization config "%s" due to parsing errors: %s',
xml_filepath, str( parse_exc ), exc_info=self.debug )
if visualization:
returned[ visualization_name ] = visualization
log.debug( 'Visualization config loaded for: %s', visualization_name )
return returned
+216
View File
@@ -0,0 +1,216 @@
"""
Base class for plugins - frameworks or systems that may:
* serve static content
* serve templated html
* have some configuration at startup
"""
import os.path
import glob
import sys
import pkg_resources
pkg_resources.require( 'MarkupSafe' )
pkg_resources.require( 'Mako' )
import mako
# ============================================================================= exceptions
class PluginFrameworkException( Exception ):
"""Base exception for plugin frameworks.
"""
pass
class PluginFrameworkConfigException( PluginFrameworkException ):
"""Exception for plugin framework configuration errors.
"""
pass
class PluginFrameworkStaticException( PluginFrameworkException ):
"""Exception for plugin framework static directory set up errors.
"""
pass
class PluginFrameworkTemplateException( PluginFrameworkException ):
"""Exception for plugin framework template directory
and template rendering errors.
"""
pass
# ============================================================================= base
class PluginFramework( object ):
"""
Plugins are files/directories living outside the Galaxy ``lib`` directory
that serve static files (css, js, images, etc.), use and serve mako templates,
and have some configuration to control the rendering.
A plugin framework sets up all the above components.
"""
#: does the class need a config file(s) to be parsed?
has_config = True
#: does the class need static files served?
serves_static = True
#: does the class need template files served?
serves_templates = True
#TODO: allow plugin mako inheritance from existing ``/templates`` files
#uses_galaxy_templates = True
#TODO: possibly better as instance var (or a combo)
#: the directories in ``plugin_directory`` with basenames listed here will
#: be ignored for config, static, and templates
non_plugin_directories = []
# ------------------------------------------------------------------------- setup
@classmethod
def from_config( cls, config_plugin_directory, config ):
"""
Set up the framework based on data from some config object by:
* constructing it's absolute plugin_directory filepath
* getting a template_cache
* and appending itself to the config object's ``plugin_frameworks`` list
.. note::
precondition: config obj should have attributes:
root, template_cache, and (list) plugin_frameworks
"""
# currently called from (base) app.py - defined here to allow override if needed
if not config_plugin_directory:
return None
try:
full_plugin_filepath = os.path.join( config.root, config_plugin_directory )
template_cache = config.template_cache if cls.serves_static else None
plugin = cls( full_plugin_filepath, template_cache )
config.plugin_frameworks.append( plugin )
return plugin
except PluginFrameworkException, plugin_exc:
log.exception( "Error loading framework %s. Skipping...", cls.__class__.__name__ )
return None
def __str__( self ):
return '%s(%s)' %( self.__class__.__name__, self.plugin_directory )
def __init__( self, plugin_directory, template_cache_dir=None, debug=False ):
if not os.path.isdir( plugin_directory ):
raise PluginFrameworkException( 'Framework plugin directory not found: %s, %s'
%( self.__class__.__name__, plugin_directory ) )
# absolute (?) path
self.plugin_directory = plugin_directory
self.name = os.path.basename( self.plugin_directory )
if self.has_config:
self.load_configuration()
# set_up_static_urls will be called during the static middleware creation (if serves_static)
if self.serves_templates:
self.set_up_templates( template_cache_dir )
def get_plugin_directories( self ):
"""
Return the plugin directory paths for this plugin.
Gets any directories within ``plugin_directory`` that are directories
themselves and whose ``basename`` is not in ``plugin_directory``.
"""
# could instead explicitly list on/off in master config file
for plugin_path in glob.glob( os.path.join( self.plugin_directory, '*' ) ):
if not os.path.isdir( plugin_path ):
continue
if os.path.basename( plugin_path ) in self.non_plugin_directories:
continue
yield plugin_path
# ------------------------------------------------------------------------- config
def load_configuration( self ):
"""
Override to load some framework/plugin specifc configuration.
"""
# Abstract method
return True
# ------------------------------------------------------------------------- serving static files
def get_static_urls_and_paths( self ):
"""
For each plugin, return a 2-tuple where the first element is a url path
to the plugin's static files and the second is a filesystem path to those
same files.
Meant to be passed to a Static url map.
"""
url_and_paths = []
# called during the static middleware creation (buildapp.py, wrap_in_static)
# NOTE: this only searches for static dirs two levels deep (i.e. <plugin_directory>/<plugin-name>/static)
for plugin_path in self.get_plugin_directories():
# that path is a plugin, search for subdirs named static in THAT dir
plugin_static_path = os.path.join( plugin_path, 'static' )
if not os.path.isdir( plugin_static_path ):
continue
# build a url for that static subdir and create a Static urlmap entry for it
plugin_name = os.path.splitext( os.path.basename( plugin_path ) )[0]
plugin_url = self.name + '/' + plugin_name + '/static'
url_and_paths.append( ( plugin_url, plugin_static_path ) )
return url_and_paths
# ------------------------------------------------------------------------- templates
def set_up_templates( self, template_cache_dir ):
"""
Add a ``template_lookup`` attribute to the framework that can be passed
to the mako renderer to find templates.
"""
if not template_cache_dir:
raise PluginFrameworkTemplateException( 'Plugins that serve templates require a template_cache_dir' )
self.template_lookup = self._create_mako_template_lookup( template_cache_dir, self._get_template_paths() )
return self.template_lookup
def _get_template_paths( self ):
"""
Get the paths that will be searched for templates.
"""
return [ self.plugin_directory ]
def _create_mako_template_lookup( self, cache_dir, paths, collection_size=500, output_encoding='utf-8' ):
"""
Create a ``TemplateLookup`` with defaults.
"""
return mako.lookup.TemplateLookup(
directories = paths,
module_directory = cache_dir,
collection_size = collection_size,
output_encoding = output_encoding )
#TODO: do we want to remove trans and app from the plugin template context?
def fill_template( self, trans, template_filename, **kwargs ):
"""
Pass control over to trans and render the ``template_filename``.
"""
# defined here to be overridden
return trans.fill_template( template_filename, template_lookup=self.template_lookup, **kwargs )
def fill_template_with_plugin_imports( self, trans, template_filename, **kwargs ):
"""
Returns a rendered plugin template but allows importing modules from inside
the plugin directory within the template.
..example:: I.e. given this layout for a plugin:
bler/
template/
bler.mako
static/
conifg/
my_script.py
this version of `fill_template` allows `bler.mako` to call `import my_script`.
"""
try:
plugin_base_path = os.path.split( os.path.dirname( template_filename ) )[0]
plugin_path = os.path.join( self.plugin_directory, plugin_base_path )
sys.path.append( plugin_path )
filled_template = self.fill_template( trans, template_filename, **kwargs )
finally:
sys.path.remove( plugin_path )
return filled_template
#TODO: could add plugin template helpers here
+6 -3
View File
@@ -993,10 +993,13 @@ class GalaxyWebTransaction( base.DefaultWebTransaction ):
searchList=[kwargs, self.template_context, dict(caller=self, t=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app)] )
return str( template )
def fill_template_mako( self, filename, **kwargs ):
template = self.webapp.mako_template_lookup.get_template( filename )
def fill_template_mako( self, filename, template_lookup=None, **kwargs ):
template_lookup = template_lookup or self.webapp.mako_template_lookup
template = template_lookup.get_template( filename )
template.output_encoding = 'utf-8'
data = dict( caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app )
data = dict( caller=self, t=self, trans=self, h=helpers, util=util,
request=self.request, response=self.response, app=self.app )
data.update( self.template_context )
data.update( kwargs )
return template.render( **data )
@@ -12,9 +12,11 @@ from paste.httpheaders import ETAG
from paste.urlparser import StaticURLParser
class CacheableStaticURLParser( StaticURLParser ):
def __init__( self, directory, cache_seconds=None ):
StaticURLParser.__init__( self, directory )
self.cache_seconds = cache_seconds
def __call__( self, environ, start_response ):
path_info = environ.get('PATH_INFO', '')
if not path_info:
+24 -10
View File
@@ -2,22 +2,26 @@
Provides factory methods to assemble the Galaxy web application
"""
import logging, atexit
import os, os.path
import sys, warnings
from galaxy.util import asbool
import sys
import os
import os.path
import atexit
import warnings
import glob
from paste import httpexceptions
import pkg_resources
log = logging.getLogger( __name__ )
from galaxy import util
import galaxy.model
import galaxy.model.mapping
import galaxy.datatypes.registry
import galaxy.web.framework
from galaxy import util
from galaxy.util import asbool
import logging
log = logging.getLogger( __name__ )
class GalaxyWebApplication( galaxy.web.framework.WebApplication ):
pass
@@ -181,7 +185,7 @@ def app_factory( global_conf, **kwargs ):
if kwargs.get( 'middleware', True ):
webapp = wrap_in_middleware( webapp, global_conf, **kwargs )
if asbool( kwargs.get( 'static_enabled', True ) ):
webapp = wrap_in_static( webapp, global_conf, **kwargs )
webapp = wrap_in_static( webapp, global_conf, plugin_frameworks=app.config.plugin_frameworks, **kwargs )
if asbool(kwargs.get('pack_scripts', False)):
pack_scripts()
# Close any pooled database connections before forking
@@ -323,7 +327,7 @@ def wrap_in_middleware( app, global_conf, **local_conf ):
log.debug( "Enabling 'Request ID' middleware" )
return app
def wrap_in_static( app, global_conf, **local_conf ):
def wrap_in_static( app, global_conf, plugin_frameworks=None, **local_conf ):
from paste.urlmap import URLMap
from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static
urlmap = URLMap()
@@ -343,6 +347,16 @@ def wrap_in_static( app, global_conf, **local_conf ):
urlmap["/static/style"] = Static( conf.get( "static_style_dir" ), cache_time )
urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir" ), cache_time )
urlmap["/robots.txt"] = Static( conf.get( "static_robots_txt", 'static/robots.txt'), cache_time )
# wrap any static dirs for plugins
plugin_frameworks = plugin_frameworks or []
for static_serving_framework in ( framework for framework in plugin_frameworks if framework.serves_static ):
# invert control to each plugin for finding their own static dirs
for plugin_url, plugin_static_path in static_serving_framework.get_static_urls_and_paths():
plugin_url = '/plugins/' + plugin_url
urlmap[( plugin_url )] = Static( plugin_static_path, cache_time )
log.debug( 'added url, path to static middleware: %s, %s', plugin_url, plugin_static_path )
# URL mapper becomes the root webapp
return urlmap
@@ -708,7 +708,7 @@ class VisualizationController( BaseUIController, SharableMixin, UsesAnnotations,
# validate name vs. registry
registry = trans.app.visualizations_registry
if not registry:
raise HTTPNotFound( 'No visualization registry (possibly disabled in universe_wsgi.ini)')
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?
@@ -722,16 +722,15 @@ class VisualizationController( BaseUIController, SharableMixin, UsesAnnotations,
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 )
template_path = registry_listing[ 'template' ]
returned = registry.fill_template( trans, template_path,
visualization_name=visualization_name, query_args=kwargs,
embedded=embedded, shared_vars={}, **resources )
#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 ) )
+2 -2
View File
@@ -616,7 +616,7 @@ function create_scatterplot_action_fn( url, params ){
title : "Scatterplot",
type : "url",
content : url + '/scatterplot?' + $.param(params),
center : true
location : 'center'
});
//TODO: this needs to go away
@@ -699,4 +699,4 @@ function create_trackster_action_fn(vis_url, dataset_params, dbkey) {
//==============================================================================
//return {
// HDAView : HDAView,
//};});
//};});
+2 -4
View File
@@ -174,10 +174,8 @@ paste.app_factory = galaxy.web.buildapp:app_factory
# Galaxy.
#datatypes_config_file = datatypes_conf.xml
# Visualizations config directory, where to look for individual visualization
# xml configuration files. Those files define how visualizations apply to
# particular data and how to pass them the necessary parameters
#visualizations_config_directory = config/visualizations
# Visualizations config directory: where to look for individual visualization plugins.
#visualizations_plugins_directory = config/plugins/visualizations
# Each job is given a unique empty directory as its current working directory.
# This option defines in what parent directory those directories will be