Skip to content

Commit

Permalink
Merge pull request #18651 from guerler/parse_missing_viz_config
Browse files Browse the repository at this point in the history
Parse stored config details to script-based visualizations
  • Loading branch information
dannon authored Sep 13, 2024
2 parents da37dd3 + 4e3eebd commit bd54d46
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 121 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
# -*- coding: utf-8 -*-
<%inherit file="visualization_base.mako"/>

## No stylesheets
<%def name="stylesheets()"></%def>
## No javascript libraries
<%def name="late_javascripts()">
<% tag_attrs = ' '.join([ '{0}="{1}"'.format( key, attr ) for key, attr in script_attributes.items() ]) %>
<script type="text/javascript" ${tag_attrs}></script>
## Add stylesheet
<%def name="stylesheets()">
<% script_css = script_attributes.get("css") %>
%if script_css is not None:
<% script_css = script_css if h.is_url(script_css) else f"{static_url}{script_css}" %>
<link rel="stylesheet" href="${script_css}">
%endif
</%def>

## Create a container, attach data and import script file
<%def name="get_body()">
## Collect incoming data
<%
from markupsafe import escape
data_incoming = {
"root": h.url_for("/"),
"visualization_id": visualization_id,
"visualization_name": visualization_name,
"visualization_plugin": visualization_plugin,
"visualization_title": escape(title),
"visualization_config": config }
%>
## Create a container with default identifier `app`
<% container = script_attributes.get("container") or "app" %>
<div id="${container}" data-incoming='${h.dumps(data_incoming)}'></div>
## Add script tag
<% script_src = script_attributes.get("src") %>
<% script_src = script_src if h.is_url(script_src) else f"{static_url}{script_src}" %>
<% script_type = script_attributes.get("type") or "module" %>
<script type="${script_type}" src="${script_src}"></script>
</%def>
38 changes: 13 additions & 25 deletions config/plugins/visualizations/example/config/example.xml
Original file line number Diff line number Diff line change
@@ -1,45 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE visualization SYSTEM "../../visualization.dtd">
<visualization name="Example" embeddable="false">
<description>This is a developer example which demonstrates how to implement and configure a basic d3-based plugin for charts.</description>
<visualization name="Minimal Example" embeddable="false">
<description>Welcome to the Minimal JS-Based Example Plugin.</description>
<data_sources>
<data_source>
<model_class>HistoryDatasetAssociation</model_class>
<test type="isinstance" test_attr="datatype" result_type="datatype">tabular.Tabular</test>
<test type="isinstance" test_attr="datatype" result_type="datatype">tabular.CSV</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>
<entry_point entry_point_type="chart" src="script.js"/>
<entry_point entry_point_type="script" src="script.js" />
<settings>
<input>
<name>data_dialog</name>
<label>Some data selector</label>
<type>data_dialog</type>
<multiple>false</multiple>
<name>setting_input</name>
<help>setting help</help>
<type>setting_type</type>
</input>
</settings>
<groups>
<input>
<name>x</name>
<label>Bubble x-position</label>
<type>data_column</type>
<is_numeric>true</is_numeric>
</input>
<input>
<name>y</name>
<label>Bubbles y-position</label>
<type>data_column</type>
<is_numeric>true</is_numeric>
</input>
<input>
<name>z</name>
<label>Bubble size</label>
<type>data_column</type>
<is_numeric>true</is_numeric>
<name>group_input</name>
<help>group help</help>
<type>group_type</type>
</input>
</groups>
</visualization>
<specs>
<spec_name>spec_value</spec_name>
</specs>
</visualization>
24 changes: 0 additions & 24 deletions config/plugins/visualizations/example/package.json

This file was deleted.

54 changes: 0 additions & 54 deletions config/plugins/visualizations/example/src/script.js

This file was deleted.

33 changes: 33 additions & 0 deletions config/plugins/visualizations/example/static/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const { visualization_config, visualization_plugin, root } = JSON.parse(document.getElementById("app").dataset.incoming);

const div = Object.assign(document.createElement("div"), {
style: "border: 2px solid #25537b; border-radius: 1rem; padding: 1rem"
});

const img = Object.assign(document.createElement("img"), {
src: root + visualization_plugin.logo,
style: "height: 3rem"
});
div.appendChild(img);

Object.entries(visualization_plugin).forEach(([key, value]) => {
const row = document.createElement("div");
const spanKey = Object.assign(document.createElement("span"), {
innerText: `${key}: `,
style: "font-weight: bold"
});
const spanValue = Object.assign(document.createElement("span"), {
innerText: JSON.stringify(value)
});
row.appendChild(spanKey);
row.appendChild(spanValue);
div.appendChild(row);
});

