Merge branch 'visualization_header' into dev

This commit is contained in:
Dannon Baker
2018-03-27 13:44:23 -04:00
54 changed files with 565 additions and 426 deletions
+13 -20
View File
@@ -17,13 +17,13 @@ import Workflows from "mvc/workflow/workflow";
import HistoryImport from "components/HistoryImport.vue";
import HistoryView from "components/HistoryView.vue";
import HistoryList from "mvc/history/history-list";
import PluginList from "components/PluginList.vue";
import ToolFormComposite from "mvc/tool/tool-form-composite";
import QueryStringParsing from "utils/query-string-parsing";
import Utils from "utils/utils";
import Ui from "mvc/ui/ui-misc";
import DatasetError from "mvc/dataset/dataset-error";
import DatasetEditAttributes from "mvc/dataset/dataset-edit-attributes";
import Visualization from "mvc/visualization/visualization-view";
import Citations from "components/Citations.vue";
import DisplayStructure from "components/DisplayStructured.vue";
import Vue from "vue";
@@ -57,9 +57,9 @@ window.app = function app(options, bootstrapped) {
"(/)pages(/)create(/)": "show_pages_create",
"(/)pages(/)edit(/)": "show_pages_edit",
"(/)pages(/)(:action_id)": "show_pages",
"(/)visualizations(/)": "show_plugins",
"(/)visualizations(/)edit(/)": "show_visualizations_edit",
"(/)visualizations/show/(:visualization_id)": "show_visualizations_client",
"(/)visualizations/dataset_id=(:dataset_id)": "show_visualizations_selector",
"(/)visualizations/(:action_id)": "show_visualizations",
"(/)workflows/import_workflow": "show_import_workflow",
"(/)workflows/run(/)": "show_run",
@@ -101,14 +101,11 @@ window.app = function app(options, bootstrapped) {
var model = new UserPreferences.Model({
user_id: Galaxy.params.id
});
this.page.display(new FormWrapper.View(_.extend(
model.get(form_id),
{active_tab: "user"}
)));
this.page.display(new FormWrapper.View(_.extend(model.get(form_id), { active_tab: "user" })));
},
show_visualizations: function(action_id) {
var activeTab = action_id=="list_published"?"shared":"visualization";
var activeTab = action_id == "list_published" ? "shared" : "visualization";
this.page.display(
new GridShared.View({
action_id: action_id,
@@ -129,14 +126,6 @@ window.app = function app(options, bootstrapped) {
);
},
show_visualizations_selector: function(dataset_id) {
this.page.display(
new Visualization.View({
dataset_id: dataset_id
})
);
},
show_workflows_published: function() {
this.page.display(
new GridView({
@@ -215,7 +204,7 @@ window.app = function app(options, bootstrapped) {
},
show_pages: function(action_id) {
var activeTab = action_id=="list_published"?"shared":"user";
var activeTab = action_id == "list_published" ? "shared" : "user";
this.page.display(
new GridShared.View({
action_id: action_id,
@@ -246,6 +235,13 @@ window.app = function app(options, bootstrapped) {
);
},
show_plugins: function() {
var pluginListInstance = Vue.extend(PluginList);
var vm = document.createElement("div");
this.page.display(vm);
new pluginListInstance().$mount(vm);
},
show_workflows: function() {
this.page.display(new Workflows.View());
},
@@ -331,10 +327,7 @@ window.app = function app(options, bootstrapped) {
Utils.get({
url: `${Galaxy.root}api/workflows/${Utils.getQueryString("id")}/download?style=run`,
success: response => {
this.page.display(new ToolFormComposite.View(_.extend(
response,
{active_tab: "workflow"}
)));
this.page.display(new ToolFormComposite.View(_.extend(response, { active_tab: "workflow" })));
},
error: response => {
var error_msg = response.err_msg || "Error occurred while loading the resource.";
@@ -7,7 +7,7 @@
</div>
</div>
<div class="portlet-content">
<div v-if="errormessage" class="ui-message alert alert-danger">
<div v-if="errormessage" class="ui-message ui-show alert alert-danger">
{{ errormessage }}
</div>
<div class="portlet-body">
@@ -57,7 +57,7 @@ export default {
};
</script>
<style>
.ui-message {
.ui-show {
display: block;
}
</style>
@@ -0,0 +1,136 @@
<template>
<div class="ui-thumbnails">
<div v-if="error" class="ui-message ui-show alert alert-danger">
{{ error }}
</div>
<div v-else class="ui-thumbnails-grid">
<input class="search-query parent-width" name="query" placeholder="search visualizations" autocomplete="off" type="text" v-model="search">
<div v-for="plugin in plugins">
<table v-if="match(plugin)">
<tr class="ui-thumbnails-item" @click="select(plugin)">
<td>
<img v-if="plugin.logo" class="ui-thumbnails-image" :src="plugin.logo"/>
<div v-else class="ui-thumbnails-icon fa fa-eye"/>
</td>
<td>
<div class="ui-thumbnails-description-title ui-form-info">
{{ plugin.html }}
</div>
<div class="ui-thumbnails-description-text ui-form-info">
{{ plugin.description }}
</div>
</td>
</tr>
<tr v-if="!fixed">
<td/>
<td v-if="plugin.name == name">
<div v-if="hdas && hdas.length > 0">
<div class="ui-form-info ui-bold">Select a dataset to visualize:</div>
<div class="ui-select">
<select class="select" v-model="selected">
<option v-for="file in hdas" :value="file.id">{{ file.name }}</option>
</select>
<div class="icon-dropdown fa fa-caret-down"/>
</div>
<button type="button" class="ui-button-default ui-float-left btn btn-primary" @click="create(plugin)">
<i class="icon fa fa-check ui-margin-right"/>
<span class="title">Create Visualization</span>
</button>
</div>
<div v-else class="ui-message ui-show alert alert-danger">
There is no suitable dataset in your current history which can be visualized with this plugin.
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
plugins: [],
hdas: [],
search: "",
selected: null,
name: null,
error: null,
fixed: false
}
},
created() {
let url = `${Galaxy.root}api/plugins`;
let dataset_id = Galaxy.params.dataset_id;
if (dataset_id) {
this.fixed = true;
this.selected = dataset_id;
url += `?dataset_id=${dataset_id}`;
}
axios.get(url)
.then(response => {
this.plugins = response.data;
})
.catch(e => {
this.error = this._errorMessage(e);
})
},
methods: {
select: function(plugin) {
if (this.fixed) {
this.create(plugin);
} else {
let history_id = Galaxy.currHistoryPanel && Galaxy.currHistoryPanel.model.id;
if (history_id) {
axios.get(`${Galaxy.root}api/plugins/${plugin.name}?history_id=${history_id}`)
.then(response => {
this.name = plugin.name;
this.hdas = response.data && response.data.hdas;
if (this.hdas && this.hdas.length > 0) {
this.selected = this.hdas[0].id;
}
})
.catch(e => {
this.error = this._errorMessage(e);
})
} else {
this.error = "This option requires an accessible history.";
}
}
},
create: function(plugin) {
let href = `${plugin.href}?dataset_id=${this.selected}`;
if (plugin.target == "_top") {
window.location.href = href;
} else {
$("#galaxy_main").attr("src", href);
}
},
match: function(plugin) {
return !this.search ||
plugin.name.indexOf(this.search) != -1 ||
(plugin.description && plugin.description.indexOf(this.search) != -1);
},
_errorMessage: function(e) {
let message = e && e.response && e.response.data && e.response.data.err_msg;
return message || "Request failed for unkown reason.";
}
}
};
</script>
<style>
.ui-show {
display: block;
margin-top: 0px;
}
.ui-bold {
font-weight: bold;
margin-bottom: 5px;
}
.ui-float-left {
float: left;
margin-top:10px;
}
</style>
+39 -35
View File
@@ -42,6 +42,33 @@ var Collection = Backbone.Collection.extend({
url: "workflows/list"
});
//
// Visualization tab.
//
this.add({
id: "visualization",
title: _l("Visualize"),
url: "visualizations/list",
tooltip: _l("Visualize datasets"),
disabled: !Galaxy.user.id,
menu: [
{
title: _l("Create Visualization"),
url: "visualizations"
},
{
title: _l("Interactive Environments"),
url: "visualization/gie_list",
target: "galaxy_main"
},
{
title: _l("Saved Visualizations"),
url: "visualizations/list",
target: "_frame"
}
]
});
//
// 'Shared Items' or Libraries tab.
//
@@ -74,34 +101,6 @@ var Collection = Backbone.Collection.extend({
]
});
//
// Visualization tab.
//
this.add({
id: "visualization",
title: _l("Visualization"),
url: "visualizations/list",
tooltip: _l("Visualize datasets"),
disabled: !Galaxy.user.id,
menu: [
{
title: _l("New Track Browser"),
url: "visualization/trackster",
target: "_frame"
},
{
title: _l("Saved Visualizations"),
url: "visualizations/list",
target: "_frame"
},
{
title: _l("Interactive Environments"),
url: "visualization/gie_list",
target: "galaxy_main"
}
]
});
//
// Webhooks
//
@@ -272,20 +271,25 @@ var Collection = Backbone.Collection.extend({
target: "_top",
divider: true
},
{
title: _l("Saved Histories"),
url: "histories/list",
target: "_top"
},
{
title: _l("Saved Datasets"),
url: "datasets/list",
target: "_top"
},
{
title: _l("Saved Histories"),
url: "histories/list",
target: "_top"
},
{
title: _l("Saved Pages"),
url: "pages/list",
target: "_top"
},
{
title: _l("Saved Visualizations"),
url: "visualizations/list",
target: "_top"
}
]
};
@@ -340,13 +344,13 @@ var Tab = Backbone.View.extend({
.attr("title", this.model.get("tooltip"))
.tooltip("destroy");
this.model.get("tooltip") && this.$toggle.tooltip({ placement: "bottom" });
if(!this.model.get("menu")){
if (!this.model.get("menu")) {
this.$dropdown
.removeClass()
.addClass("dropdown")
.addClass(this.model.get("disabled") && "disabled")
.addClass(this.model.get("active") && "active");
};
}
if (this.model.get("menu") && this.model.get("show_menu")) {
this.$menu.show();
$("#dd-helper")
@@ -261,7 +261,7 @@ var DatasetListItemEdit = _super.extend(
}
if (visualizations.length >= 1) {
var url = Galaxy.root + "visualizations/dataset_id=" + this.model.get("id");
var url = Galaxy.root + "plugins?dataset_id=" + this.model.get("id");
return $("<a/>")
.addClass("visualization-link icon-btn")
.attr("href", url)
@@ -6,7 +6,7 @@ var View = Backbone.View.extend({
this.model = new Backbone.Model(options);
this.url = this.model.get("url");
this.redirect = this.model.get("redirect");
if (options && options.active_tab){
if (options && options.active_tab) {
this.active_tab = options.active_tab;
}
this.setElement("<div/>");
@@ -9,7 +9,7 @@ var View = Backbone.View.extend({
this.model = new Backbone.Model(options);
this.item = this.model.get("item");
this.title = this.model.get("plural");
if (options && options.active_tab){
if (options && options.active_tab) {
this.active_tab = options.active_tab;
}
$.ajax({
@@ -107,10 +107,9 @@ var View = Backbone.View.extend({
var self = this;
LoadingIndicator.markViewAsLoading(this);
if(options.action_id == "list_published"){
if (options.action_id == "list_published") {
this.active_tab = "shared";
}
else if (options.action_id = "list"){
} else if ((options.action_id = "list")) {
this.active_tab = "user";
}
this.model = new Backbone.Model();
@@ -15,7 +15,7 @@ var View = Backbone.View.extend({
this.modal = parent.Galaxy.modal || new Modal.View();
this.model = (options && options.model) || new Backbone.Model(options);
this.deferred = new Deferred();
if (options && options.active_tab){
if (options && options.active_tab) {
this.active_tab = options.active_tab;
}
this.setElement(
@@ -303,17 +303,17 @@ var View = Backbone.View.extend({
});
this._append(this.$steps, this.history_form.$el);
},
/** Render Workflow Options */
_renderResourceParameters: function() {
this.workflow_resource_parameters_form = null;
if(!_.isEmpty(this.model.get('workflow_resource_parameters'))){
if (!_.isEmpty(this.model.get("workflow_resource_parameters"))) {
this.workflow_resource_parameters_form = new Form({
cls : 'ui-portlet-narrow',
title : '<b>Workflow Resource Options</b>',
inputs : this.model.get('workflow_resource_parameters')
cls: "ui-portlet-narrow",
title: "<b>Workflow Resource Options</b>",
inputs: this.model.get("workflow_resource_parameters")
});
this._append( this.$steps, this.workflow_resource_parameters_form.$el );
this._append(this.$steps, this.workflow_resource_parameters_form.$el);
}
},
@@ -538,7 +538,9 @@ var View = Backbone.View.extend({
var job_def = {
new_history_name: history_form_data["new_history|name"] ? history_form_data["new_history|name"] : null,
history_id: !history_form_data["new_history|name"] ? this.model.get("history_id") : null,
resource_params: this.workflow_resource_parameters_form ? this.workflow_resource_parameters_form.data.create() : {},
resource_params: this.workflow_resource_parameters_form
? this.workflow_resource_parameters_form.data.create()
: {},
replacement_params: this.wp_form ? this.wp_form.data.create() : {},
parameters: {},
// Tool form will submit flat maps for each parameter
+11 -3
View File
@@ -14,7 +14,8 @@ var ButtonDefault = Backbone.View.extend({
wait_text: "Sending...",
wait_cls: "btn btn-info",
disabled: false,
percentage: -1
percentage: -1,
visible: true
}).set(options);
this.setElement(
$("<button/>")
@@ -36,6 +37,7 @@ var ButtonDefault = Backbone.View.extend({
.addClass(options.disabled && "disabled")
.attr("id", options.id)
.attr("disabled", options.disabled)
.css("display", options.visible ? "inline-block" : "none")
.off("click")
.on("click", () => {
$(".tooltip").hide();
@@ -60,12 +62,12 @@ var ButtonDefault = Backbone.View.extend({
/** Show button */
show: function() {
this.$el.show();
this.model.set("visible", true);
},
/** Hide button */
hide: function() {
this.$el.hide();
this.model.set("visible", false);
},
/** Disable button */
@@ -101,6 +103,7 @@ var ButtonLink = ButtonDefault.extend({
(options && options.model) ||
new Backbone.Model({
id: Utils.uid(),
visible: true,
title: "",
icon: "",
cls: ""
@@ -122,6 +125,7 @@ var ButtonLink = ButtonDefault.extend({
target: options.target || "_top",
disabled: options.disabled
})
.css("display", options.visible ? "inline-block" : "none")
.tooltip({ placement: "bottom" })
.off("click")
.on("click", () => {
@@ -141,6 +145,7 @@ var ButtonCheck = Backbone.View.extend({
title: "Select/Unselect all",
icons: ["fa-square-o", "fa-minus-square-o", "fa-check-square-o"],
value: 0,
visible: true,
onchange: function() {}
}).set(options);
this.setElement(
@@ -157,6 +162,7 @@ var ButtonCheck = Backbone.View.extend({
var options = this.model.attributes;
this.$el
.addClass("ui-button-check")
.css("display", options.visible ? "inline-block" : "none")
.off("click")
.on("click", () => {
self.model.set("value", (self.model.get("value") === 0 && 2) || 0);
@@ -196,6 +202,7 @@ var ButtonIcon = ButtonDefault.extend({
id: Utils.uid(),
title: "",
icon: "",
visible: true,
cls: "ui-button-icon",
disabled: false
}).set(options);
@@ -217,6 +224,7 @@ var ButtonIcon = ButtonDefault.extend({
.removeClass()
.addClass(options.cls)
.addClass(options.disabled && "disabled")
.css("display", options.visible ? "inline-block" : "none")
.attr("disabled", options.disabled)
.attr("id", options.id)
.off("click")
+1 -1
View File
@@ -47,7 +47,7 @@ export var Message = Backbone.View.extend({
fade: true
}).set(options);
this.listenTo(this.model, "change", this.render, this);
if (options && options.active_tab){
if (options && options.active_tab) {
this.active_tab = options.active_tab;
}
this.render();
@@ -8,9 +8,9 @@ export default Backbone.Model.extend({
date: null,
state: "",
state_info: "",
modified: false,
modified: true,
dataset_id: "",
dataset_id_job: ""
dataset_id_job: null
},
initialize: function(options, viz_options) {
@@ -89,15 +89,16 @@ export default Backbone.Model.extend({
}
console.debug("model::save() - Saving failed.");
});
console.debug("model::save() - Saved with configuration:");
console.debug(this.chart_last);
console.debug("model::save() - Saving with configuration.");
},
/** Load nested models/collections from packed dictionary */
load: function(chart_parsed) {
var d = chart_parsed || this.chart_dict;
console.debug("model::load() - Attempting to load with configuration:");
console.debug(d);
if (d) {
console.debug("model::load() - Attempting to load with configuration:");
console.debug(d);
}
this.reset();
if (d && d.attributes) {
this.set(d.attributes);
@@ -109,9 +110,11 @@ export default Backbone.Model.extend({
this.trigger("load");
console.debug("model::load() - Loading chart model " + d.attributes.type + ".");
return true;
} else {
this.set("modified", true);
this.trigger("load");
console.debug("model::load() - Visualization attributes unavailable.");
return false;
}
this.trigger("load");
console.debug("model::load() - Visualization attributes unavailable.");
return false;
}
});
@@ -125,6 +125,9 @@ var _fillFromCache = function(options) {
});
if (limit === 0) {
console.debug("tabular-datasets::_fillFromCache() - No data available.");
if (options.chart) {
options.chart.state("failed", "No data available.");
}
}
var results = [];
groups.each(function(group, group_index) {
@@ -28,7 +28,7 @@ var requestCharts = function(chart, module) {
});
columns_string = columns_string.substring(0, columns_string.length - 2);
return {
tool_id: "charts",
tool_id: "toolshed.g2.bx.psu.edu/repos/iuc/charts/charts/1.0.1",
inputs: {
input: {
id: chart.get("dataset_id"),
@@ -44,12 +44,19 @@ var requestCharts = function(chart, module) {
/** Submit job request to charts tool */
var request = function(chart, parameters, success, error) {
chart.state("wait", "Requesting job results...");
if (chart.get("modified")) {
cleanup(chart);
if (chart.get("modified") && chart.get("dataset_id_job")) {
Utils.request({
type: "PUT",
url: Galaxy.root + "api/histories/none/contents/" + chart.get("dataset_id_job"),
data: { deleted: true },
success: () => {
refreshHdas();
}
});
chart.set("dataset_id_job", null);
chart.set("modified", false);
}
var dataset_id_job = chart.get("dataset_id_job");
if (dataset_id_job !== "") {
if (chart.get("dataset_id_job")) {
wait(chart, success, error);
} else {
chart.state("wait", "Sending job request...");
@@ -95,22 +102,6 @@ var request = function(chart, parameters, success, error) {
}
};
/* Remove previous data when re-running jobs */
var cleanup = function(chart) {
var previous = chart.get("dataset_id_job");
if (previous !== "") {
Utils.request({
type: "PUT",
url: Galaxy.root + "api/histories/none/contents/" + previous,
data: { deleted: true },
success: function() {
refreshHdas();
}
});
chart.set("dataset_id_job", "");
}
};
/** Request job details */
var wait = function(chart, success, error) {
Utils.request({
@@ -66,6 +66,7 @@ var GroupView = Backbone.View.extend({
cls: "ui-portlet-plain",
onchange: function() {
self.group.set(self.form.data.create());
self.chart.set("modified", true);
self.chart.trigger("redraw");
}
});
@@ -7,6 +7,13 @@ export default Backbone.View.extend({
initialize: function(app) {
this.app = app;
this.model = new Backbone.Model({ visible: true });
this.execute_button = new Ui.ButtonIcon({
icon: "fa-check-square",
tooltip: "Confirm",
onclick: () => {
app.chart.trigger("redraw", true);
}
});
this.export_button = new Ui.ButtonMenu({
icon: "fa-camera",
tooltip: "Export"
@@ -72,53 +79,50 @@ export default Backbone.View.extend({
});
}
});
this.buttons = [
new Ui.ButtonIcon({
icon: "fa-angle-double-left",
tooltip: "Show",
cls: "ui-button-icon charts-fullscreen-button",
onclick: () => {
this.model.set("visible", true);
window.dispatchEvent(new Event("resize"));
this.left_button = new Ui.ButtonIcon({
icon: "fa-angle-double-left",
tooltip: "Show",
onclick: () => {
this.model.set("visible", true);
window.dispatchEvent(new Event("resize"));
}
});
this.right_button = new Ui.ButtonIcon({
icon: "fa-angle-double-right",
tooltip: "Hide",
onclick: () => {
this.model.set("visible", false);
window.dispatchEvent(new Event("resize"));
}
});
this.save_button = new Ui.ButtonIcon({
icon: "fa-save",
tooltip: "Save",
onclick: () => {
if (app.chart.get("title")) {
app.message.update({
message: `Saving '${app.chart.get(
"title"
)}'. It will appear in the list of 'Saved Visualizations'.`,
status: "success"
});
app.chart.save({
error: () => {
app.message.update({
message: "Could not save visualization.",
status: "danger"
});
}
});
} else {
app.message.update({
message: "Please provide a name.",
status: "danger"
});
}
}),
new Ui.ButtonIcon({
icon: "fa-angle-double-right",
tooltip: "Hide",
onclick: () => {
this.model.set("visible", false);
window.dispatchEvent(new Event("resize"));
}
}),
this.export_button,
new Ui.ButtonIcon({
icon: "fa-save",
tooltip: "Save",
onclick: () => {
if (app.chart.get("title")) {
app.message.update({
message: `Saving '${app.chart.get(
"title"
)}'. It will appear in the list of 'Saved Visualizations'.`,
status: "success"
});
app.chart.save({
error: () => {
app.message.update({
message: "Could not save visualization.",
status: "danger"
});
}
});
} else {
app.message.update({
message: "Please provide a name.",
status: "danger"
});
}
}
})
];
}
});
this.buttons = [this.left_button, this.right_button, this.execute_button, this.export_button, this.save_button];
this.setElement("<div/>");
for (let b of this.buttons) {
this.$el.append(b.$el);
@@ -130,7 +134,11 @@ export default Backbone.View.extend({
render: function() {
var visible = this.model.get("visible");
this.app.$el[visible ? "removeClass" : "addClass"]("charts-fullscreen");
this.execute_button.model.set("visible", visible && !!this.app.chart.plugin.specs.confirm);
this.save_button.model.set("visible", visible);
this.export_button.model.set("visible", visible);
this.right_button.model.set("visible", visible);
this.left_button.model.set("visible", !visible);
var exports = this.app.chart.plugin.specs.exports || [];
this.export_button.collection.each(model => {
model.set("visible", exports.indexOf(model.get("key")) !== -1);
@@ -25,11 +25,15 @@ export default Backbone.View.extend({
this.$text = this.$(".text");
this._fullscreen(this.$el, 20);
this._createContainer("div");
this.chart.on("redraw", function() {
self.app.deferred.execute(function(process) {
console.debug("viewer:redraw() - Redrawing...");
self._draw(process, self.chart);
});
this.chart.on("redraw", function(confirmed) {
if (!self.chart.get("modified") || !self.chart.plugin.specs.confirm || confirmed) {
self.app.deferred.execute(function(process) {
console.debug("viewer:redraw() - Redrawing...");
self._draw(process, self.chart);
});
} else {
self.chart.state("info", "Please confirm the settings before rendering the results.");
}
});
this.chart.on("set:state", function() {
var $container = self.$(".charts-viewer-container");
@@ -49,6 +53,10 @@ export default Backbone.View.extend({
$icon.addClass("icon fa fa-warning");
$container.hide();
break;
case "info":
$icon.addClass("icon fa fa-info");
$container.hide();
break;
default:
$icon.addClass("icon fa fa-spinner fa-spin");
$container.show();
@@ -1,67 +0,0 @@
/**
* Visualization selection grid
*/
import Thumbnails from "mvc/ui/ui-thumbnails";
var View = Backbone.View.extend({
initialize: function(options) {
var self = this;
this.setElement($("<div/>"));
this.model = new Backbone.Model();
this.active_tab = "visualization";
$.ajax({
url: `${Galaxy.root}api/datasets/${options.dataset_id}`
}).done(dataset => {
if (dataset.visualizations.length === 0) {
self.$el.append(
$("<div/>")
.addClass("errormessagelarge")
.append(
$("<p/>").text(
"Unfortunately we could not identify a suitable plugin. Feel free to contact us if you are aware of visualizations for this datatype."
)
)
);
} else {
self.model.set({ dataset: dataset });
self.render();
}
});
},
render: function() {
var self = this;
var dataset = this.model.get("dataset");
this.vis_index = {};
this.vis_array = [];
_.each(
dataset.visualizations.sort(function(a, b) {
return a.html < b.html ? -1 : 1;
}),
function(v, id) {
var dict = {
id: id,
name: v.name,
keywords: v.keywords || [],
title: v.html,
image_src: v.logo ? Galaxy.root + v.logo : null,
description: v.description || "No description available.",
regular: v.regular,
visualization: v
};
self.vis_index[dict.id] = dict;
self.vis_array.push(dict);
}
);
this.types = new Thumbnails.View({
title_default: "Suggested plugins",
title_list: "List of available plugins",
collection: this.vis_array,
onclick: function(id) {
var vis_type = self.vis_index[id];
$("#galaxy_main").attr("src", vis_type.visualization.href);
}
});
this.$el.empty().append(this.types.$el);
}
});
export default { View: View };
+1 -1
View File
@@ -1211,7 +1211,7 @@ var Circster = Backbone.View.extend({
icon_class: "cross-circle",
title: _l("Close"),
on_click: function() {
window.location = `${Galaxy.root}visualizations/list`;
window.top.location = `${Galaxy.root}visualizations/list`;
}
}
],
+1 -1
View File
@@ -874,7 +874,7 @@ var SweepsterVisualizationView = Backbone.View.extend({
icon_class: "cross-circle",
title: _l("Close"),
on_click: function() {
window.location = "${h.url_for( controller='visualization', action='list' )}";
window.top.location = `${Galaxy.root}visualizations/list`;
}
}
],
+10 -10
View File
@@ -95,7 +95,7 @@ class TracksterUI extends Backbone.Model {
this.view.has_changes = false;
// Needed to set URL when first saving a visualization.
window.history.pushState({}, "", vis_info.url + window.location.hash);
window.history.pushState({}, "", vis_info.url + window.top.location.hash);
})
.error(() => {
// show dialog
@@ -151,7 +151,7 @@ class TracksterUI extends Backbone.Model {
icon_class: "globe",
title: _l("Circster"),
on_click: () => {
window.location = `${this.baseURL}visualization/circster?id=${this.view.vis_id}`;
window.top.location = `${this.baseURL}visualization/circster?id=${this.view.vis_id}`;
}
},
{
@@ -354,17 +354,17 @@ class TracksterUI extends Backbone.Model {
},
"Leave without Saving": () => {
$(window).off("beforeunload");
window.location = `${Galaxy.root}visualization`;
window.top.location = `${Galaxy.root}visualization`;
},
Save: () => {
$.when(this.save_viz()).then(() => {
window.location = `${Galaxy.root}visualization`;
window.top.location = `${Galaxy.root}visualization`;
});
}
}
});
} else {
window.location = `${Galaxy.root}visualization`;
window.top.location = `${Galaxy.root}visualization`;
}
}
}
@@ -433,7 +433,7 @@ class TracksterUIView extends Backbone.View {
"Not available."}</b></li><li>or create a new session with this dataset as the only track</li></ul></p>`,
buttons: {
Cancel: () => {
window.location = `${Galaxy.root}visualizations/list`;
window.top.location = `${Galaxy.root}visualizations/list`;
},
"View in saved visualization": () => {
this.view_in_saved(dataset_params);
@@ -456,14 +456,14 @@ class TracksterUIView extends Backbone.View {
body: tracks_grid.$el,
buttons: {
Cancel: () => {
window.location = `${Galaxy.root}visualizations/list`;
window.top.location = `${Galaxy.root}visualizations/list`;
},
"Add to visualization": () => {
$(window.parent.document)
.find("input[name=id]:checked")
.each(() => {
dataset_params.id = $(this).val();
window.location = `${Galaxy.root}visualization/trackster?${$.param(dataset_params)}`;
window.top.location = `${Galaxy.root}visualization/trackster?${$.param(dataset_params)}`;
});
}
}
@@ -509,7 +509,7 @@ class TracksterUIView extends Backbone.View {
body: this.template_view_new(response),
buttons: {
Cancel: () => {
window.location = `${Galaxy.root}visualizations/list`;
window.top.location = `${Galaxy.root}visualizations/list`;
},
Create: () => {
this.create_browser($("#new-title").val(), $("#new-dbkey").val());
@@ -562,7 +562,7 @@ class TracksterUIView extends Backbone.View {
// close selection/finalize template
html += `</select></div><div style="clear: both;"></div></div><div class="form-row">Is the build not listed here? <a href="${
Galaxy.root
}custom_builds">Add a Custom Build</a></div></form>`;
}custom_builds" target="_top">Add a Custom Build</a></div></form>`;
// return
return html;
@@ -2493,7 +2493,7 @@ extend(Track.prototype, Drawable.prototype, {
Galaxy.modal.hide();
// Go to visualization.
window.location.href = `${Galaxy.root}visualization/sweepster?${$.param({
window.top.location.href = `${Galaxy.root}visualization/sweepster?${$.param({
dataset_id: track.dataset.id,
hda_ldda: track.dataset.get("hda_ldda"),
regions: JSON.stringify(new Backbone.Collection(regions).toJSON())
+3 -12
View File
@@ -6,8 +6,6 @@
.ui-button-icon {
display: block;
margin-right: 5px;
}
.ui-button-icon {
background: white;
color: #666;
border-color: white;
@@ -15,9 +13,9 @@
font-size: 1.2em;
line-height: 1.2em;
}
}
.ui-button-icon.charts-fullscreen-button {
display: none;
.fa-check-square {
color: @panel-success-text;
}
}
}
.charts-center {
@@ -111,13 +109,6 @@
.charts-buttons {
position: absolute;
right: 0px;
.ui-button-icon,
.ui-button-menu {
display: none;
}
.ui-button-icon.charts-fullscreen-button {
display: block;
}
}
.charts-center {
width: 100%;
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE visualization SYSTEM "../../visualization.dtd">
<visualization name="CSG Viewer">
<description>Constructive Solid Geometry (CSG) viewer.</description>
<data_sources>
<data_source>
<model_class>HistoryDatasetAssociation</model_class>
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE visualization SYSTEM "../../visualization.dtd">
<visualization name="Graph Visualization">
<description>Cytoscape-based graph visualization plugin.</description>
<data_sources>
<data_source>
<model_class>HistoryDatasetAssociation</model_class>
@@ -22,4 +23,4 @@
<param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
</params>
<template>graphviz.mako</template>
</visualization>
</visualization>
@@ -7,4 +7,15 @@
<description>Applies hierarchical clustering to a matrix using R. The data has to be provided in 3-column format. The result is displayed as clustered heatmap.</description>
<entry_point entry_point_type="chart" src="../heatmap_default/static/script.js" load="heatmap_cluster"/>
<expand macro="shared"/>
<specs>
<tag>svg</tag>
<use_panels>yes</use_panels>
<zoomable>true</zoomable>
<confirm>True</confirm>
<exports>
<exports>pdf</exports>
<exports>png</exports>
<exports>svg</exports>
</exports>
</specs>
</visualization>
@@ -7,4 +7,14 @@
<description>Renders a heatmap from matrix data provided in 3-column format (x, y, observation).</description>
<entry_point entry_point_type="chart" src="../heatmap_default/static/script.js" load="heatmap_default"/>
<expand macro="shared"/>
<specs>
<tag>svg</tag>
<use_panels>yes</use_panels>
<zoomable>true</zoomable>
<exports>
<exports>pdf</exports>
<exports>png</exports>
<exports>svg</exports>
</exports>
</specs>
</visualization>
@@ -11,16 +11,6 @@
<params>
<param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
</params>
<specs>
<tag>svg</tag>
<use_panels>yes</use_panels>
<zoomable>true</zoomable>
<exports>
<exports>pdf</exports>
<exports>png</exports>
<exports>svg</exports>
</exports>
</specs>
<groups>
<input>
<label>Provide a label</label>
@@ -9,6 +9,7 @@
<entry_point entry_point_type="chart" src="../jqplot_bar/static/script.js" load="jqplot_box"/>
<specs>
<expand macro="specs"/>
<confirm>True</confirm>
</specs>
<groups>
<expand macro="groups"/>
@@ -9,6 +9,7 @@
<entry_point entry_point_type="chart" src="../jqplot_bar/static/script.js" load="jqplot_histogram"/>
<specs>
<expand macro="specs"/>
<confirm>True</confirm>
</specs>
<groups>
<expand macro="groups"/>
File diff suppressed because one or more lines are too long
@@ -105,6 +105,7 @@ var PieWrapper = Backbone.View.extend({
var targets = options.targets;
var process = options.process;
Datasets.request({
chart: chart,
dataset_id: chart.get("dataset_id"),
dataset_groups: chart.groups,
success: function(groups) {
File diff suppressed because one or more lines are too long
@@ -10,6 +10,7 @@
<specs>
<expand macro="specs"/>
<use_panels>no</use_panels>
<confirm>True</confirm>
</specs>
<groups>
<expand macro="groups"/>
@@ -10,6 +10,7 @@
<specs>
<expand macro="specs"/>
<use_panels>no</use_panels>
<confirm>True</confirm>
</specs>
<groups>
<expand macro="groups"/>
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE visualization SYSTEM "../../visualization.dtd">
<visualization name="Phyloviz">
<description>Phylogenetic data analysis from multiple data sources.</description>
<data_sources>
<data_source>
<model_class>HistoryDatasetAssociation</model_class>
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

@@ -8,10 +8,59 @@ import "../../../../../client/galaxy/scripts/ui/peek-column-selector";
import "../../../../../client/galaxy/scripts/ui/pagination";
import "jquery-ui-bundle";
import "bootstrap";
import * as VisualizationModel from "../../../../../client/galaxy/scripts/mvc/visualization/visualization-model";
//TODO: Finish unlinking this from the Galaxy codebase (package it, use that way?)
var Visualization = Backbone.Model.extend({
/** default attributes for a model */
defaults: {
config: {}
},
/** override urlRoot to handle prefix */
urlRoot: function() {
var apiUrl = "api/visualizations";
return Galaxy.root + apiUrl;
},
/** Set up the model, determine if accessible, bind listeners
* @see Backbone.Model#initialize
*/
initialize: function(data) {
// munge config sub-object here since bbone won't handle defaults with this
if (_.isObject(data.config) && _.isObject(this.defaults.config)) {
_.defaults(data.config, this.defaults.config);
}
this._setUpListeners();
},
/** set up any event listeners */
_setUpListeners: function() {},
/** override set to properly allow update and trigger change when setting the sub-obj 'config' */
set: function(key, val) {
if (key === "config") {
var oldConfig = this.get("config");
if (_.isObject(oldConfig)) {
val = _.extend(_.clone(oldConfig), val);
}
}
Backbone.Model.prototype.set.call(this, key, val);
return this;
},
/** String representation */
toString: function() {
var idAndTitle = this.get("id") || "";
if (this.get("title")) {
idAndTitle += `:${this.get("title")}`;
}
return `Visualization(${idAndTitle})`;
}
}
);
/**
* Two Variable scatterplot visualization using d3
* Uses semi transparent circles to show density of data in x, y grid
@@ -382,7 +431,7 @@ var ScatterplotConfigEditor = Backbone.View.extend({
/** initialize requires a configuration Object containing a dataset Object */
initialize: function(attributes) {
if (!this.model) {
this.model = new VisualizationModel.Visualization({ type: "scatterplot" });
this.model = new Visualization({ type: "scatterplot" });
}
//this.log( this + '.initialize, attributes:', attributes );
@@ -986,7 +1035,7 @@ var ScatterplotDisplay = Backbone.View.extend({
return "ScatterplotView()";
}
});
var ScatterplotModel = VisualizationModel.Visualization.extend({
var ScatterplotModel = Visualization.extend({
defaults: {
type: "scatterplot",
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE visualization SYSTEM "../../visualization.dtd">
<visualization name="Trackster">
<!--not tested yet -->
<visualization name=" Trackster">
<description>Fast, interactive visualization for large, NGS/HTS datasets using only a web browser.</description>
<data_sources>
<data_source>
<model_class>HistoryDatasetAssociation</model_class>
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE visualization SYSTEM "../../visualization.dtd">
<visualization name="Unipept Taxonomy Viewer">
<description>Interactive Unipept Tree viewer.</description>
<data_sources>
<data_source>
<model_class>HistoryDatasetAssociation</model_class>
@@ -18,6 +18,7 @@ _.extend(window.bundleEntries || {}, {
});
};
Datasets.request({
chart : options.chart,
dataset_id : options.chart.get( 'dataset_id' ),
dataset_groups : options.chart.groups,
success : function( result ) {
File diff suppressed because one or more lines are too long
@@ -64,9 +64,6 @@ class VisualizationsConfigParser(object):
if not returned['name']:
raise ParsingException('Visualization needs a name attribute.')
# a regular visualization for a certain datatype is highlighted in the ui
returned['regular'] = xml_tree.get('regular', False)
# allow manually turning off a vis by checking for a disabled property
if 'disabled' in xml_tree.attrib:
log.info('Visualizations plugin disabled: %s. Skipping...', returned['name'])
+11 -7
View File
@@ -14,6 +14,7 @@ from galaxy.visualization.plugins import (
resource_parser,
utils
)
from galaxy.web import url_for
log = logging.getLogger(__name__)
@@ -96,10 +97,6 @@ class VisualizationPlugin(ServesStaticPluginMixin, ServesTemplatesPluginMixin):
A plugin that instantiates resources, serves static files, and uses mako
templates to render web pages.
"""
# AKA: MakoVisualizationPlugin
# config[ 'entry_point' ][ 'type' ] == 'mako'
# TODO: concept/name collision between plugin config and visualization config
def __init__(self, app, path, name, config, context=None, **kwargs):
context = context or {}
self.app = app
@@ -146,7 +143,6 @@ class VisualizationPlugin(ServesStaticPluginMixin, ServesTemplatesPluginMixin):
'name' : self.name,
'html' : self.config.get('name'),
'description' : self.config.get('description'),
'regular' : self.config.get('regular'),
'logo' : self.config.get('logo'),
'title' : self.config.get('title'),
'target' : self.config.get('render_target', 'galaxy_main'),
@@ -155,9 +151,15 @@ class VisualizationPlugin(ServesStaticPluginMixin, ServesTemplatesPluginMixin):
'settings' : self.config.get('settings'),
'groups' : self.config.get('groups'),
'specs' : self.config.get('specs'),
'static_url' : None if not self.serves_static else '/'.join(['plugins', self.static_url]) # Todo: refactor so this isn't generated here
'static_url' : None if not self.serves_static else '/'.join(['plugins', self.static_url]),
'href' : self._get_url()
}
def _get_url(self):
if self.name in self.app.visualizations_registry.BUILT_IN_VISUALIZATIONS:
return url_for(controller='visualization', action=self.name)
return url_for('visualization_plugin', visualization_name=self.name)
def _get_saved_visualization_config(self, visualization, revision=None, **kwargs):
"""
Return the config of a saved visualization and revision.
@@ -198,7 +200,6 @@ class VisualizationPlugin(ServesStaticPluginMixin, ServesTemplatesPluginMixin):
# further parse config to resources (models, etc.) used in template based on registry config
resources = self._config_to_resources(trans, config)
render_vars.update(resources)
return render_vars
def _build_config(self, config, trans=None, **kwargs):
@@ -312,6 +313,9 @@ class InteractiveEnvironmentPlugin(VisualizationPlugin):
log.exception("IE plugin template fill failed")
return self._error_template(trans)
def _get_url(self):
return url_for('interactive_environment_plugin', visualization_name=self.name)
class ScriptVisualizationPlugin(VisualizationPlugin):
"""
+17 -85
View File
@@ -16,10 +16,8 @@ from galaxy.util import (
)
from galaxy.visualization.plugins import (
config_parser,
plugin as vis_plugins,
utils as vis_utils
plugin as vis_plugins
)
from galaxy.web import url_for
log = logging.getLogger(__name__)
@@ -38,9 +36,7 @@ class VisualizationsRegistry(object):
BASE_URL = 'visualizations'
#: name of files to search for additional template lookup directories
TEMPLATE_PATHS_CONFIG = 'additional_template_paths.xml'
# 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
#: any built in visualizations that have their own render method in ctrls/visualization
#: built-in visualizations
BUILT_IN_VISUALIZATIONS = [
'trackster',
'circster',
@@ -117,20 +113,15 @@ class VisualizationsRegistry(object):
for plugin_path in self._find_plugins():
try:
plugin = self._load_plugin(plugin_path)
if plugin and plugin.name not in self.plugins:
self.plugins[plugin.name] = plugin
log.info('%s, loaded plugin: %s', self, plugin.name)
# NOTE: prevent silent, implicit overwrite here (two plugins in two diff directories)
# TODO: overwriting may be desired
elif plugin and plugin.name in self.plugins:
log.warning('%s, plugin with name already exists: %s. Skipping...', self, plugin.name)
except Exception:
if not self.skip_bad_plugins:
raise
log.exception('Plugin loading raised exception: %s. Skipping...', plugin_path)
return self.plugins
def _find_plugins(self):
@@ -230,19 +221,24 @@ class VisualizationsRegistry(object):
raise ObjectNotFound('Unknown or invalid visualization: ' + key)
return self.plugins[key]
def get_plugins(self):
result = []
for plugin in self.plugins.itervalues():
result.append(plugin.to_dict())
return sorted(result, key=lambda k: k.get('html'))
# -- 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?
applicable_visualizations = []
for vis_name in self.plugins:
url_data = self.get_visualization(trans, vis_name, target_object)
if url_data:
applicable_visualizations.append(url_data)
return applicable_visualizations
return sorted(applicable_visualizations, key=lambda k: k.get('html'))
def get_visualization(self, trans, visualization_name, target_object):
"""
@@ -250,29 +246,15 @@ class VisualizationsRegistry(object):
`visualization_name` if it's applicable to `target_object` or
`None` if it's not.
"""
# log.debug( 'VisReg.get_visualization: %s, %s', visualization_name, target_object )
visualization = self.plugins.get(visualization_name, None)
if not visualization:
return None
data_sources = visualization.config['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
# TODO: not true: must have test currently
tests = data_source['tests']
if tests and not self.is_object_applicable(trans, target_object, tests):
continue
# remap some of these vars for direct use in ui.js, PopupMenu (e.g. text->html)
param_data = data_source['to_params']
response = visualization.to_dict()
response['href'] = self.get_visualization_url(trans, target_object, visualization, param_data)
return response
return None
if visualization is not None:
data_sources = visualization.config['data_sources']
for data_source in data_sources:
model_class = data_source['model_class']
if isinstance(target_object, model_class):
tests = data_source['tests']
if tests is None or self.is_object_applicable(trans, target_object, tests):
return visualization.to_dict()
def is_object_applicable(self, trans, target_object, data_source_tests):
"""
@@ -308,53 +290,3 @@ class VisualizationsRegistry(object):
return True
return False
def get_visualization_url(self, trans, target_object, visualization, param_data):
"""
Generates a url for the visualization with `visualization`
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)
# TODO: needs to be split off as it's own registry
elif isinstance(visualization, vis_plugins.InteractiveEnvironmentPlugin):
url = url_for('interactive_environment_plugin', visualization_name=visualization.name, **params)
else:
url = url_for('visualization_plugin', 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 vis_utils.hasattr_recursive(target_object, target_attr):
params[to_param_name] = vis_utils.getattr_recursive(target_object, target_attr)
if assign:
params[to_param_name] = assign
# NOTE!: don't expose raw ids: encode id, _id
# TODO: double encodes if from config
if params:
params = trans.security.encode_dict_ids(params)
return params
+59
View File
@@ -0,0 +1,59 @@
"""
Plugins resource control over the API.
"""
import logging
from galaxy import exceptions
from galaxy.managers import hdas, histories
from galaxy.web import _future_expose_api as expose_api
from galaxy.web.base.controller import BaseAPIController
log = logging.getLogger(__name__)
class PluginsController(BaseAPIController):
"""
RESTful controller for interactions with plugins.
"""
def __init__(self, app):
super(PluginsController, self).__init__(app)
self.hda_manager = hdas.HDAManager(app)
self.history_manager = histories.HistoryManager(app)
@expose_api
def index(self, trans, **kwargs):
"""
GET /api/plugins:
"""
registry = self._get_registry(trans)
dataset_id = kwargs.get("dataset_id")
if dataset_id is not None:
hda = self.hda_manager.get_accessible(self.decode_id(dataset_id), trans.user)
return registry.get_visualizations(trans, hda)
else:
return registry.get_plugins()
@expose_api
def show(self, trans, id, **kwargs):
"""
GET /api/plugins/{id}:
"""
registry = self._get_registry(trans)
result = {}
history_id = kwargs.get("history_id")
if history_id is not None:
history = self.history_manager.get_owned(trans.security.decode_id(history_id), trans.user, current_history=trans.history)
result["hdas"] = []
for hda in history.datasets:
if registry.get_visualization(trans, id, hda):
result["hdas"].append({
"id": trans.security.encode_id(hda.id),
"name": hda.name
})
return result
def _get_registry(self, trans):
if not trans.app.visualizations_registry:
raise exceptions.MessageException("The visualization registry has not been configured.")
return trans.app.visualizations_registry
+2 -1
View File
@@ -108,7 +108,7 @@ def app_factory(global_conf, load_app_kwds={}, **kwargs):
webapp.add_client_route('/user')
webapp.add_client_route('/user/{form_id}')
webapp.add_client_route('/openids/list')
webapp.add_client_route('/visualizations/dataset_id={dataset_id}')
webapp.add_client_route('/visualizations')
webapp.add_client_route('/visualizations/edit')
webapp.add_client_route('/visualizations/list_published')
webapp.add_client_route('/visualizations/list')
@@ -306,6 +306,7 @@ def populate_api_routes(webapp, app):
webapp.mapper.connect('/api/genomes/{id}/indexes', controller='genomes', action='indexes')
webapp.mapper.connect('/api/genomes/{id}/sequences', controller='genomes', action='sequences')
webapp.mapper.resource('visualization', 'visualizations', path_prefix='/api')
webapp.mapper.resource('plugins', 'plugins', path_prefix='/api')
webapp.mapper.connect('/api/workflows/build_module', action='build_module', controller="workflows")
webapp.mapper.connect('/api/workflows/menu', action='get_workflow_menu', controller="workflows", conditions=dict(method=["GET"]))
webapp.mapper.connect('/api/workflows/menu', action='set_workflow_menu', controller="workflows", conditions=dict(method=["PUT"]))
File diff suppressed because one or more lines are too long
+1
View File
@@ -188,6 +188,7 @@ class MockTrans(object):
class MockVisualizationsRegistry(object):
BUILT_IN_VISUALIZATIONS = ['trackster']
def get_visualizations(self, trans, target):
return []
@@ -64,7 +64,7 @@ class VisualizationsRegistry_TestCase(unittest.TestCase):
self.assertEqual(trackster.name, 'trackster')
self.assertEqual(trackster.path, os.path.join(expected_plugins_path, 'trackster'))
self.assertEqual(trackster.base_url, '/'.join([plugin_mgr.base_url, trackster.name]))
self.assertFalse(trackster.serves_static)
self.assertTrue(trackster.serves_static)
self.assertFalse(trackster.serves_templates)
def test_plugin_load(self):