Merge branch 'dev' into visualization_header

This commit is contained in:
guerler
2018-03-12 17:43:58 -04:00
31 changed files with 2379 additions and 993 deletions
+12 -9
View File
@@ -1,6 +1,10 @@
import * as _ from "libs/underscore";
import * as Backbone from "libs/backbone";
import util_mod from "viz/trackster/util";
import config_mod from "utils/config";
/* global $ */
/* global Galaxy */
/**
* A configuration setting. Currently key is used as id.
*/
@@ -307,23 +311,22 @@ var ConfigSettingCollectionView = Backbone.View.extend({
// No propagation to avoid triggering document click (and tip hiding) above.
e.stopPropagation();
});
var // Icon for setting a new random color; behavior set below.
new_color_icon = $("<a href='javascript:void(0)'/>")
// Icon for setting a new random color; behavior set below.
var new_color_icon = $("<a href='javascript:void(0)'/>")
.addClass("icon-button arrow-circle")
.appendTo(container_div)
.attr("title", "Set new random color")
.tooltip();
var // Color picker in tool tip style.
tip = $("<div class='tooltip right' style='position: absolute;' />")
// Color picker in tool tip style.
var tip = $("<div class='tooltip right' style='position: absolute;' />")
.appendTo(container_div)
.hide();
var // Inner div for padding purposes
tip_inner = $("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(tip);
// Inner div for padding purposes
var tip_inner = $("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(tip);
var tip_arrow = $("<div class='tooltip-arrow'></div>").appendTo(tip);
$("<div class='tooltip-arrow'></div>").appendTo(tip);
var farb_obj = $.farbtastic(tip_inner, {
width: 100,
+170 -183
View File
@@ -1,15 +1,15 @@
import _l from "utils/localization";
/**
* Top-level trackster code, used for creating/loading visualizations and user interface elements.
*/
//import * as $ from 'jquery';
import _l from "utils/localization";
import * as _ from "libs/underscore";
import * as Backbone from "libs/backbone";
import tracks from "viz/trackster/tracks";
import visualization from "viz/visualization";
import mod_icon_btn from "mvc/ui/icon-button";
import IconButton from "mvc/ui/icon-button";
import query_string from "utils/query-string-parsing";
import GridView from "mvc/grid/grid-view";
import mod_utils from "utils/utils";
import Utils from "utils/utils";
import "libs/jquery/jquery.event.drag";
import "libs/jquery/jquery.event.hover";
import "libs/jquery/jquery.mousewheel";
@@ -20,44 +20,36 @@ import "libs/jquery/jquery.form";
import "libs/jquery/jquery.rating";
import "ui/editable-text";
// trackster global variables
var ui = null;
var view = null;
var browser_router = null;
/**
* Base Object/Model for inhertiance.
*/
var Base = function() {
if (this.initialize) {
this.initialize.apply(this, arguments);
}
};
Base.extend = Backbone.Model.extend;
/* global Galaxy */
/* global $ */
/**
* User interface controls for trackster
*/
var TracksterUI = Base.extend({
initialize: function(baseURL) {
mod_utils.cssLoadFile("static/style/jquery.rating.css");
mod_utils.cssLoadFile("static/style/autocomplete_tagging.css");
mod_utils.cssLoadFile("static/style/jquery-ui/smoothness/jquery-ui.css");
mod_utils.cssLoadFile("static/style/library.css");
mod_utils.cssLoadFile("static/style/trackster.css");
class TracksterUI extends Backbone.Model {
constructor(options) {
super(options);
}
initialize(baseURL) {
this.baseURL = baseURL;
},
Utils.cssLoadFile("static/style/jquery.rating.css");
Utils.cssLoadFile("static/style/autocomplete_tagging.css");
Utils.cssLoadFile("static/style/jquery-ui/smoothness/jquery-ui.css");
Utils.cssLoadFile("static/style/library.css");
Utils.cssLoadFile("static/style/trackster.css");
}
/**
* Save visualization, returning a Deferred object for the remote call to save.
*/
save_viz: function() {
save_viz() {
// show dialog
Galaxy.modal.show({ title: "Saving...", body: "progress" });
// Save bookmarks.
var bookmarks = [];
$(".bookmark").each(function() {
$(".bookmark").each(() => {
bookmarks.push({
position: $(this)
.children(".position")
@@ -69,14 +61,16 @@ var TracksterUI = Base.extend({
});
// FIXME: give unique IDs to Drawables and save overview as ID.
var overview_track_name = view.overview_drawable ? view.overview_drawable.config.get_value("name") : null;
var overview_track_name = this.view.overview_drawable
? this.view.overview_drawable.config.get_value("name")
: null;
var viz_config = {
view: view.to_dict(),
view: this.view.to_dict(),
viewport: {
chrom: view.chrom,
start: view.low,
end: view.high,
chrom: this.view.chrom,
start: this.view.low,
end: this.view.high,
overview: overview_track_name
},
bookmarks: bookmarks
@@ -88,17 +82,17 @@ var TracksterUI = Base.extend({
type: "POST",
dataType: "json",
data: {
id: view.vis_id,
title: view.config.get_value("name"),
dbkey: view.dbkey,
id: this.view.vis_id,
title: this.view.config.get_value("name"),
dbkey: this.view.dbkey,
type: "trackster",
vis_json: JSON.stringify(viz_config)
}
})
.success(vis_info => {
Galaxy.modal.hide();
view.vis_id = vis_info.vis_id;
view.has_changes = false;
this.view.vis_id = vis_info.vis_id;
this.view.has_changes = false;
// Needed to set URL when first saving a visualization.
window.history.pushState({}, "", vis_info.url + window.top.location.hash);
@@ -109,29 +103,27 @@ var TracksterUI = Base.extend({
title: _l("Could Not Save"),
body: "Could not save visualization. Please try again later.",
buttons: {
Cancel: function() {
Cancel: () => {
Galaxy.modal.hide();
}
}
});
});
},
}
/**
* Create button menu
*/
createButtonMenu: function() {
var self = this;
var menu = mod_icon_btn.create_icon_buttons_menu(
createButtonMenu() {
var menu = IconButton.create_icon_buttons_menu(
[
{
icon_class: "plus-button",
title: _l("Add tracks"),
on_click: function() {
visualization.select_datasets({ dbkey: view.dbkey }, new_tracks => {
on_click: () => {
visualization.select_datasets({ dbkey: this.view.dbkey }, new_tracks => {
_.each(new_tracks, track => {
view.add_drawable(tracks.object_from_template(track, view, view));
this.view.add_drawable(tracks.object_from_template(track, this.view, this.view));
});
});
}
@@ -139,9 +131,9 @@ var TracksterUI = Base.extend({
{
icon_class: "block--plus",
title: _l("Add group"),
on_click: function() {
view.add_drawable(
new tracks.DrawableGroup(view, view, {
on_click: () => {
this.view.add_drawable(
new tracks.DrawableGroup(this.view, this.view, {
name: "New Group"
})
);
@@ -150,7 +142,7 @@ var TracksterUI = Base.extend({
{
icon_class: "bookmarks",
title: _l("Bookmarks"),
on_click: function() {
on_click: () => {
// HACK -- use style to determine if panel is hidden and hide/show accordingly.
window.force_right_panel($("div#right").css("right") == "0px" ? "hide" : "show");
}
@@ -158,22 +150,22 @@ var TracksterUI = Base.extend({
{
icon_class: "globe",
title: _l("Circster"),
on_click: function() {
window.top.location = `${self.baseURL}visualization/circster?id=${view.vis_id}`;
on_click: () => {
window.top.location = `${this.baseURL}visualization/circster?id=${this.view.vis_id}`;
}
},
{
icon_class: "disk--arrow",
title: _l("Save"),
on_click: function() {
self.save_viz();
on_click: () => {
this.save_viz();
}
},
{
icon_class: "cross-circle",
title: _l("Close"),
on_click: function() {
self.handle_unsaved_changes(view);
on_click: () => {
this.handle_unsaved_changes(this.view);
}
}
],
@@ -184,12 +176,12 @@ var TracksterUI = Base.extend({
this.buttonMenu = menu;
return menu;
},
}
/**
* Add bookmark.
*/
add_bookmark: function(position, annotation, editable) {
add_bookmark(position, annotation, editable) {
// Create HTML.
var bookmarks_container = $("#right .unified-panel-body");
@@ -201,11 +193,12 @@ var TracksterUI = Base.extend({
.addClass("position")
.appendTo(new_bookmark);
var position_link = $("<a href=''/>")
//position_link
$("<a href=''/>")
.text(position)
.appendTo(position_div)
.click(() => {
view.go_to(position);
this.view.go_to(position);
return false;
});
@@ -222,11 +215,12 @@ var TracksterUI = Base.extend({
// Remove bookmark.
new_bookmark.slideUp("fast");
new_bookmark.remove();
view.has_changes = true;
this.view.has_changes = true;
return false;
});
var delete_icon = $("<a href=''/>")
// delete_icon
$("<a href=''/>")
.addClass("icon-button delete")
.appendTo(delete_icon_container);
@@ -239,56 +233,50 @@ var TracksterUI = Base.extend({
.addClass("annotation");
}
view.has_changes = true;
this.view.has_changes = true;
return new_bookmark;
},
}
/**
* Create a complete Trackster visualization. Returns view.
*/
create_visualization: function(view_config, viewport_config, drawables_config, bookmarks_config, editable) {
create_visualization(view_config, viewport_config, drawables_config, bookmarks_config, editable) {
// Create view.
var self = this;
this.view = new tracks.TracksterView(_.extend(view_config, { header: false }));
this.view.editor = true;
view = new tracks.TracksterView(_.extend(view_config, { header: false }));
view.editor = true;
$.when(view.load_chroms_deferred).then(chrom_info => {
$.when(this.view.load_chroms_deferred).then(chrom_info => {
var overview_drawable_name = null;
// Viewport config.
if (viewport_config) {
var chrom = viewport_config.chrom;
var start = viewport_config.start;
var end = viewport_config.end;
var overview_drawable_name = viewport_config.overview;
overview_drawable_name = viewport_config.overview;
if (chrom && start !== undefined && end) {
view.change_chrom(chrom, start, end);
this.view.change_chrom(chrom, start, end);
} else {
// No valid viewport, so use first chromosome.
view.change_chrom(chrom_info[0].chrom);
this.view.change_chrom(chrom_info[0].chrom);
}
} else {
// No viewport, so use first chromosome.
view.change_chrom(chrom_info[0].chrom);
this.view.change_chrom(chrom_info[0].chrom);
}
// Add drawables to view.
if (drawables_config) {
// FIXME: can from_dict() be used to create view and add drawables?
var drawable_config;
var drawable_type;
var drawable;
for (var i = 0; i < drawables_config.length; i++) {
view.add_drawable(tracks.object_from_template(drawables_config[i], view, view));
for (let i = 0; i < drawables_config.length; i++) {
this.view.add_drawable(tracks.object_from_template(drawables_config[i], this.view, this.view));
}
}
// Set overview.
var overview_drawable;
for (var i = 0; i < view.drawables.length; i++) {
if (view.drawables[i].config.get_value("name") === overview_drawable_name) {
view.set_overview(view.drawables[i]);
for (let i = 0; i < this.view.drawables.length; i++) {
if (this.view.drawables[i].config.get_value("name") === overview_drawable_name) {
this.view.set_overview(this.view.drawables[i]);
break;
}
}
@@ -296,34 +284,35 @@ var TracksterUI = Base.extend({
// Load bookmarks.
if (bookmarks_config) {
var bookmark;
for (var i = 0; i < bookmarks_config.length; i++) {
for (let i = 0; i < bookmarks_config.length; i++) {
bookmark = bookmarks_config[i];
self.add_bookmark(bookmark["position"], bookmark["annotation"], editable);
this.add_bookmark(bookmark.position, bookmark.annotation, editable);
}
}
// View has no changes as of yet.
view.has_changes = false;
this.view.has_changes = false;
});
// Final initialization.
this.set_up_router({ view: view });
this.set_up_router({ view: this.view });
return view;
},
// TODO: This is hopefully not necessary anymore, since we're using the instance view. Do it for compatibility for now.
return this.view;
}
/**
* Set up location router to use hashes as track browser locations.
*/
set_up_router: function(options) {
set_up_router(options) {
new visualization.TrackBrowserRouter(options);
Backbone.history.start();
},
}
/**
* Set up keyboard navigation for a visualization.
*/
init_keyboard_nav: function(view) {
init_keyboard_nav(view) {
// Keyboard navigation. Scroll ~7% of height when scrolling up/down.
$(document).keyup(e => {
// Do not navigate if arrow keys used in input element.
@@ -337,39 +326,38 @@ var TracksterUI = Base.extend({
view.move_fraction(0.25);
break;
case 38:
var change = Math.round(view.viewport_container.height() / 15.0);
// var change = Math.round(view.viewport_container.height() / 15.0);
view.viewport_container.scrollTop(view.viewport_container.scrollTop() - 20);
break;
case 39:
view.move_fraction(-0.25);
break;
case 40:
var change = Math.round(view.viewport_container.height() / 15.0);
// var change = Math.round(view.viewport_container.height() / 15.0);
view.viewport_container.scrollTop(view.viewport_container.scrollTop() + 20);
break;
}
});
},
}
/**
* Handle unsaved changes in visualization.
*/
handle_unsaved_changes: function(view) {
handle_unsaved_changes(view) {
if (view.has_changes) {
var self = this;
Galaxy.modal.show({
title: _l("Close visualization"),
body: "There are unsaved changes to your visualization which will be lost if you do not save them.",
buttons: {
Cancel: function() {
Cancel: () => {
Galaxy.modal.hide();
},
"Leave without Saving": function() {
"Leave without Saving": () => {
$(window).off("beforeunload");
window.top.location = `${Galaxy.root}visualization`;
},
Save: function() {
$.when(self.save_viz()).then(() => {
Save: () => {
$.when(this.save_viz()).then(() => {
window.top.location = `${Galaxy.root}visualization`;
});
}
@@ -379,22 +367,25 @@ var TracksterUI = Base.extend({
window.top.location = `${Galaxy.root}visualization`;
}
}
});
}
var TracksterView = Backbone.View.extend({
class TracksterUIView extends Backbone.View {
constructor(options) {
super(options);
}
// initalize trackster
initialize: function() {
initialize() {
// load ui
ui = new TracksterUI(Galaxy.root);
this.ui = new TracksterUI(Galaxy.root);
// create button menu
ui.createButtonMenu();
this.ui.createButtonMenu();
// attach the button menu to the panel header and float it left
ui.buttonMenu.$el.attr("style", "float: right");
this.ui.buttonMenu.$el.attr("style", "float: right");
// add to center panel
$("#center .unified-panel-header-inner").append(ui.buttonMenu.$el);
$("#center .unified-panel-header-inner").append(this.ui.buttonMenu.$el);
// configure right panel
$("#right .unified-panel-title").append("Bookmarks");
@@ -404,7 +395,7 @@ var TracksterView = Backbone.View.extend({
// resize view when showing/hiding right panel (bookmarks for now).
$("#right-border").click(() => {
view.resize_window();
this.ui.view.resize_window();
});
// hide right panel
@@ -418,10 +409,9 @@ var TracksterView = Backbone.View.extend({
} else {
this.view_new();
}
},
}
choose_existing_or_new: function() {
var self = this;
choose_existing_or_new() {
var dbkey = query_string.get("dbkey");
var listTracksParams = {};
@@ -442,21 +432,21 @@ var TracksterView = Backbone.View.extend({
body: `<p><ul style='list-style: disc inside none'>You can add this dataset as:<li>a new track to one of your existing, saved Trackster sessions if they share the genome build: <b>${dbkey ||
"Not available."}</b></li><li>or create a new session with this dataset as the only track</li></ul></p>`,
buttons: {
Cancel: function() {
Cancel: () => {
window.top.location = `${Galaxy.root}visualizations/list`;
},
"View in saved visualization": function() {
self.view_in_saved(dataset_params);
"View in saved visualization": () => {
this.view_in_saved(dataset_params);
},
"View in new visualization": function() {
self.view_new();
"View in new visualization": () => {
this.view_new();
}
}
});
},
}
// view
view_in_saved: function(dataset_params) {
view_in_saved(dataset_params) {
var tracks_grid = new GridView({
url_base: `${Galaxy.root}visualization/list_tracks`,
embedded: true
@@ -465,28 +455,28 @@ var TracksterView = Backbone.View.extend({
title: _l("Add Data to Saved Visualization"),
body: tracks_grid.$el,
buttons: {
Cancel: function() {
Cancel: () => {
window.top.location = `${Galaxy.root}visualizations/list`;
},
"Add to visualization": function() {
$(parent.document)
"Add to visualization": () => {
$(window.parent.document)
.find("input[name=id]:checked")
.each(function() {
.each(() => {
dataset_params.id = $(this).val();
window.top.location = `${Galaxy.root}visualization/trackster?${$.param(dataset_params)}`;
});
}
}
});
},
}
// view
view_existing: function() {
view_existing() {
// get config
var viz_config = window.galaxy_config.app.viz_config;
// view
view = ui.create_visualization(
this.ui.create_visualization(
{
container: $("#center .unified-panel-body"),
name: viz_config.title,
@@ -501,31 +491,28 @@ var TracksterView = Backbone.View.extend({
// initialize editor
this.init_editor();
},
}
// view
view_new: function() {
// reference this
var self = this;
view_new() {
// ajax
$.ajax({
url: `${Galaxy.root}api/genomes?chrom_info=True`,
data: {},
error: function() {
error: () => {
alert("Couldn't create new browser.");
},
success: function(response) {
success: response => {
// show dialog
Galaxy.modal.show({
title: _l("New Visualization"),
body: self.template_view_new(response),
body: this.template_view_new(response),
buttons: {
Cancel: function() {
Cancel: () => {
window.top.location = `${Galaxy.root}visualizations/list`;
},
Create: function() {
self.create_browser($("#new-title").val(), $("#new-dbkey").val());
Create: () => {
this.create_browser($("#new-title").val(), $("#new-dbkey").val());
Galaxy.modal.hide();
}
}
@@ -548,10 +535,10 @@ var TracksterView = Backbone.View.extend({
$("#overlay").css("overflow", "auto");
}
});
},
}
// new browser form
template_view_new: function(response) {
template_view_new(response) {
// start template
var html =
'<form id="new-browser-form" action="javascript:void(0);" method="post" onsubmit="return false;">' +
@@ -568,7 +555,7 @@ var TracksterView = Backbone.View.extend({
'<select name="dbkey" id="new-dbkey">';
// add dbkeys
for (var i = 0; i < response.length; i++) {
for (let i = 0; i < response.length; i++) {
html += `<option value="${response[i][1]}">${response[i][0]}</option>`;
}
@@ -579,13 +566,48 @@ var TracksterView = Backbone.View.extend({
// return
return html;
},
}
// initialization for editor-specific functions.
init_editor() {
// set title
$("#center .unified-panel-title").text(`${this.ui.view.config.get_value("name")} (${this.ui.view.dbkey})`);
// add dataset
if (window.galaxy_config.app.add_dataset)
$.ajax({
url: `${Galaxy.root}api/datasets/${window.galaxy_config.app.add_dataset}`,
data: { hda_ldda: "hda", data_type: "track_config" },
dataType: "json",
success: track_data => {
this.ui.view.add_drawable(tracks.object_from_template(track_data, this.ui.view, this.ui.view));
}
});
// initialize icons
$("#add-bookmark-button").click(() => {
// add new bookmark.
var position = `${this.ui.view.chrom}:${this.ui.view.low}-${this.ui.view.high}`;
var annotation = "Bookmark description";
return this.ui.add_bookmark(position, annotation, true);
});
// initialize keyboard
this.ui.init_keyboard_nav(this.ui.view);
$(window).on("beforeunload", () => {
if (this.ui.view.has_changes) {
return "There are unsaved changes to your visualization that will be lost if you leave this page.";
}
});
}
// create
create_browser: function(name, dbkey) {
create_browser(name, dbkey) {
$(document).trigger("convert_to_values");
view = ui.create_visualization(
this.ui.create_visualization(
{
container: $("#center .unified-panel-body"),
name: name,
@@ -598,46 +620,11 @@ var TracksterView = Backbone.View.extend({
this.init_editor();
// modify view setting
view.editor = true;
},
// initialization for editor-specific functions.
init_editor: function() {
// set title
$("#center .unified-panel-title").text(`${view.config.get_value("name")} (${view.dbkey})`);
// add dataset
if (window.galaxy_config.app.add_dataset)
$.ajax({
url: `${Galaxy.root}api/datasets/${window.galaxy_config.app.add_dataset}`,
data: { hda_ldda: "hda", data_type: "track_config" },
dataType: "json",
success: function(track_data) {
view.add_drawable(tracks.object_from_template(track_data, view, view));
}
});
// initialize icons
$("#add-bookmark-button").click(() => {
// add new bookmark.
var position = `${view.chrom}:${view.low}-${view.high}`;
var annotation = "Bookmark description";
return ui.add_bookmark(position, annotation, true);
});
// initialize keyboard
ui.init_keyboard_nav(view);
$(window).on("beforeunload", () => {
if (view.has_changes) {
return "There are unsaved changes to your visualization that will be lost if you leave this page.";
}
});
this.ui.view.editor = true;
}
});
}
export default {
TracksterUI: TracksterUI,
GalaxyApp: TracksterView
GalaxyApp: TracksterUIView
};
@@ -151,7 +151,7 @@ var NumberFilter = function(obj_dict) {
.addClass("elt-label")
.appendTo(filter.parent_div);
var name_span = $("<span/>")
$("<span/>")
.addClass("slider-name")
.text(`${filter.name} `)
.appendTo(filter_label);
@@ -341,9 +341,6 @@ extend(NumberFilter.prototype, {
return true;
}
// Keep value function.
var filter = this;
// Do filtering.
var to_filter = element[this.index];
if (to_filter instanceof Array) {
@@ -482,7 +479,7 @@ var FiltersManager = function(track, obj_dict) {
filter.height_icon.addClass("active").show();
}
} else {
console.log("ERROR: unsupported filter: ", name, type);
console.log("ERROR: unsupported filter: ", filters_dict[i]);
}
}
@@ -656,6 +653,7 @@ extend(FiltersManager.prototype, {
// Remove current filter.
filters = filters.slice(1);
// DBTODO: This will never work, run_tool_url doesn't exist?
$.getJSON(run_tool_url, url_params, response => {
if (response.error) {
// General error.
File diff suppressed because it is too large Load Diff
@@ -45,7 +45,7 @@ extend(FeatureSlotter.prototype, {
var draw_end = Math.ceil(feature[2] * this.w_scale);
var f_name = feature[3];
var text_align;
//var text_align;
// Update start, end drawing locations to include feature name.
// Try to put the name on the left, if not, put on right.
@@ -55,10 +55,10 @@ extend(FeatureSlotter.prototype, {
var text_len = this.measureText(f_name).width + (LABEL_SPACING + PACK_SPACING);
if (draw_start - text_len >= 0) {
draw_start -= text_len;
text_align = "left";
//text_align = "left";
} else {
draw_end += text_len;
text_align = "right";
//text_align = "right";
}
}
@@ -130,7 +130,7 @@ extend(FeatureSlotter.prototype, {
// Loop through features to (a) find those that are not yet slotted and (b) update
// those that are slotted if new information is availabe. For (a), features already
// slotted (based on slotting from other tiles) will retain their current slot.
for (var i = 0, len = features.length; i < len; i++) {
for (let i = 0, len = features.length; i < len; i++) {
feature = features[i];
feature_uid = feature[0];
var slotted_info = this.slots[feature_uid];
@@ -163,7 +163,7 @@ extend(FeatureSlotter.prototype, {
// Slot unslotted features.
// Do slotting.
for (var i = 0, len = undone.length; i < len; i++) {
for (let i = 0, len = undone.length; i < len; i++) {
feature = features[undone[i]];
feature_uid = feature[0];
var draw_coords = this._get_draw_coords(feature);
+167 -173
View File
@@ -1,5 +1,6 @@
import _l from "utils/localization";
import * as _ from "libs/underscore";
import * as Backbone from "libs/backbone";
import visualization from "viz/visualization";
import viz_views from "viz/viz_views";
import util from "viz/trackster/util";
@@ -13,6 +14,9 @@ import bbi from "viz/bbi-data-manager";
import "ui/editable-text";
var extend = _.extend;
/* global $ */
/* global Galaxy */
// ---- Web UI specific utilities ----
/**
@@ -24,9 +28,9 @@ var html_elt_js_obj_dict = {};
/**
* Designates an HTML as a container.
*/
var is_container = (element, obj) => {
function is_container(element, obj) {
html_elt_js_obj_dict[element.attr("id")] = obj;
};
}
/**
* Make `element` moveable within parent and sibling elements by dragging `handle` (a selector).
@@ -37,7 +41,7 @@ var is_container = (element, obj) => {
* @param container_selector selector used to identify possible containers for this element
* @param element_js_obj JavaScript object associated with element; used
*/
var moveable = (element, handle_class, container_selector, element_js_obj) => {
function moveable(element, handle_class, container_selector, element_js_obj) {
// HACK: set default value for container selector.
container_selector = ".group";
@@ -48,11 +52,9 @@ var moveable = (element, handle_class, container_selector, element_js_obj) => {
element
.bind("drag", { handle: `.${handle_class}`, relative: true }, function(e, d) {
var element = $(this);
var parent = $(this).parent();
var // Only sorting amongst tracks and groups.
children = parent.children(".track,.group");
var parent = element.parent();
// Only sorting amongst tracks and groups.
var children = parent.children(".track,.group");
var this_obj = html_elt_js_obj_dict[$(this).attr("id")];
var child;
var container;
@@ -144,46 +146,47 @@ var moveable = (element, handle_class, container_selector, element_js_obj) => {
.bind("dragend", function() {
$(this).removeClass("dragging");
});
};
}
/**
* Init constants & functions used throughout trackster.
*/
var // Padding at the top of tracks for error messages
ERROR_PADDING = 20;
var // Maximum number of rows un a slotted track
MAX_FEATURE_DEPTH = 100;
// Padding at the top of tracks for error messages
const ERROR_PADDING = 20;
var // Minimum width for window for squish to be used.
MIN_SQUISH_VIEW_WIDTH = 12000;
// Maximum number of rows un a slotted track
const MAX_FEATURE_DEPTH = 100;
var // Number of pixels per tile, not including left offset.
TILE_SIZE = 400;
// Minimum width for window for squish to be used.
const MIN_SQUISH_VIEW_WIDTH = 12000;
var DEFAULT_DATA_QUERY_WAIT = 5000;
// Number of pixels per tile, not including left offset.
const TILE_SIZE = 400;
var // Maximum number of chromosomes that are selectable at any one time.
MAX_CHROMS_SELECTABLE = 100;
const DEFAULT_DATA_QUERY_WAIT = 5000;
var DATA_ERROR = "Cannot display dataset due to an error. ";
// Maximum number of chromosomes that are selectable at any one time.
const MAX_CHROMS_SELECTABLE = 100;
var DATA_NOCONVERTER = "A converter for this dataset is not installed. Please check your datatypes_conf.xml file.";
const DATA_ERROR = "Cannot display dataset due to an error. ";
var DATA_NONE = "No data for this chrom/contig.";
const DATA_NOCONVERTER = "A converter for this dataset is not installed. Please check your datatypes_conf.xml file.";
var DATA_PENDING =
const DATA_NONE = "No data for this chrom/contig.";
const DATA_PENDING =
"Preparing data. This can take a while for a large dataset. " +
"If the visualization is saved and closed, preparation will continue in the background.";
var DATA_CANNOT_RUN_TOOL = "Tool cannot be rerun: ";
var DATA_LOADING = "Loading data...";
var DATA_OK = "Ready for display";
var TILE_CACHE_SIZE = 10;
var DATA_CACHE_SIZE = 20;
var // Numerical/continuous data display modes.
CONTINUOUS_DATA_MODES = ["Histogram", "Line", "Filled", "Intensity"];
const DATA_CANNOT_RUN_TOOL = "Tool cannot be rerun: ";
//var DATA_LOADING = "Loading data...";
const DATA_OK = "Ready for display";
const TILE_CACHE_SIZE = 10;
//var DATA_CACHE_SIZE = 20;
//
// Numerical/continuous data display modes.
const CONTINUOUS_DATA_MODES = ["Histogram", "Line", "Filled", "Intensity"];
/**
* Round a number to a given number of decimal places.
@@ -248,7 +251,10 @@ var Drawable = function(view, container, obj_dict) {
this.action_icons = {};
// -- Set up drawable configuration. --
this.config = config_mod.ConfigSettingCollection.from_models_and_saved_values(this.config_params, obj_dict.prefs);
this.config = config_mod.ConfigSettingCollection.from_models_and_saved_values(
this.build_config_params(),
obj_dict.prefs
);
// If there's no saved name, use object name.
if (!this.config.get_value("name")) {
@@ -342,6 +348,10 @@ extend(Drawable.prototype, {
}
],
build_config_params: function() {
return this.config_params;
},
config_onchange: function() {},
init: function() {},
@@ -723,7 +733,6 @@ extend(DrawableGroup.prototype, Drawable.prototype, DrawableCollection.prototype
for (i = 0; i < num_drawables; i++) {
drawable = this.drawables[i];
if (drawable.get_type() !== a_type) {
can_composite = false;
break;
}
if (drawable instanceof FeatureTrack) {
@@ -772,16 +781,12 @@ extend(DrawableGroup.prototype, Drawable.prototype, DrawableCollection.prototype
// manager.
//
this.filters_manager.remove_all();
var filters;
var new_filter;
var min;
var max;
for (var filter_name in shared_filters) {
filters = shared_filters[filter_name];
let filters = shared_filters[filter_name];
if (filters.length === num_feature_tracks) {
// Add new filter.
// FIXME: can filter.copy() be used?
new_filter = new filters_mod.NumberFilter({
let new_filter = new filters_mod.NumberFilter({
name: filters[0].name,
index: filters[0].index
});
@@ -834,7 +839,7 @@ extend(DrawableGroup.prototype, Drawable.prototype, DrawableCollection.prototype
name: this.config.get_value("name"),
drawables: this.drawables
});
var index = this.container.replace_drawable(this, composite_track, true);
this.container.replace_drawable(this, composite_track, true);
composite_track.request_draw();
},
@@ -981,7 +986,8 @@ var TracksterView = Backbone.View.extend({
this.intro_div = $("<div/>")
.addClass("intro")
.appendTo(this.viewport_container);
var add_tracks_button = $("<div/>")
// Add tracks button
$("<div/>")
.text("Add Datasets to Visualization")
.addClass("action-button")
.appendTo(this.intro_div)
@@ -1270,11 +1276,11 @@ var TracksterView = Backbone.View.extend({
$(window).bind("resize", function() {
// Stop previous timer.
if (this.resize_timer) {
clearTimeout(this.resize_timer);
window.clearTimeout(this.resize_timer);
}
// When function activated, resize window and redraw.
this.resize_timer = setTimeout(() => {
this.resize_timer = window.setTimeout(() => {
view.resize_window();
}, 500);
});
@@ -1309,18 +1315,17 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
trigger_navigate: function(new_chrom, new_low, new_high, delay) {
// Stop previous timer.
if (this.timer) {
clearTimeout(this.timer);
window.clearTimeout(this.timer);
}
if (delay) {
// To aggregate calls, use timer and only navigate once
// location has stabilized.
var self = this;
this.timer = setTimeout(() => {
self.trigger("navigate", `${new_chrom}:${new_low}-${new_high}`);
this.timer = window.setTimeout(() => {
this.trigger("navigate", `${new_chrom}:${new_low}-${new_high}`);
}, 500);
} else {
view.trigger("navigate", `${new_chrom}:${new_low}-${new_high}`);
this.trigger("navigate", `${new_chrom}:${new_low}-${new_high}`);
}
},
@@ -1343,13 +1348,12 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
load_chroms: function(url_parms) {
url_parms.num = MAX_CHROMS_SELECTABLE;
var view = this;
var chrom_data = $.Deferred();
$.ajax({
url: `${Galaxy.root}api/genomes/${this.dbkey}`,
data: url_parms,
dataType: "json",
success: function(result) {
success: result => {
// Do nothing if could not load chroms.
if (result.chrom_info.length === 0) {
return;
@@ -1357,34 +1361,34 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
// Load chroms.
if (result.reference) {
var ref_track = new ReferenceTrack(view);
view.add_label_track(ref_track);
view.reference_track = ref_track;
var ref_track = new ReferenceTrack(this);
this.add_label_track(ref_track);
this.reference_track = ref_track;
}
view.chrom_data = result.chrom_info;
this.chrom_data = result.chrom_info;
view.chrom_select.html("");
view.chrom_select.append($('<option value="">Select Chrom/Contig</option>'));
this.chrom_select.html("");
this.chrom_select.append($('<option value="">Select Chrom/Contig</option>'));
for (var i = 0, len = view.chrom_data.length; i < len; i++) {
var chrom = view.chrom_data[i].chrom;
for (var i = 0, len = this.chrom_data.length; i < len; i++) {
var chrom = this.chrom_data[i].chrom;
var chrom_option = $("<option>");
chrom_option.text(chrom);
chrom_option.val(chrom);
view.chrom_select.append(chrom_option);
this.chrom_select.append(chrom_option);
}
if (result.prev_chroms) {
view.chrom_select.append($(`<option value="previous">Previous ${MAX_CHROMS_SELECTABLE}</option>`));
this.chrom_select.append($(`<option value="previous">Previous ${MAX_CHROMS_SELECTABLE}</option>`));
}
if (result.next_chroms) {
view.chrom_select.append($(`<option value="next">Next ${MAX_CHROMS_SELECTABLE}</option>`));
this.chrom_select.append($(`<option value="next">Next ${MAX_CHROMS_SELECTABLE}</option>`));
}
view.chrom_start_index = result.start_index;
this.chrom_start_index = result.start_index;
chrom_data.resolve(result.chrom_info);
},
error: function() {
alert(`Could not load chroms for this dbkey: ${view.dbkey}`);
alert(`Could not load chroms for this dbkey: ${this.dbkey}`);
}
});
return chrom_data;
@@ -1491,7 +1495,7 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
str = str.replace(/,/g, "");
// Replace colons and hyphens with space for easy parsing.
str = str.replace(/:|\-/g, " ");
str = str.replace(/:|-/g, " ");
// Parse new location.
var chrom_pos = str.split(/\s+/);
@@ -1537,10 +1541,10 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
// Set up timeout to redraw with more data when moving stops.
if (this.redraw_on_move_fn) {
clearTimeout(this.redraw_on_move_fn);
window.clearTimeout(this.redraw_on_move_fn);
}
this.redraw_on_move_fn = setTimeout(() => {
this.redraw_on_move_fn = window.setTimeout(() => {
view.request_redraw();
}, 200);
@@ -1617,7 +1621,7 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
// Set up redraw if it has not been requested since last redraw.
if (!this.requested_redraw) {
requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
view._redraw();
});
this.requested_redraw = true;
@@ -1779,8 +1783,8 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
this.overview_box.height(this.default_overview_height);
this.overview_close.hide();
this.overview_highlight.hide();
view.resize_window();
view.overview_drawable = null;
this.resize_window();
this.overview_drawable = null;
}
});
@@ -1828,7 +1832,7 @@ var ToolParameterView = Backbone.View.extend({
var param = this.model;
// Param label.
var label_div = $("<div>")
$("<div>")
.addClass("param-label")
.text(param.get("label"))
.appendTo(param_div);
@@ -1881,7 +1885,7 @@ var TracksterToolView = Backbone.View.extend({
});
// Add name, inputs.
var name_div = $("<div class='tool-name'>")
$("<div class='tool-name'>")
.appendTo(parent_div)
.text(tool.get("name"));
tool.get("inputs").each(param => {
@@ -1989,44 +1993,42 @@ var TracksterToolView = Backbone.View.extend({
regions: [region.toJSON()]
};
var current_track = track;
var // Set name of track to include tool name, parameters, and region used.
track_name = tool.get("name") + current_track.tool_region_and_parameters_str(region);
// Set name of track to include tool name, parameters, and region used.
var track_name = tool.get("name") + track.tool_region_and_parameters_str(region);
var container;
// If track not in a group, create a group for it and add new track to group. If track
// already in group, add track to group.
if (current_track.container === view) {
if (track.container === track.view) {
// Create new group.
var group = new DrawableGroup(view, view, {
var group = new DrawableGroup(track.view, track.view, {
name: track.config.get_value("name")
});
// Replace track with group.
var index = current_track.container.replace_drawable(current_track, group, false);
var index = track.container.replace_drawable(track, group, false);
// Update HTML.
// FIXME: this is ugly way to replace a track with a group -- make this easier via
// a Drawable or DrawableCollection function.
group.container_div.insertBefore(current_track.view.content_div.children()[index]);
group.add_drawable(current_track);
current_track.container_div.appendTo(group.content_div);
group.container_div.insertBefore(track.view.content_div.children()[index]);
group.add_drawable(track);
track.container_div.appendTo(group.content_div);
container = group;
} else {
// Use current group.
container = current_track.container;
container = track.container;
}
// Create and init new track.
var new_track = new current_track.constructor(view, container, {
var new_track = new track.constructor(track.view, container, {
name: track_name,
hda_ldda: "hda"
});
new_track.init_for_tool_data();
new_track.change_mode(current_track.mode);
new_track.set_filters_manager(current_track.filters_manager.copy(new_track));
new_track.change_mode(track.mode);
new_track.set_filters_manager(track.filters_manager.copy(new_track));
new_track.update_icons();
container.add_drawable(new_track);
new_track.tiles_div.text("Starting job.");
@@ -2441,12 +2443,17 @@ extend(Track.prototype, Drawable.prototype, {
name: "param_space_viz_icon",
title: _l("Tool parameter space visualization"),
css_class: "arrow-split",
on_click_fn: function(track) {
var html = `<strong>Tool</strong>:${track.tool.get(
"name"
)}<br/><strong>Dataset</strong>:${track.config.get_value(
"name"
)}<br/><strong>Region(s)</strong>: <select name="regions"><option value="cur">current viewing area</option><option value="bookmarks">bookmarks</option><option value="both">current viewing area and bookmarks</option></select>`;
on_click_fn: track => {
var html = `
<strong>Tool</strong>:${track.tool.get("name")}<br/>
<strong>Dataset</strong>:${track.config.get_value("name")}<br/>
<strong>Region(s)</strong>:
<select name="regions">
<option value="cur">current viewing area</option>
<option value="bookmarks">bookmarks</option>
<option value="both">current viewing area and bookmarks</option>
</select>
`;
var cancel_fn = () => {
Galaxy.modal.hide();
@@ -2454,22 +2461,22 @@ extend(Track.prototype, Drawable.prototype, {
};
var ok_fn = () => {
var regions_to_use = $('select[name="regions"] option:selected').val(),
regions,
view_region = new visualization.GenomeRegion({
chrom: view.chrom,
start: view.low,
end: view.high
}),
bookmarked_regions = _.map(
$(".bookmark"),
elt =>
new visualization.GenomeRegion({
from_str: $(elt)
.children(".position")
.text()
})
);
var regions_to_use = $('select[name="regions"] option:selected').val();
var regions;
var view_region = new visualization.GenomeRegion({
chrom: this.view.chrom,
start: this.view.low,
end: this.view.high
});
var bookmarked_regions = _.map(
$(".bookmark"),
elt =>
new visualization.GenomeRegion({
from_str: $(elt)
.children(".position")
.text()
})
);
// Get regions for visualization.
if (regions_to_use === "cur") {
@@ -2493,6 +2500,8 @@ extend(Track.prototype, Drawable.prototype, {
})}`;
};
/*
* TODO: Re-enable this when functional.
var check_enter_esc = e => {
if ((e.keyCode || e.which) === 27) {
// Escape key
@@ -2502,6 +2511,7 @@ extend(Track.prototype, Drawable.prototype, {
ok_fn();
}
};
*/
// show dialog
Galaxy.modal.show({
@@ -2732,7 +2742,7 @@ extend(Track.prototype, Drawable.prototype, {
track.container_div.addClass("pending");
track.show_message(DATA_PENDING);
//$("<img/>").attr("src", image_path + "/yui/rel_interstitial_loading.gif").appendTo(track.tiles_div);
setTimeout(() => {
window.setTimeout(() => {
track.init();
}, track.data_query_wait);
} else if (result === "data" || result.status === "data") {
@@ -3040,7 +3050,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
var clear_after = options && options.clear_after;
var low = this.view.low;
var high = this.view.high;
var range = high - low;
//var range = high - low;
var width = this.view.container.width();
var w_scale = this.view.resolution_px_b;
var resolution = 1 / w_scale;
@@ -3049,7 +3059,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
if (this.is_overview) {
low = this.view.max_low;
high = this.view.max_high;
w_scale = width / (view.max_high - view.max_low);
w_scale = width / (this.view.max_high - this.view.max_low);
resolution = 1 / w_scale;
}
@@ -3105,17 +3115,16 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
}
// When all tiles are drawn, call post-draw actions.
var track = this;
$.when.apply($, tile_promises).then(() => {
// Step (c) for (re)moving tiles when clear_after is true:
track.tiles_div.children(".remove").remove();
this.tiles_div.children(".remove").remove();
// Only do postdraw actions for tiles; instances where tiles may not be drawn include:
// (a) ReferenceTrack without sufficient resolution;
// (b) data_fetch = false.
tiles = _.filter(tiles, t => t !== null);
if (tiles.length !== 0) {
track.postdraw_actions(tiles, width, w_scale, clear_after);
this.postdraw_actions(tiles, width, w_scale, clear_after);
}
});
},
@@ -3124,18 +3133,17 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
* Add a maximum/minimum label to track.
*/
_add_yaxis_label: function(type, on_change) {
var track = this;
var css_class = type === "max" ? "top" : "bottom";
var text = type === "max" ? "max" : "min";
var pref_name = type === "max" ? "max_value" : "min_value";
var label = this.container_div.find(`.yaxislabel.${css_class}`);
var value = round(track.config.get_value(pref_name), 1);
var value = round(this.config.get_value(pref_name), 1);
// Default action for on_change is to redraw track.
on_change =
on_change ||
(() => {
track.request_draw({ clear_tile_cache: true });
this.request_draw({ clear_tile_cache: true });
});
if (label.length !== 0) {
@@ -3149,7 +3157,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
num_cols: 12,
on_finish: function(new_val) {
$(".tooltip").remove();
track.config.set_value(pref_name, round(new_val, 1));
this.config.set_value(pref_name, round(new_val, 1));
on_change();
},
help_text: `Set ${text} value`
@@ -3176,18 +3184,17 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
// Clear because this is set when drawing.
this.max_height_px = 0;
var track = this;
_.each(tiles, tile => {
if (!(tile instanceof LineTrackTile)) {
tile.html_elt.remove();
track.draw_helper(tile.region, w_scale, {
this.draw_helper(tile.region, w_scale, {
force: true,
mode: "Coverage"
});
}
});
track._add_yaxis_label("max");
this._add_yaxis_label("max");
} else {
// -- Drawing in non-Coverage mode. --
@@ -3247,19 +3254,16 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
var mode = options.mode || this.mode;
var resolution = 1 / w_scale;
var // Useful vars.
track = this;
var drawables = this._get_drawables();
var key = this._gen_tile_cache_key(w_scale, region);
var is_tile = o => o && "track" in o;
// Check tile cache, if found show existing tile in correct position
var tile = force ? undefined : track.tile_cache.get_elt(key);
var tile = force ? undefined : this.tile_cache.get_elt(key);
if (tile) {
if (is_tile(tile)) {
track.show_tile(tile, w_scale);
this.show_tile(tile, w_scale);
}
return tile;
}
@@ -3277,16 +3281,16 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
// Map drawable object to data needed for drawing.
var tile_data = _.map(drawables, (
d // Get the track data/promise.
) => d.data_manager.get_data(region, data_mode, resolution, track.data_url_extra_params));
) => d.data_manager.get_data(region, data_mode, resolution, this.data_url_extra_params));
// Get reference data/promise.
if (view.reference_track) {
if (this.view.reference_track) {
tile_data.push(
view.reference_track.data_manager.get_data(
this.view.reference_track.data_manager.get_data(
region,
mode,
resolution,
view.reference_track.data_url_extra_params
this.view.reference_track.data_url_extra_params
)
);
}
@@ -3298,7 +3302,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
// When data is available, draw tile.
//
var tile_drawn = $.Deferred();
track.tile_cache.set_elt(key, tile_drawn);
this.tile_cache.set_elt(key, tile_drawn);
$.when.apply($, get_tile_data()).then(() => {
var tile_data = get_tile_data();
var tracks_data = tile_data;
@@ -3309,41 +3313,36 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
// Deferred, try again from the top. NOTE: this condition could (should?) be handled by the
// GenomeDataManager in visualization module.
if (_.find(tile_data, d => util.is_deferred(d))) {
track.tile_cache.set_elt(key, undefined);
$.when(track.draw_helper(region, w_scale, options)).then(tile => {
this.tile_cache.set_elt(key, undefined);
$.when(this.draw_helper(region, w_scale, options)).then(tile => {
tile_drawn.resolve(tile);
});
return;
}
// If sequence data is available, subset to get only data in region.
if (view.reference_track) {
seq_data = view.reference_track.data_manager.subset_entry(tile_data.pop(), region);
if (this.view.reference_track) {
seq_data = this.view.reference_track.data_manager.subset_entry(tile_data.pop(), region);
}
// Get drawing modes, heights for all tracks.
var drawing_modes = [];
var drawing_heights = [];
_.each(drawables, (d, i) => {
var mode = d.mode;
var data = tracks_data[i];
if (mode === "Auto") {
mode = d.get_mode(data);
d.update_auto_mode(mode);
if (d.mode === "Auto") {
d.mode = d.get_mode(data);
d.update_auto_mode(d.mode);
}
drawing_modes.push(mode);
drawing_heights.push(d.get_canvas_height(data, mode, w_scale, width));
drawing_modes.push(d.mode);
drawing_heights.push(d.get_canvas_height(data, d.mode, w_scale, width));
});
var canvas = track.view.canvas_manager.new_canvas();
var canvas = this.view.canvas_manager.new_canvas();
var tile_low = region.get("start");
var tile_high = region.get("end");
var all_data_index = 0;
var width = Math.ceil((tile_high - tile_low) * w_scale) + track.left_offset;
var width = Math.ceil((tile_high - tile_low) * w_scale) + this.left_offset;
var height = _.max(drawing_heights);
var tile;
@@ -3354,7 +3353,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
// Height is specified in options or is the height found above.
canvas.height = options.height || height;
var ctx = canvas.getContext("2d");
ctx.translate(track.left_offset, 0);
ctx.translate(this.left_offset, 0);
if (drawables.length > 1) {
ctx.globalAlpha = 0.5;
ctx.globalCompositeOperation = "source-over";
@@ -3365,8 +3364,8 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
// Don't cache, show if no tile.
if (tile !== undefined) {
track.tile_cache.set_elt(key, tile);
track.show_tile(tile, w_scale);
this.tile_cache.set_elt(key, tile);
this.show_tile(tile, w_scale);
}
tile_drawn.resolve(tile);
@@ -3428,7 +3427,6 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
* an existing tile rather than reshowing it.
*/
show_tile: function(tile, w_scale) {
var track = this;
var tile_element = tile.html_elt;
// -- Show/move tile element. --
@@ -3785,7 +3783,7 @@ extend(CompositeTrack.prototype, TiledTrack.prototype, {
}
// Replace track with group.
var index = this.container.replace_drawable(this, group, true);
this.container.replace_drawable(this, group, true);
group.request_draw({ clear_tile_cache: true });
},
@@ -3819,7 +3817,6 @@ extend(CompositeTrack.prototype, TiledTrack.prototype, {
* Update minimum, maximum for component tracks.
*/
update_all_min_max: function() {
var track = this;
var min_value = this.config.get_value("min_value");
var max_value = this.config.get_value("max_value");
_.each(this.drawables, d => {
@@ -4127,7 +4124,8 @@ extend(DiagonalHeatmapTrack.prototype, Drawable.prototype, TiledTrack.prototype,
hda_ldda: track.dataset.get("hda_ldda")
},
result => {
var data = result.data;
// What does this do? Is it meant to be attached to some higher scope state object?
// var data = result.data;
}
);
},
@@ -4163,10 +4161,9 @@ var FeatureTrack = function(view, container, obj_dict) {
this.slotters = {};
this.start_end_dct = {};
this.left_offset = 200;
// this.painter = painters.LinkedFeaturePainter;
this.set_painter_from_config();
};
extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
display_modes: ["Auto", "Coverage", "Dense", "Squish", "Pack"],
@@ -4258,9 +4255,6 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
postdraw_actions: function(tiles, width, w_scale, clear_after) {
TiledTrack.prototype.postdraw_actions.call(this, tiles, width, w_scale, clear_after);
var track = this;
var i;
var line_track_tiles = _.filter(tiles, t => t instanceof LineTrackTile);
//
@@ -4277,7 +4271,6 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
});
// Draw incomplete features on each tile.
var self = this;
_.each(tiles, tile => {
// Remove features already drawn on tile originally.
var tile_incomplete_features = _.omit(
@@ -4296,16 +4289,16 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
data: _.values(tile_incomplete_features)
};
var new_canvas = self.view.canvas_manager.new_canvas();
var new_canvas = this.view.canvas_manager.new_canvas();
var new_canvas_ctx = new_canvas.getContext("2d");
new_canvas.height = Math.max(
tile.canvas.height,
self.get_canvas_height(features, tile.mode, tile.w_scale, 100)
this.get_canvas_height(features, tile.mode, tile.w_scale, 100)
);
new_canvas.width = tile.canvas.width;
new_canvas_ctx.drawImage(tile.canvas, 0, 0);
new_canvas_ctx.translate(track.left_offset, 0);
var new_tile = self.draw_tile(
new_canvas_ctx.translate(this.left_offset, 0);
var new_tile = this.draw_tile(
features,
new_canvas_ctx,
tile.mode,
@@ -4348,8 +4341,8 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
//
// Update filtering UI.
if (track.filters_manager) {
var filters = track.filters_manager.filters;
if (this.filters_manager) {
var filters = this.filters_manager.filters;
var f;
for (f = 0; f < filters.length; f++) {
filters[f].update_ui_elt();
@@ -4361,7 +4354,7 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
var example_feature;
var filter;
for (i = 0; i < tiles.length; i++) {
for (let i = 0; i < tiles.length; i++) {
if (tiles[i].data.length) {
example_feature = tiles[i].data[0];
for (f = 0; f < filters.length; f++) {
@@ -4375,12 +4368,12 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
}
// If filter availability changed, hide filter div if necessary and update menu.
if (track.filters_available !== filters_available) {
track.filters_available = filters_available;
if (!track.filters_available) {
track.filters_manager.hide();
if (this.filters_available !== filters_available) {
this.filters_available = filters_available;
if (!this.filters_available) {
this.filters_manager.hide();
}
track.update_icons();
this.update_icons();
}
}
@@ -4389,7 +4382,7 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
//
if (tiles[0] instanceof FeatureTrackTile) {
var all_slotted = true;
for (i = 0; i < tiles.length; i++) {
for (let i = 0; i < tiles.length; i++) {
if (!tiles[i].all_slotted) {
all_slotted = false;
break;
@@ -4557,6 +4550,7 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
);
var feature_mapper = null;
var incomplete_features = null;
ctx.fillStyle = this.config.get_value("block_color");
ctx.font = ctx.canvas.manager.default_font;
+3 -5
View File
@@ -5,7 +5,6 @@ import util_mod from "viz/trackster/util";
import config_mod from "utils/config";
import GridView from "mvc/grid/grid-view";
import Tabs from "mvc/ui/ui-tabs";
import Ui from "mvc/ui/ui-misc";
/**
* Mixin for returning custom JSON representation from toJSON. Class attribute to_json_keys defines a set of attributes
* to include in the representation; to_json_mappers defines mappers for returned objects.
@@ -1109,12 +1108,11 @@ var TrackBrowserRouter = Backbone.Router.extend({
// Can't put regular expression in routes dictionary.
// NOTE: parentheses are used to denote parameters returned to callback.
this.route(/([\w]+)$/, "change_location");
this.route(/([\w\+]+\:[\d,]+-[\d,]+)$/, "change_location");
this.route(/([\w+]+:[\d,]+-[\d,]+)$/, "change_location");
// Handle navigate events from view.
var self = this;
self.view.on("navigate", new_loc => {
self.navigate(new_loc);
this.view.on("navigate", new_loc => {
this.navigate(new_loc);
});
},
+2 -2
View File
@@ -21,7 +21,7 @@
</datatype>
<datatype extension="qname_sorted.bam" type="galaxy.datatypes.binary:BamQuerynameSorted" mimetype="application/octet-stream" display_in_upload="true" description="A binary file compressed in the BGZF format with a '.bam' file extension and sorted by queryname." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#BAM">
</datatype>
<datatype extension="bam_native" type="galaxy.datatypes.binary:BamNative" mimetype="application/octet-stream" display_in_upload="true" description="A binary file compressed in the BGZF format with a '.bam' file extension." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#BAM">
<datatype extension="unsorted.bam" type="galaxy.datatypes.binary:BamNative" mimetype="application/octet-stream" display_in_upload="true" description="A binary file compressed in the BGZF format with a '.bam' file extension." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#BAM">
<converter file="bam_to_bigwig_converter.xml" target_datatype="bigwig"/>
<converter file="to_coordinate_sorted_bam.xml" target_datatype="bam"/>
<converter file="to_qname_sorted_bam.xml" target_datatype="qname_sorted.bam"/>
@@ -299,7 +299,7 @@
<datatype extension="qual454" type="galaxy.datatypes.qualityscore:QualityScore454" display_in_upload="true"/>
<datatype extension="roadmaps" type="galaxy.datatypes.assembly:Roadmaps" display_in_upload="false"/>
<datatype extension="sam" type="galaxy.datatypes.tabular:Sam" display_in_upload="true">
<converter file="sam_to_bam_native.xml" target_datatype="bam_native"/>
<converter file="sam_to_unsorted_bam.xml" target_datatype="unsorted.bam"/>
<converter file="to_coordinate_sorted_bam.xml" target_datatype="bam"/>
<converter file="to_qname_sorted_bam.xml" target_datatype="qname_sorted.bam"/>
<converter file="sam_to_bigwig_converter.xml" target_datatype="bigwig"/>
File diff suppressed because it is too large Load Diff
+140 -3
View File
@@ -3,8 +3,145 @@
January 2018 Galaxy Release (v 18.01)
===========================================================
.. include:: _header.rst
Schedule
Highlights
===========================================================
* Planned Freeze Date: 2018-01-08
* Planned Release Date: 2018-01-29
**Performance and User Experience Improvements**
We made Galaxy more lively and responsive. Homepage, published workflows, published/saved histories, and data libraries should all load much faster now. Importing data from FTP will also take less of your time.
We achieved this by optimizing database queries, implementing cache, rethinking presentation, and adding progress bars and loading indicators where needed. See the `list of related performance pull requests`_.
**Web Server and Configuration**
The default web server used by Galaxy has changed from Paste to `uWSGI <https://uwsgi-docs.readthedocs.io/>`__ and the default configuration file for Galaxy is now ``config/galaxy.yml`` instead of ``config/galaxy.ini``.
uWSGI is more production ready and allows Galaxy to scale better in its default
configuration. In the future uWSGI will allow Galaxy to setup GIE proxies without additional
configuration and use modern web technologies such as web sockets.
Read more about the server, configuration, and documentation changes in the `uWSGI details`_ section of this document.
**Dataset Collection Usability**
This release has significantly improved the usability of Galaxy dataset collections. Dozens of improvements
to collections have been made, some of the key highlights include:
- Data library folders can now be sent to histories as a dataset collection. `Pull Request 4998`_, `Pull Request 5080`_
- Failed dataset collection elements can now be fixed using job re-running (thanks to `@mvdbeek <https://github.com/mvdbeek>`__). `Pull Request 5321`_, `Pull Request 5247`_, `Pull Request 5270`_
- Collections now appear with state and progress bars in the history panel and contained datasets
are hidden by default (with help from `@mvdbeek <https://github.com/mvdbeek>`__).
`Pull Request 5013`_, `Pull Request 5078`_
- We added intuitive workflow post job actions for dataset collections. `Pull Request 5416`_, `Pull Request 5418`_, `Pull Request 5414`_
- The web interface now supports collections with arbitrary nesting and size (with help
from `@mvdbeek <https://github.com/mvdbeek>`__). `Pull Request 4942`_, `Pull Request 4934`_, `Pull Request 5091`_
- More robust nametag discovery and propagation when using collections. `Pull Request 5403`_
**Client Architecture**
The architecture for the client code that powers the Galaxy user interface has been significantly
overhauled. The code base has been converted to `ES6 <http://es6-features.org/>`__,
`Yarn <https://github.com/yarnpkg/yarn>`__ now powers the build and dependency management of the code,
`Prettier <https://prettier.io/>`__ is now used to ensure consistent code formatting,
and the `VueJS <https://vuejs.org/>`__ framework has been integrated. Taken together, these changes are
enabling Galaxy developers to write usable, responsive client code more quickly and concisely than
previously possible. A big thanks goes out to community contributions from
`@anuprulez <https://github.com/anuprulez>`__ and `@anatskiy <https://github.com/anatskiy>`__ that are
already converting existing Galaxy components to reactive VueJS ones. See the `list of related client pull requests`_.
**New BAM datatypes**
Previously Galaxy only supported coordinate sorted BAM files by default (the ``bam`` datatype). In addition, this
release of Galaxy now supports three new types of BAM:
- ``qname_sorted.bam``, that ensures that the file is queryname sorted (e.g. ``SO:queryname``);
- ``qname_input_sorted.bam``, that can be used to describe the output of aligners which generally keep mate pairs adjacent
- ``unsorted.bam``, that makes no assumptions about the sort order of the file.
A huge thanks goes out to `@bgruening <https://github.com/bgruening>`__ and `@mvdbeek <https://github.com/mvdbeek>`__ who
implemented these datatypes.
`Pull Request 5180`_, `Pull Request 5589`_, `Pull Request 5532`_, `Pull Request 5644`_, `Pull Request 5674`_
**Experimental Job Caching**
Galaxy can now be configured to allow users the option of skipping duplicated jobs if one with
identical parameters has been previously executed and simply reuse the previously generated outputs.
This contribution is thanks to `@mvdbeek <https://github.com/mvdbeek>`__.
`Pull Request 4690`_
Get Galaxy
==========
The code lives at `Github <https://github.com/galaxyproject/galaxy>`__ and you should have `Git <https://git-scm.com/>`__ to obtain it.
To get a new Galaxy repository run:
.. code-block:: shell
$ git clone -b release_18.01 https://github.com/galaxyproject/galaxy.git
To update an existing Galaxy repository run:
.. code-block:: shell
$ git checkout release_18.01 && git pull --ff-only origin release_18.01
See the `community hub <https://galaxyproject.org/develop/source-code/>`__ for additional details regarding the source code locations.
Security
========
Breaking Changes
================
We have reworked the **Galaxy Webhook** interface so if you have custom webhooks at your instance
you need to take the following steps in order to make them work:
1. Rename the main config file: <name>.yml -> config.yml
2. In the config file, rename the name attribute: name -> id
3. Put all files into the plugin's root folder, which should contain only four files: ``config.yml`` (mandatory), ``__init__.py`` (optional), ``script.js`` (optional), ``styles.css`` (optional)
Deprecation Notices
===================
* This is the last release we are shipping JavaScript source maps for Galaxy client.
Removal Notices
===============
The following features that have been deprecated in the past releases are being removed in 18.01:
* Sample tracking
* Sample request and external services management
* Legacy library interface for Administrators
In addition to that, the PlantTribes datatypes have been commented out in the source code. Uncomment them if you want to re-enable it.
uWSGI details
=============
To minimize the impact of this change on existing Galaxy instances, if a Galaxy has a ``galaxy.ini``
file configured, it will continue to use Paste by default unless additional steps are
taken by the administrator (Galaxy can be forced to start under uWSGI even with an older configuration file
by setting ``APP_WEBSERVER=uwsgi`` in the environment). As part of the transition to YAML-based
configuration files, we have implemented a schema to validate Galaxy configuration files. Run
``make config-validate`` from Galaxy's root directory to validate a schema and ``make config-lint`` to
check for best practices. While there is no need to convert your configuration file (``galaxy.ini`` hasn't
been deprecated), you can run ``make config-convert-dry-run`` and ``make config-convert``
to respectively test and perform the conversion of an ``ini`` configuration file to a YAML one.
These are big changes that affect many parts of Galaxy's administration documentation and makes
this documentation very dependent on which Galaxy version they are targeting. To address this, we have
moved a significant amount of administration documentation into Galaxy's code
base and made it available on a per-release basis. The latest administration documentation for
the previous release of Galaxy (17.09) which reference ``galaxy.ini`` files and Paste servers can
be found `here <https://docs.galaxyproject.org/en/release_17.09/admin/index.html>`__, while documentation for this release can be found `here <https://docs.galaxyproject.org/en/release_18.01/admin/index.html>`__.
`Pull Request 4475`_, `Pull Request 5135`_, `Pull Request 5390`_, `Pull Request 5373`_,
`Pull Request 5105`_, `Pull Request 5441`_
Release Notes
=============
.. include:: 18.01.rst
:start-after: announce_start
Security patch details
======================
.. include:: _thanks.rst
+10
View File
@@ -0,0 +1,10 @@
===========================================================
May 2018 Galaxy Release (v 18.05)
===========================================================
Schedule
===========================================================
* Planned Freeze Date: 2018-05-07
* Planned Release Date: 2018-05-28
+1
View File
@@ -4,6 +4,7 @@ Releases
.. toctree::
:maxdepth: 1
18.01_announce
17.09_announce
17.05_announce
17.01_announce
+12 -2
View File
@@ -85,7 +85,12 @@ class DockerContainer(Container):
# }
# ]
rval = []
port_mappings = self.inspect[0]['NetworkSettings']['Ports']
try:
port_mappings = self.inspect[0]['NetworkSettings']['Ports']
except (IndexError, KeyError) as exc:
log.warning("Failed to get ports for container %s from `docker inspect` output at "
"[0]['NetworkSettings']['Ports']: %s: %s", self.id, exc.__class__.__name__, str(exc))
return None
for port_name in port_mappings:
for binding in port_mappings[port_name]:
rval.append(ContainerPort(
@@ -165,7 +170,12 @@ class DockerService(Container):
# }
# ]
rval = []
port_mappings = self.inspect[0]['Endpoint']['Ports']
try:
port_mappings = self.inspect[0]['Endpoint']['Ports']
except (IndexError, KeyError) as exc:
log.warning("Failed to get ports for container %s from `docker service inspect` output at "
"[0]['Endpoint']['Ports']: %s: %s", self.id, exc.__class__.__name__, str(exc))
return None
for binding in port_mappings:
rval.append(ContainerPort(
binding['TargetPort'],
+1 -1
View File
@@ -192,7 +192,7 @@ class BamNative(Binary):
"""Class describing a BAM binary file that is not necessarily sorted"""
edam_format = "format_2572"
edam_data = "data_0863"
file_ext = "bam_native"
file_ext = "unsorted.bam"
sort_flag = None
MetadataElement(name="bam_index", desc="BAM Index File", param=metadata.FileParameter, file_ext="bai", readonly=True, no_value=None, visible=False, optional=True)
@@ -17,7 +17,7 @@
> temp.bg && bedGraphToBigWig temp.bg '$chromInfo' '$output']]>
</command>
<inputs>
<param format="bam,bam_native" name="input" type="data" label="Choose BAM file"/>
<param format="bam,unsorted.bam" name="input" type="data" label="Choose BAM file"/>
</inputs>
<outputs>
<data format="bigwig" name="output"/>
@@ -1,4 +1,4 @@
<tool id="CONVERTER_sam_to_bam_native" name="Convert SAM to BAM native - without sorting" version="1.0.0" profile="18.01">
<tool id="CONVERTER_sam_to_unsorted_bam" name="Convert SAM to BAM without sorting" version="1.0.0" profile="18.01">
<requirements>
<requirement type="package" version="1.6">samtools</requirement>
</requirements>
@@ -15,7 +15,7 @@
<param name="input" type="data" format="sam" label="SAM file"/>
</inputs>
<outputs>
<data name="output" format="bam_native"/>
<data name="output" format="unsorted.bam"/>
</outputs>
<help>
</help>
@@ -12,7 +12,7 @@
]]>
</command>
<inputs>
<param format="sam,bam_native,qname_sorted.bam" name="input" type="data" label="Choose a BAM native or queryname sortedfile"/>
<param format="sam,unsorted.bam,qname_sorted.bam" name="input" type="data" label="Choose a BAM native or queryname sortedfile"/>
</inputs>
<outputs>
<data format="bam" name="output"/>
@@ -13,7 +13,7 @@
]]>
</command>
<inputs>
<param format="sam,bam_native" name="input" type="data" label="Choose a BAM native or queryname sortedfile"/>
<param format="sam,unsorted.bam" name="input" type="data" label="Choose a BAM native or queryname sortedfile"/>
</inputs>
<outputs>
<data format="qname_sorted.bam" name="output"/>
+1 -1
View File
@@ -344,7 +344,7 @@ def guess_ext(fname, sniff_order):
'bam'
>>> fname = get_test_fname('3unsorted.bam')
>>> guess_ext(fname, sniff_order)
'bam_native'
'unsorted.bam'
>>> fname = get_test_fname('test.idpDB')
>>> guess_ext(fname, sniff_order)
'idpdb'
+1 -1
View File
@@ -34,7 +34,7 @@ class FilterFactory(object):
filters = deepcopy(self.default_filters)
if trans.user:
for name, value in trans.user.preferences.items():
if value.strip():
if value and value.strip():
user_filters = listify(value, do_strip=True)
category = ''
if name == 'toolbox_tool_filters':
+24 -13
View File
@@ -622,7 +622,7 @@ def _verify_extra_files_content(extra_files, hda_id, dataset_fetcher, test_data_
_verify_composite_datatype_file_content(filepath, hda_id, base_name=filename, attributes=attributes, dataset_fetcher=dataset_fetcher, test_data_path_builder=test_data_path_builder, keep_outputs_dir=keep_outputs_dir)
def verify_tool(tool_id, galaxy_interactor, resource_parameters={}, register_job_data=None, test_index=0, tool_version=None):
def verify_tool(tool_id, galaxy_interactor, resource_parameters={}, register_job_data=None, test_index=0, tool_version=None, quiet=False):
tool_test_dicts = galaxy_interactor.get_tool_tests(tool_id, tool_version=tool_version)
tool_test_dict = tool_test_dicts[test_index]
testdef = ToolTestDescription(tool_test_dict)
@@ -642,6 +642,7 @@ def verify_tool(tool_id, galaxy_interactor, resource_parameters={}, register_job
job_output_exceptions = None
tool_execution_exception = None
expected_failure_occurred = False
begin_time = time.time()
try:
try:
tool_response = galaxy_interactor.run_tool(testdef, test_history, resource_parameters=resource_parameters)
@@ -662,7 +663,7 @@ def verify_tool(tool_id, galaxy_interactor, resource_parameters={}, register_job
assert data_list or data_collection_list
try:
job_stdio = _verify_outputs(testdef, test_history, jobs, tool_id, data_list, data_collection_list, galaxy_interactor)
job_stdio = _verify_outputs(testdef, test_history, jobs, tool_id, data_list, data_collection_list, galaxy_interactor, quiet=quiet)
except JobOutputsError as e:
job_stdio = e.job_stdio
job_output_exceptions = e.output_exceptions
@@ -671,16 +672,26 @@ def verify_tool(tool_id, galaxy_interactor, resource_parameters={}, register_job
job_output_exceptions = [e]
raise e
finally:
job_data = {}
if tool_inputs is not None:
job_data["inputs"] = tool_inputs
if job_stdio is not None:
job_data["job"] = job_stdio
if job_output_exceptions:
job_data["output_problems"] = [str(_) for _ in job_output_exceptions]
if tool_execution_exception:
job_data["execution_problem"] = str(tool_execution_exception)
if register_job_data is not None:
end_time = time.time()
job_data = {
"tool_id": tool_id,
"tool_version": tool_version,
"test_index": test_index,
"time_seconds": end_time - begin_time,
}
if tool_inputs is not None:
job_data["inputs"] = tool_inputs
if job_stdio is not None:
job_data["job"] = job_stdio
status = "success"
if job_output_exceptions:
job_data["output_problems"] = [str(_) for _ in job_output_exceptions]
status = "failure"
if tool_execution_exception:
job_data["execution_problem"] = str(tool_execution_exception)
status = "error"
job_data["status"] = status
register_job_data(job_data)
galaxy_interactor.delete_history(test_history)
@@ -698,7 +709,7 @@ def _handle_def_errors(testdef):
raise Exception("Test parse failure")
def _verify_outputs(testdef, history, jobs, tool_id, data_list, data_collection_list, galaxy_interactor):
def _verify_outputs(testdef, history, jobs, tool_id, data_list, data_collection_list, galaxy_interactor, quiet=False):
assert len(jobs) == 1, "Test framework logic error, somehow tool test resulted in more than one job."
job = jobs[0]
@@ -713,7 +724,7 @@ def _verify_outputs(testdef, history, jobs, tool_id, data_list, data_collection_
found_exceptions = []
def register_exception(e):
if not found_exceptions:
if not found_exceptions and not quiet:
# Only print this stuff out once.
for stream in ['stdout', 'stderr']:
if stream in job_stdio:
+62 -22
View File
@@ -1,11 +1,14 @@
#!/usr/bin/env python
from __future__ import print_function
import argparse
import json
import sys
from galaxy.tools.verify.interactor import GalaxyInteractorApi, verify_tool
DESCRIPTION = "Script to quickly run a tool test against a running Galaxy instance."
DESCRIPTION = """Script to quickly run a tool test against a running Galaxy instance."""
ALL_TESTS = "*all_tests*"
def main(argv=None):
@@ -20,35 +23,70 @@ def main(argv=None):
"keep_outputs_dir": args.output,
}
tool_id = args.tool_id
test_index = int(args.test_index)
tool_version = args.tool_version
galaxy_interactor = GalaxyInteractorApi(**galaxy_interactor_kwds)
raw_test_index = args.test_index
if raw_test_index == ALL_TESTS:
tool_test_dicts = galaxy_interactor.get_tool_tests(tool_id, tool_version=tool_version)
test_indices = list(range(len(tool_test_dicts)))
else:
test_indices = [int(raw_test_index)]
test_results = []
def _register_job_data(job_data):
test_results.append({
'id': tool_id + "-" + str(test_index),
'has_data': True,
'data': job_data,
})
if args.append:
with open(args.output_json, "r") as f:
previous_results = json.load(f)
test_results = previous_results["tests"]
try:
verify_tool(
tool_id, galaxy_interactor, test_index=test_index, tool_version=tool_version, register_job_data=_register_job_data
)
finally:
report_obj = {
'version': '0.1',
'tests': test_results,
}
output_json = args.output_json
if output_json:
exceptions = []
verbose = args.verbose
for test_index in test_indices:
if tool_version:
tool_id_and_version = "%s/%s" % (tool_id, tool_version)
else:
tool_id_and_version = tool_id
test_identifier = "tool %s test # %d" % (tool_id_and_version, test_index)
def register(job_data):
test_results.append({
'id': tool_id + "-" + str(test_index),
'has_data': True,
'data': job_data,
})
try:
verify_tool(
tool_id, galaxy_interactor, test_index=test_index, tool_version=tool_version,
register_job_data=register, quiet=not verbose
)
if verbose:
print("%s passed" % test_identifier)
except Exception as e:
if verbose:
print("%s failed, %s" % (test_identifier, e))
exceptions.append(e)
report_obj = {
'version': '0.1',
'tests': test_results,
}
output_json = args.output_json
if output_json:
if args.output_json == "-":
assert not args.append
print(json.dumps(report_obj))
else:
with open(args.output_json, "w") as f:
print(report_obj)
json.dump(report_obj, f)
if exceptions:
raise exceptions[0]
def _arg_parser():
parser = argparse.ArgumentParser(description=DESCRIPTION)
@@ -57,9 +95,11 @@ def _arg_parser():
parser.add_argument('-a', '--admin-key', default=None, help='Galaxy Admin API Key')
parser.add_argument('-t', '--tool-id', default=None, help='Tool ID')
parser.add_argument('--tool-version', default=None, help='Tool Version')
parser.add_argument('-i', '--test-index', default=0, help='Tool Test Index (starting at 0)')
parser.add_argument('-i', '--test-index', default=ALL_TESTS, help='Tool Test Index (starting at 0) - by default all tests will run.')
parser.add_argument('-o', '--output', default=None, help='directory to dump outputs to')
parser.add_argument('-j', '--output_json', default=None, help='output metadata json')
parser.add_argument('--append', default=False, action="store_true", help="Extend a test record json (created with --output-json) with additional tests.")
parser.add_argument('-j', '--output-json', default=None, help='output metadata json')
parser.add_argument('--verbose', default=False, action="store_true", help="Verbose logging.")
return parser
@@ -6,6 +6,7 @@ import shlex
import stat
import string
import tempfile
import time
import uuid
from itertools import product
from subprocess import PIPE, Popen
@@ -442,7 +443,20 @@ class InteractiveEnvironmentRequest(object):
"""
run_args = self.container_run_args(image, env_override, volumes)
container = self.attr.container_interface.run_in_container(None, **run_args)
container_port = self._find_port_mapping(container.ports)
attempt = 0
container_ports = container.ports
while container_ports is None and attempt < 30:
# TODO: it would be better to do this in /interactive_environments/ready so the client doesn't block here,
# but _find_port_mapping needs certain non-persisted data (the port configured to be published) and the
# proxy manager doesn't have an update method, so that'd require bigger changes than I have the time for
# right now
attempt += 1
log.warning("Sleeping for 2 seconds while waiting for container %s ports", container.id)
time.sleep(2)
container_ports = container.ports
if container_ports is None:
raise Exception("Failed to determine ports for container '%s' after 30 attempts" % container.id)
container_port = self._find_port_mapping(container_ports)
log.debug("Container '%s' accessible at: %s:%s", container.id, container_port.hostaddr, container_port.hostport)
self.attr.proxy_request = self.trans.app.proxy_manager.setup_proxy(
self.trans,
+14 -7
View File
@@ -270,6 +270,14 @@ class InteractiveEnvironmentPlugin(VisualizationPlugin):
context['base_url'] = 'interactive_environments'
super(InteractiveEnvironmentPlugin, self).__init__(app, path, name, config, context=context, **kwargs)
def _error_template(self, trans):
return trans.fill_template('message.mako',
message='Loading the interactive environment failed, please contact the {admin_tag} for assistance'.format(
admin_tag='<a href="mailto:{admin_mail}">Galaxy administrator</a>'.format(
admin_mail=trans.app.config.error_email_to)
if trans.app.config.error_email_to else 'Galaxy administrator'),
status='error')
def _render(self, render_vars, trans=None, embedded=None, **kwargs):
"""
Override to add interactive environment specific template vars.
@@ -295,16 +303,15 @@ class InteractiveEnvironmentPlugin(VisualizationPlugin):
request = self.INTENV_REQUEST_FACTORY(trans, self)
except Exception:
log.exception("IE plugin request handling failed")
return trans.fill_template('message.mako',
message='Loading the interactive environment failed, please contact the {admin_tag} for assistance'.format(
admin_tag='<a href="mailto:{admin_mail}">Galaxy administrator</a>'.format(
admin_mail=trans.app.config.error_email_to)
if trans.app.config.error_email_to else 'Galaxy administrator'),
status='error')
return self._error_template(trans)
render_vars["ie_request"] = request
template_filename = self.config['entry_point']['file']
return trans.fill_template(template_filename, template_lookup=self.template_lookup, **render_vars)
try:
return trans.fill_template(template_filename, template_lookup=self.template_lookup, **render_vars)
except Exception:
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)
@@ -494,11 +494,10 @@ class HistoryController(BaseUIController, SharableMixin, UsesAnnotations, UsesIt
history = trans.sa_session.query(model.History).options(
eagerload_all('active_datasets.creating_job_associations.job.workflow_invocation_step.workflow_invocation.workflow'),
).get(id)
assert history
# TODO: formalize to trans.show_error
assert (history.user and (history.user.id == trans.user.id) or
(history.id == trans.history.id) or
(trans.user_is_admin()))
if not (history and ((history.user and trans.user and history.user.id == trans.user.id) or
(trans.history and history.id == trans.history.id) or
trans.user_is_admin())):
return trans.show_error_message("Cannot display history structure.")
# Resolve jobs and workflow invocations for the datasets in the history
# items is filled with items (hdas, jobs, or workflows) that go at the
# top level
@@ -1127,7 +1126,7 @@ class HistoryController(BaseUIController, SharableMixin, UsesAnnotations, UsesIt
@web.expose
def purge_deleted_datasets(self, trans):
count = 0
if trans.app.config.allow_user_dataset_purge:
if trans.app.config.allow_user_dataset_purge and trans.history:
for hda in trans.history.datasets:
if not hda.deleted or hda.purged:
continue
@@ -1145,7 +1144,8 @@ class HistoryController(BaseUIController, SharableMixin, UsesAnnotations, UsesIt
except Exception:
log.exception('Unable to purge dataset (%s) on purge of hda (%s):' % (hda.dataset.id, hda.id))
count += 1
return trans.show_ok_message("%d datasets have been deleted permanently" % count, refresh_frames=['history'])
return trans.show_ok_message("%d datasets have been deleted permanently" % count, refresh_frames=['history'])
return trans.show_error_message("Cannot purge deleted datasets from this session.")
@web.expose
def delete(self, trans, id, purge=False):
@@ -925,7 +925,9 @@ class User(BaseUIController, UsesFormDefinitionsMixin, CreatesUsersMixin, Create
Check whether token fits the user and then activate the user's account.
"""
params = util.Params(kwd, sanitize=False)
email = unquote(params.get('email', None))
email = params.get('email', None)
if email is not None:
email = unquote(email)
activation_token = params.get('activation_token', None)
if email is None or activation_token is None:
@@ -934,6 +936,9 @@ class User(BaseUIController, UsesFormDefinitionsMixin, CreatesUsersMixin, Create
else:
# Find the user
user = trans.sa_session.query(trans.app.model.User).filter(trans.app.model.User.table.c.email == email).first()
if not user:
# Probably wrong email address
return trans.show_error_message("You are using an invalid activation link. Try to log in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>") % web.url_for(controller="root", action="index")
# If the user is active already don't try to activate
if user.active is True:
return trans.show_ok_message("Your account is already active. Nothing has changed. <br><a href='%s'>Go to login page.</a>") % web.url_for(controller='root', action='index')
+7 -4
View File
@@ -43,6 +43,7 @@ def copy_sample_file(app, filename, dest_path=None):
"""
Copies a sample file at `filename` to `the dest_path`
directory and strips the '.sample' extensions from `filename`.
Returns the path to the copied file (with the .sample extension).
"""
if dest_path is None:
dest_path = os.path.abspath(app.config.tool_data_path)
@@ -56,8 +57,10 @@ def copy_sample_file(app, filename, dest_path=None):
shutil.copy(full_source_path, full_destination_path)
# Only create the .loc file if it does not yet exist. We don't overwrite it in case it
# contains stuff proprietary to the local instance.
if not os.path.lexists(os.path.join(dest_path, copied_file)):
non_sample_path = os.path.join(dest_path, copied_file)
if not os.path.lexists(non_sample_path):
shutil.copy(full_source_path, os.path.join(dest_path, copied_file))
return non_sample_path
def copy_sample_files(app, sample_files, tool_path=None, sample_files_copied=None, dest_path=None):
@@ -159,10 +162,10 @@ def handle_missing_index_file(app, tool_path, sample_files, repository_tools_tup
for sample_file in sample_files:
sample_file_name = basic_util.strip_path(sample_file)
if sample_file_name == '%s.sample' % missing_file_name:
copy_sample_file(app, os.path.join(tool_path, sample_file))
target_path = copy_sample_file(app, os.path.join(tool_path, sample_file))
if options.tool_data_table and options.tool_data_table.missing_index_file:
options.tool_data_table.handle_found_index_file(options.missing_index_file)
sample_files_copied.append(options.missing_index_file)
options.tool_data_table.handle_found_index_file(target_path)
sample_files_copied.append(target_path)
break
return repository_tools_tups, sample_files_copied
+2 -1
View File
@@ -10,6 +10,7 @@ except ImportError:
return x
from base.driver_util import setup_keep_outdir, target_url_parts
from base.instrument import register_job_data
from galaxy.tools import DataManagerTool # noqa: I201
from galaxy.tools.verify.interactor import GalaxyInteractorApi, verify_tool # noqa: I201
from .twilltestcase import TwillTestCase
@@ -37,7 +38,7 @@ class ToolTestCase(TwillTestCase):
tool_id = self.tool_id
assert tool_id
verify_tool(tool_id, self.galaxy_interactor, resource_parameters=resource_parameters, test_index=test_index, tool_version=tool_version)
verify_tool(tool_id, self.galaxy_interactor, resource_parameters=resource_parameters, test_index=test_index, tool_version=tool_version, register_job_data=register_job_data)
@nottest
@@ -23,13 +23,13 @@
</command>
<inputs>
<param name="input1" type="data" format="sam" label="SAM file" optional="true"/>
<param name="input2" type="data" format="bam_native" label="Unsorted BAM file" optional="true"/>
<param name="input2" type="data" format="unsorted.bam" label="Unsorted BAM file" optional="true"/>
<param name="input3" type="data" format="bam" label="BAM file sorted by coordinate" optional="true"/>
<param name="input4" type="data" format="qname_sorted.bam" label="BAM file sorted by queryname" optional="true"/>
<param name="input5" type="data" format="qname_input_sorted.bam" label="BAM file with sort order determined by the aligner" optional="true"/>
</inputs>
<outputs>
<data name="bam_native_output" format="bam_native">
<data name="bam_native_output" format="unsorted.bam">
<filter>input1 or input2</filter>
</data>
<data name="bam_output" format="bam">
@@ -46,12 +46,12 @@
<!-- Test that bam native output won't be sorted-->
<test>
<param name="input1" value="sam_with_header.sam" ftype="sam"/>
<output name="bam_native_output" file="bam_native_from_sam.bam" ftype="bam_native"/>
<output name="bam_native_output" file="bam_native_from_sam.bam" ftype="unsorted.bam"/>
</test>
<!-- Test that sam input is properly converted to bam native -->
<test>
<param name="input2" value="sam_with_header.sam" ftype="sam"/>
<output name="bam_native_output" file="bam_native_from_sam.bam" ftype="bam_native"/>
<output name="bam_native_output" file="bam_native_from_sam.bam" ftype="unsorted.bam"/>
</test>
<!-- Test that sam input is properly converted to bam -->
<test>
@@ -22,7 +22,7 @@
<converter file="fastqsangerbz2_to_fastqsanger.xml" target_datatype="fastqsanger"/>
</datatype>
<datatype extension="sam" type="galaxy.datatypes.tabular:Sam" display_in_upload="true">
<converter file="sam_to_bam_native.xml" target_datatype="bam_native"/>
<converter file="sam_to_unsorted_bam.xml" target_datatype="unsorted.bam"/>
<converter file="to_qname_sorted_bam.xml" target_datatype="qname_sorted.bam"/>
<converter file="to_coordinate_sorted_bam.xml" target_datatype="bam"/>
<converter file="sam_to_bigwig_converter.xml" target_datatype="bigwig"/>
@@ -30,7 +30,7 @@
<datatype extension="bam" type="galaxy.datatypes.binary:Bam" mimetype="application/octet-stream" display_in_upload="true" description="A binary file compressed in the BGZF format with a '.bam' file extension." description_url="https://galaxyproject.org/learn/datatypes/#bam" />
<datatype extension="qname_input_sorted.bam" type="galaxy.datatypes.binary:BamInputSorted" mimetype="application/octet-stream" display_in_upload="false" description="A binary file compressed in the BGZF format with a '.bam' file extension and sorted based on the aligner output." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#BAM"/>
<datatype extension="qname_sorted.bam" type="galaxy.datatypes.binary:BamQuerynameSorted" mimetype="application/octet-stream" display_in_upload="true" description="A binary file compressed in the BGZF format with a '.bam' file extension and sorted by queryname." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#BAM"/>
<datatype extension="bam_native" type="galaxy.datatypes.binary:BamNative" mimetype="application/octet-stream" display_in_upload="true" description="A binary file compressed in the BGZF format with a '.bam' file extension." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#BAM">
<datatype extension="unsorted.bam" type="galaxy.datatypes.binary:BamNative" mimetype="application/octet-stream" display_in_upload="true" description="A binary file compressed in the BGZF format with a '.bam' file extension." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#BAM">
<converter file="bam_to_bigwig_converter.xml" target_datatype="bigwig"/>
<converter file="to_coordinate_sorted_bam.xml" target_datatype="bam"/>
<converter file="to_qname_sorted_bam.xml" target_datatype="qname_sorted.bam"/>
+1 -1
View File
@@ -86,7 +86,7 @@
<tool file="column_multi_param.xml" />
<tool file="special_params.xml" />
<tool file="section.xml" />
<tool file="sam_to_bam_native.xml" />
<tool file="sam_to_unsorted_bam.xml" />
<tool file="top_level_data.xml" />
<tool file="validation_default.xml" />
<tool file="validation_sanitizer.xml" />