const dataset = Object.assign(document.createElement("div"), {
innerText: `You have selected dataset: ${visualization_config.dataset_id}.`,
style: "font-weight: bold; padding-top: 1rem;"
});
div.appendChild(dataset);

document.body.appendChild(div);
13 changes: 11 additions & 2 deletions lib/galaxy/app_unittest_utils/galaxy_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,17 @@ def remove(self):


class MockTemplateHelpers:
def js(*js_files):
def css(*css_files):
pass

def css(*css_files):
def dumps(*kwargs):
return {}

def js(*js_files):
pass

def is_url(*kwargs):
return True

def url_for(*kwargs):
return "/"
2 changes: 1 addition & 1 deletion lib/galaxy/visualization/plugins/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ def parse(self, xml_tree):
# when no tests are given, default to isinstance( object, model_class )
returned["tests"] = self.parse_tests(xml_tree.findall("test"))

# to_params (optional, 0 or more) - tells the registry to set certain params based on the model_clas, tests
# to_params (optional, 0 or more) - tells the registry to set certain params based on the model_class, tests
returned["to_params"] = {}
if to_params := self.parse_to_params(xml_tree.findall("to_param")):
returned["to_params"] = to_params
Expand Down
2 changes: 1 addition & 1 deletion lib/galaxy/visualization/plugins/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def _build_render_vars(self, config: Dict[str, Any], trans=None, **kwargs) -> Di
render_vars.update(
visualization_name=self.name,
visualization_display_name=self.config["name"],
title=kwargs.get("title", None),
title=kwargs.get("title", "Unnamed Visualization"),
saved_visualization=None,
visualization_id=None,
visualization_plugin=self.to_dict(),
Expand Down
17 changes: 17 additions & 0 deletions lib/galaxy/web/framework/helpers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
GalaxyWebTransaction in galaxy/webapps/base/webapp.py
"""

import re
from datetime import (
datetime,
timedelta,
Expand Down Expand Up @@ -105,6 +106,22 @@ def is_true(val):
return val is True or val in ["True", "true", "T", "t"]


def is_url(val):
"""
Regular expression to match common URL protocols
>>> assert is_url(None) == False
>>> assert is_url("is_url") == False
>>> assert is_url("http://is_url") == True
>>> assert is_url("https://is_url") == True
"""
if val is not None:
url_pattern = re.compile(r"^(https?:\/\/|ftp:\/\/)")
return bool(url_pattern.match(val))
else:
return False


def to_js_bool(val):
"""
Prints javascript boolean for passed value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def test_build_render_vars_default(self):
render_vars = plugin._build_render_vars(config)
assert render_vars["visualization_name"] == plugin.name
assert render_vars["visualization_display_name"] == plugin.config["name"]
assert render_vars["title"] is None
assert render_vars["title"] == "Unnamed Visualization"
assert render_vars["saved_visualization"] is None
assert render_vars["visualization_id"] is None
assert render_vars["query"] == {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

glx_dir = galaxy_directory()
template_cache_dir = os.path.join(glx_dir, "database", "compiled_templates")
addtional_templates_dir = os.path.join(glx_dir, "config", "plugins", "visualizations", "common", "templates")
additional_templates_dir = os.path.join(glx_dir, "config", "plugins", "visualizations", "common", "templates")
vis_reg_path = "config/plugins/visualizations"

config1 = """\
Expand Down Expand Up @@ -144,7 +144,7 @@ def test_script_entry(self):
<model_class>HistoryDatasetAssociation</model_class>
</data_source>
</data_sources>
<entry_point entry_point_type="script" data-main="one" src="bler"></entry_point>
<entry_point entry_point_type="script" container="mycontainer" src="mysrc" css="mycss"></entry_point>
</visualization>
"""
)
Expand All @@ -167,11 +167,11 @@ def test_script_entry(self):
assert script_entry.serves_templates

trans = galaxy_mock.MockTrans()
script_entry._set_up_template_plugin(mock_app_dir.root_path, [addtional_templates_dir])
response = script_entry._render({}, trans=trans, embedded=True)
assert 'src="bler"' in response
assert 'type="text/javascript"' in response
assert 'data-main="one"' in response
script_entry._set_up_template_plugin(mock_app_dir.root_path, [additional_templates_dir])
response = script_entry.render(trans=trans, embedded=True)
assert '<script type="module" src="mysrc">' in response
assert '<link rel="stylesheet" href="mycss">' in response
assert "<div id=\"mycontainer\" data-incoming='{}'></div>" in response
mock_app_dir.remove()


Expand Down

0 comments on commit bd54d46

Please sign in to comment.