Merge branch 'dev' into admin_error_log

This commit is contained in:
guerler
2018-12-21 14:55:48 -05:00
35 changed files with 604 additions and 301 deletions
+12 -12
View File
@@ -4,12 +4,12 @@
import addLogging from "utils/add-logging";
import { GalaxyApp } from "./galaxy";
// import { serverPath } from "utils/serverPath";
import { serverPath } from "utils/serverPath";
export function setGalaxyInstance(factory, atTop = true) {
// console.warn("setGalaxyInstance", serverPath());
export function setGalaxyInstance(factory) {
console.warn("setGalaxyInstance", serverPath());
let storage = getStorage(atTop);
let storage = getStorage();
let newInstance = factory(GalaxyApp);
if (!(newInstance instanceof GalaxyApp)) {
newInstance = new GalaxyApp(newInstance);
@@ -19,7 +19,6 @@ export function setGalaxyInstance(factory, atTop = true) {
}
// Debugging frame property
// storage._galaxyInstance = new Proxy(newInstance, {
// get(galaxy, prop) {
// if (prop == "frame") {
@@ -46,17 +45,18 @@ export function setGalaxyInstance(factory, atTop = true) {
return storage._galaxyInstance;
}
export function getGalaxyInstance(atTop = true) {
let storage = getStorage(atTop);
export function getGalaxyInstance() {
let storage = getStorage();
return storage._galaxyInstance;
}
export function galaxyIsInitialized(atTop = true) {
let instance = getGalaxyInstance(atTop);
export function galaxyIsInitialized() {
let instance = getGalaxyInstance();
return instance !== null;
}
export function getStorage(atTop = true) {
let storage = window !== window.top && atTop ? window.top : window;
return storage;
// Having a CORS issue in the toolshed iframe, store separate versions
// of galaxy in each window for the short-term
export function getStorage() {
return window;
}
+8
View File
@@ -15,6 +15,7 @@ import DataTypes from "components/admin/DataTypes.vue";
import DataManagerView from "components/admin/DataManager/DataManagerView.vue";
import DataManagerRouter from "components/admin/DataManager/DataManagerRouter.vue";
import ErrorStack from "components/admin/ErrorStack.vue";
import DisplayApplications from "components/admin/DisplayApplications.vue";
import Vue from "vue";
import { serverPath } from "utils/serverPath";
@@ -35,6 +36,7 @@ window.app = function app(options, bootstrapped) {
"(/)admin(/)roles": "show_roles",
"(/)admin(/)groups": "show_groups",
"(/)admin(/)error_stack": "show_error_stack",
"(/)admin(/)display_applications": "show_display_applications",
"(/)admin(/)tool_versions": "show_tool_versions",
"(/)admin(/)quotas": "show_quotas",
"(/)admin(/)repositories": "show_repositories",
@@ -113,12 +115,14 @@ window.app = function app(options, bootstrapped) {
})
);
},
_display_vue_helper: function(component, props) {
let instance = Vue.extend(component);
let vm = document.createElement("div");
this.page.display(vm);
new instance(props).$mount(vm);
},
show_data_tables: function() {
this._display_vue_helper(DataTables);
},
@@ -131,6 +135,10 @@ window.app = function app(options, bootstrapped) {
this._display_vue_helper(ErrorStack);
},
show_display_applications: function() {
this._display_vue_helper(DisplayApplications);
},
show_data_manager: function(path) {
let vueMount = document.createElement("div");
this.page.display(vueMount);
+3
View File
@@ -80,3 +80,6 @@ export function multiHistory(options) {
multipanel.render(0);
});
}
// Used in common.mako
export { default as store } from "store";
@@ -34,6 +34,7 @@ var AdminPanel = Backbone.View.extend({
{
title: _l("Display applications"),
url: "admin/display_applications",
target: "__use_router__",
id: "admin-link-display-applications"
},
{
+139 -132
View File
@@ -9,147 +9,154 @@
</template>
<script>
export default {
props: {
style: {
type: String,
default: "light"
},
size: {
type: Number,
default: 100
},
center: {
type: Boolean,
default: false
}
export default {
props: {
style: {
type: String,
default: "light"
},
size: {
type: Number,
default: 100
},
center: {
type: Boolean,
default: false
}
};
}
};
</script>
<style>
.galaxy-loader.galaxy-loader_center {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
.galaxy-loader.galaxy-loader_center {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.galaxy-loader {
display: inline-block;
width: 100px;
height: 100px;
border-radius: 50px;
position: relative;
overflow: hidden;
}
.galaxy-loader_light {
background-color: transparent;
border: 2px solid #58585a;
box-sizing: border-box;
}
.galaxy-loader_dark {
background-color: #2c3143;
border: 2px solid #2c3143;
box-sizing: border-box;
}
.galaxy-loader_eu {
background-color: transparent;
border: 2px solid #063591;
box-sizing: border-box;
}
.galaxy-loader > div {
background-color: rgba(255, 0, 0, 0.3);
display: block;
height: 18px;
position: absolute;
border-radius: 2px;
-webkit-animation: galaxy-loader_animation 1.2s infinite ease-in-out;
animation: galaxy-loader_animation 1.2s infinite ease-in-out;
}
.galaxy-loader > .galaxy-loader_strip-1 {
top: 16px;
left: 13px;
width: 59px;
-webkit-animation-delay: -1.2s;
animation-delay: -1.2s;
}
.galaxy-loader > .galaxy-loader_strip-2 {
top: 39px;
left: 13px;
width: 44px;
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.galaxy-loader > .galaxy-loader_strip-3 {
top: 61px;
left: 25px;
width: 59px;
-webkit-animation-delay: -1s;
animation-delay: -1s;
}
.galaxy-loader_light > .galaxy-loader_strip-1,
.galaxy-loader_light > .galaxy-loader_strip-2 {
background-color: #58585a;
}
.galaxy-loader_light > .galaxy-loader_strip-3 {
background: linear-gradient(to right, rgba(207, 188, 59, 1), rgba(207, 188, 59, 1) 20%, rgba(207, 188, 59, 0) 100%);
}
.galaxy-loader_dark > .galaxy-loader_strip-1,
.galaxy-loader_dark > .galaxy-loader_strip-2 {
background-color: white;
}
.galaxy-loader_dark > .galaxy-loader_strip-3 {
background: linear-gradient(to right, rgba(207, 188, 59, 1), rgba(207, 188, 59, 1) 20%, rgba(207, 188, 59, 0) 100%);
}
.galaxy-loader_eu > .galaxy-loader_strip-1,
.galaxy-loader_eu > .galaxy-loader_strip-2 {
background-color: #063591;
}
.galaxy-loader_eu > .galaxy-loader_strip-3 {
background: linear-gradient(to right, rgba(207, 188, 59, 1), rgba(207, 188, 59, 1) 20%, rgba(207, 188, 59, 0) 100%);
}
@-webkit-keyframes galaxy-loader_animation {
0%,
60%,
100% {
-webkit-transform: scaleX(1);
}
.galaxy-loader {
display: inline-block;
width: 100px;
height: 100px;
border-radius: 50px;
position: relative;
overflow: hidden;
20% {
-webkit-transform: scaleX(0.8);
}
.galaxy-loader_light {
background-color: transparent;
border: 2px solid #58585a;
box-sizing: border-box;
40% {
-webkit-transform: scaleX(1.2);
}
}
.galaxy-loader_dark {
background-color: #2c3143;
border: 2px solid #2c3143;
box-sizing: border-box;
@keyframes galaxy-loader_animation {
0%,
60%,
100% {
transform: scaleX(1);
-webkit-transform: scaleX(1);
}
.galaxy-loader_eu {
background-color: transparent;
border: 2px solid #063591;
box-sizing: border-box;
20% {
transform: scaleX(0.8);
-webkit-transform: scaleX(0.8);
}
.galaxy-loader > div {
background-color: rgba(255, 0, 0, 0.3);
display: block;
height: 18px;
position: absolute;
border-radius: 2px;
-webkit-animation: galaxy-loader_animation 1.2s infinite ease-in-out;
animation: galaxy-loader_animation 1.2s infinite ease-in-out;
}
.galaxy-loader > .galaxy-loader_strip-1 {
top: 16px;
left: 13px;
width: 59px;
-webkit-animation-delay: -1.2s;
animation-delay: -1.2s;
}
.galaxy-loader > .galaxy-loader_strip-2 {
top: 39px;
left: 13px;
width: 44px;
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.galaxy-loader > .galaxy-loader_strip-3 {
top: 61px;
left: 25px;
width: 59px;
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
.galaxy-loader_light > .galaxy-loader_strip-1, .galaxy-loader_light > .galaxy-loader_strip-2 {
background-color: #58585a;
}
.galaxy-loader_light > .galaxy-loader_strip-3 {
background: linear-gradient(to right, rgba(207, 188, 59, 1), rgba(207, 188, 59, 1) 20%, rgba(207, 188, 59, 0) 100%);
}
.galaxy-loader_dark > .galaxy-loader_strip-1, .galaxy-loader_dark > .galaxy-loader_strip-2 {
background-color: white;
}
.galaxy-loader_dark > .galaxy-loader_strip-3 {
background: linear-gradient(to right, rgba(207, 188, 59, 1), rgba(207, 188, 59, 1) 20%, rgba(207, 188, 59, 0) 100%);
}
.galaxy-loader_eu > .galaxy-loader_strip-1, .galaxy-loader_eu > .galaxy-loader_strip-2 {
background-color: #063591;
}
.galaxy-loader_eu > .galaxy-loader_strip-3 {
background: linear-gradient(to right, rgba(207, 188, 59, 1), rgba(207, 188, 59, 1) 20%, rgba(207, 188, 59, 0) 100%);
}
@-webkit-keyframes galaxy-loader_animation {
0%, 60%, 100% {
-webkit-transform: scaleX(1)
}
20% {
-webkit-transform: scaleX(0.8)
}
40% {
-webkit-transform: scaleX(1.2)
}
}
@keyframes galaxy-loader_animation {
0%, 60%, 100% {
transform: scaleX(1);
-webkit-transform: scaleX(1);
}
20% {
transform: scaleX(0.8);
-webkit-transform: scaleX(0.8);
}
40% {
transform: scaleX(1.2);
-webkit-transform: scaleX(1.2);
}
40% {
transform: scaleX(1.2);
-webkit-transform: scaleX(1.2);
}
}
</style>
@@ -5,3 +5,13 @@ export function getErrorStack() {
let url = `${getAppRoot()}api/tools/error_stack`;
return axios.get(url);
}
export function getDisplayApplications() {
let url = `${getAppRoot()}api/display_applications`;
return axios.get(url);
}
export function reloadDisplayApplications(ids) {
let url = `${getAppRoot()}api/display_applications/reload`;
return axios.post(url, { ids: ids });
}
@@ -0,0 +1,124 @@
<template>
<div>
<b-alert :show="messageVisible" :variant="messageVariant">
{{ messageText }}
</b-alert>
<b-alert :show="warningVisible" variant="warning">
No display applications available.
</b-alert>
<div v-if="applicationsVisible" class="card-header">
There are currently {{ applicationsLength }}
<b-button size="sm" @click.prevent="reloadAll()" title="Reload all display applications" data-placement="bottom">
<span class="fa fa-refresh"/>
</b-button>
display applications loaded.
</div>
<b-table id="display-applications-grid" v-if="applicationsVisible" striped :fields="applicationsAttributes" :items="applications">
<template slot="reload" slot-scope="data">
<b-button size="sm" title="Reload display application" data-placement="bottom" @click.prevent="reload(data.item.id, data.index)">
<span class="fa fa-refresh"/>
</b-button>
</template>
<template slot="links" slot-scope="data">
<li v-for="link in data.item.links">{{ link.name }}</li>
</template>
</b-table>
</div>
</template>
<script>
import { getDisplayApplications, reloadDisplayApplications } from "./AdminServices.js";
export default {
data() {
return {
applications: [],
applicationsLoaded: false,
applicationsAttributes: [
{ key: "reload" },
{ key: "name", sortable: true },
{ key: "id", sortable: true },
{ key: "version" },
{ key: "links" }
],
messageText: null,
messageVariant: null
};
},
computed: {
applicationsIndex: function() {
let result = {};
for (let app of this.applications) {
result[app.id] = app;
}
return result;
},
applicationsAll: function() {
let result = [];
for (let app of this.applications) {
result.push(app.id);
}
return result;
},
applicationsVisible: function() {
return this.applications.length > 0;
},
applicationsLength: function() {
return this.applications.length;
},
messageVisible: function() {
return this.messageText != null;
},
warningVisible: function() {
return !this.applicationsVisible && this.applicationsLoaded;
}
},
created() {
getDisplayApplications()
.then(response => {
this.applications = response.data;
this.applicationsLoaded = true;
})
.catch(e => {
this._errorMessage(e);
});
},
methods: {
reload: function(id) {
this._reload([id]);
},
reloadAll: function() {
let ids = [];
for (let app of this.applications) {
ids.push(app.id);
}
this._reload(ids);
},
_reload: function(ids) {
this._highlightRows(this.applicationsAll, "default");
reloadDisplayApplications(ids)
.then(response => {
this.messageVariant = "info";
this.messageText = response.data.message;
this._highlightRows(response.data.failed, "danger");
this._highlightRows(response.data.reloaded, "success");
})
.catch(e => {
this._errorMessage(e);
});
},
_highlightRows: function(appList, status) {
for (let appIndex of appList) {
let app = this.applicationsIndex[appIndex];
if (app) {
app._rowVariant = status;
}
}
},
_errorMessage: function(e) {
let message = e && e.response && e.response.data && e.response.data.err_msg;
this.messageText = message || "Request failed for an unknown reason.";
this.messageVariant = "danger";
}
}
};
</script>
+17 -11
View File
@@ -203,23 +203,29 @@ var CenterPanel = Backbone.View.extend({
this.setElement($(this.template()));
this.$frame = this.$(".center-frame");
this.$panel = this.$(".center-panel");
this.$frame.on("load", _.bind(this._iframeChangeHandler, this));
this.$frame.on("load", this._iframeChangeHandler.bind(this));
},
/** Display iframe if its target url changes, hide center panel */
_iframeChangeHandler: function(ev) {
var iframe = ev.currentTarget;
var location = iframe.contentWindow && iframe.contentWindow.location;
if (location && location.host) {
$(iframe).show();
this.$panel.empty().hide();
var Galaxy = getGalaxyInstance();
Galaxy.trigger("center-frame:load", {
fullpath: location.pathname + location.search + location.hash,
pathname: location.pathname,
search: location.search,
hash: location.hash
});
var Galaxy = getGalaxyInstance();
// Adding try/catch to manage a CORS error in toolshed. Accessing
// location.host is a CORS no-no
try {
if (location && location.host) {
$(iframe).show();
this.$panel.empty().hide();
Galaxy.trigger("center-frame:load", {
fullpath: location.pathname + location.search + location.hash,
pathname: location.pathname,
search: location.search,
hash: location.hash
});
}
} catch (err) {
console.warn("_iframeChangeHandler error", ev, location, Galaxy);
}
},
@@ -1,12 +1,10 @@
import LIST_ITEM from "mvc/list/list-item";
import DATASET_LI from "mvc/dataset/dataset-li";
import _ from "underscore";
import $ from "jquery";
import { ListItemView, FoldoutListItemView } from "mvc/list/list-item";
import { DatasetListItemView } from "mvc/dataset/dataset-li";
import BASE_MVC from "mvc/base-mvc";
import _l from "utils/localization";
//==============================================================================
var FoldoutListItemView = LIST_ITEM.FoldoutListItemView;
var ListItemView = LIST_ITEM.ListItemView;
/** @class Read only view for DatasetCollection.
*/
var DCListItemView = FoldoutListItemView.extend(
@@ -189,9 +187,9 @@ DCEListItemView.prototype.templates = (() => {
/** @class Read only view for a DatasetCollectionElement that is also an DatasetAssociation
* (a dataset contained in a dataset collection).
*/
var DatasetDCEListItemView = DATASET_LI.DatasetListItemView.extend(
var DatasetDCEListItemView = DatasetListItemView.extend(
/** @lends DatasetDCEListItemView.prototype */ {
className: `${DATASET_LI.DatasetListItemView.prototype.className} dataset-collection-element`,
className: `${DatasetListItemView.prototype.className} dataset-collection-element`,
/** set up */
initialize: function(attributes) {
@@ -199,7 +197,7 @@ var DatasetDCEListItemView = DATASET_LI.DatasetListItemView.extend(
this.logger = this.model.logger = attributes.logger;
}
this.log("DatasetDCEListItemView.initialize:", attributes);
DATASET_LI.DatasetListItemView.prototype.initialize.call(this, attributes);
DatasetListItemView.prototype.initialize.call(this, attributes);
},
/** In this override, only get details if in the ready state.
@@ -210,7 +208,7 @@ var DatasetDCEListItemView = DATASET_LI.DatasetListItemView.extend(
if (view.model.inReadyState() && !view.model.hasDetails()) {
return view.model.fetch({ silent: true });
}
return jQuery.when();
return $.when();
},
// ......................................................................... misc
@@ -239,7 +237,7 @@ DatasetDCEListItemView.prototype.templates = (() => {
"element"
);
return _.extend({}, DATASET_LI.DatasetListItemView.prototype.templates, {
return _.extend({}, DatasetListItemView.prototype.templates, {
titleBar: titleBarTemplate
});
})();
@@ -1,9 +1,7 @@
import _ from "underscore";
import { getAppRoot } from "onload/loadConfig";
import LIST_VIEW from "mvc/list/list-view";
import DC_MODEL from "mvc/collection/collection-model";
import DC_LI from "mvc/collection/collection-li";
import BASE_MVC from "mvc/base-mvc";
import _l from "utils/localization";
var logNamespace = "collections";
@@ -56,7 +54,6 @@ var CollectionView = _super.extend(
var panel = this;
this.handleWarning($newRender);
panel.log("_queueNewRender:", $newRender, speed);
// TODO: jquery@1.12 doesn't change display when the elem has display: flex
// this causes display: block for those elems after the use of show/hide animations
// animations are removed from this view for now until fixed
@@ -83,7 +83,7 @@ var DatasetListItemEdit = _super.extend(
}
// disable if still uploading or new
} else if (_.contains([STATES.UPLOAD, STATES.NEW], this.model.get("state"))) {
} else if ([STATES.UPLOAD, STATES.NEW].includes(this.model.get("state"))) {
editBtnData.disabled = true;
editBtnData.title = _l("This dataset is not yet editable");
}
@@ -119,10 +119,13 @@ var DatasetListItemEdit = _super.extend(
var state = this.model.get("state");
if (!this.model.isDeletedOrPurged() && _.contains([STATES.OK, STATES.FAILED_METADATA], state)) {
if (!this.model.isDeletedOrPurged()) {
//Enable tagging+annotation regardless of job state (see issue #6330)
this._renderTags($details);
this._renderAnnotation($details);
this._makeDbkeyEditLink($details);
if ([STATES.OK, STATES.FAILED_METADATA].includes(state)) {
this._makeDbkeyEditLink($details);
}
}
this._setUpBehaviors($details);
@@ -20,7 +20,7 @@ var _super = LIST_ITEM.ListItemView;
/** @class Read only list view for either LDDAs, HDAs, or HDADCEs.
* Roughly, any DatasetInstance (and not a raw Dataset).
*/
var DatasetListItemView = _super.extend(
export var DatasetListItemView = _super.extend(
/** @lends DatasetListItemView.prototype */ {
_logNamespace: logNamespace,
+3 -4
View File
@@ -1,7 +1,7 @@
import _ from "underscore";
import STATES from "mvc/dataset/states";
import DC_LI from "mvc/collection/collection-li";
import DC_VIEW from "mvc/collection/collection-view";
import BASE_MVC from "mvc/base-mvc";
import HISTORY_ITEM_LI from "mvc/history/history-item-li";
import _l from "utils/localization";
@@ -17,7 +17,8 @@ var HDCAListItemView = _super.extend(
_setUpListeners: function() {
_super.prototype._setUpListeners.call(this);
var renderListen = (model, options) => {
this.render();
// We want this to swap immediately without extra animations.
this.render(0);
};
if (this.model.jobStatesSummary) {
this.listenTo(this.model.jobStatesSummary, "change", renderListen);
@@ -64,9 +65,7 @@ var HDCAListItemView = _super.extend(
stateDescription: function() {
var collection = this.model;
var elementCount = collection.get("element_count");
var jobStateSource = collection.get("job_source_type");
var collectionType = this.model.get("collection_type");
var collectionTypeDescription = DC_VIEW.collectionTypeDescription(collection);
var simpleDescription = DC_VIEW.collectionDescription(collection);
var jobStatesSummary = collection.jobStatesSummary;
@@ -1,7 +1,9 @@
import Utils from "utils/utils";
function _templateNametag(tag) {
return `<span style="${Utils.generateTagStyle(tag.slice(5))}" class="badge badge-primary badge-tags">${_.escape(tag.slice(5))}</span>`;
return `<span style="${Utils.generateTagStyle(tag.slice(5))}" class="badge badge-primary badge-tags">${_.escape(
tag.slice(5)
)}</span>`;
}
function nametagTemplate(historyItem) {
+6 -4
View File
@@ -1,5 +1,7 @@
import $ from "jquery";
import Backbone from "backbone";
import _ from "underscore";
import BASE_MVC from "mvc/base-mvc";
import _l from "utils/localization";
var logNamespace = "list";
//==============================================================================
@@ -142,7 +144,7 @@ var ExpandableView = Backbone.View.extend(BASE_MVC.LoggableMixin).extend({
if (!this.model.hasDetails()) {
return this.model.fetch();
}
return jQuery.when();
return $.when();
},
/** Inner fn called when expand (public) has fetched the details */
@@ -182,7 +184,7 @@ var ExpandableView = Backbone.View.extend(BASE_MVC.LoggableMixin).extend({
*
* Designed as a base class for history panel contents - but usable elsewhere (I hope).
*/
var ListItemView = ExpandableView.extend(
export var ListItemView = ExpandableView.extend(
BASE_MVC.mixin(BASE_MVC.SelectableViewMixin, BASE_MVC.DraggableViewMixin, {
tagName: "div",
className: "list-item",
@@ -380,7 +382,7 @@ ListItemView.prototype.templates = (() => {
*
* _renderDetails does the work of creating this.details: a sub-view that shows the nested list
*/
var FoldoutListItemView = ListItemView.extend({
export var FoldoutListItemView = ListItemView.extend({
/** If 'foldout': show the sub-panel inside the expanded item
* If 'drilldown': only fire events and handle by pub-sub
* (allow the panel containing this item to attach it, hide itself, etc.)
@@ -1,3 +1,6 @@
import $ from "jquery";
import Backbone from "backbone";
import _ from "underscore";
import LIST_ITEM from "mvc/list/list-item";
import LoadingIndicator from "ui/loading-indicator";
import BASE_MVC from "mvc/base-mvc";
@@ -208,9 +208,9 @@ var View = Backbone.View.extend({
${_.reduce(
filteredTags.slice(0, 5),
(memo, tag) => {
return `${memo}&nbsp;<div style="${Utils.generateTagStyle(tag.slice(5))}" class="badge badge-primary badge-tags">${_.escape(
tag
)}</div>`;
return `${memo}&nbsp;<div style="${Utils.generateTagStyle(
tag.slice(5)
)}" class="badge badge-primary badge-tags">${_.escape(tag)}</div>`;
},
""
)}
+18 -20
View File
@@ -358,7 +358,8 @@ export function setWindowTitle(title) {
* @returns {integer}
*/
function hashFnv32a(str) {
var i, l,
var i,
l,
hval = 0x811c9dc5;
for (i = 0, l = str.length; i < l; i++) {
@@ -380,10 +381,8 @@ function hashFnv32a(str) {
* Assumes r, g, b are in the set [0, 1]
*/
function contrastingColor(r, g, b) {
var o = ((r * 255 * 299) +
(g * 255 * 587) +
(b * 255 * 114)) / 1000;
return (o > 125) ? 'black' : 'white';
var o = (r * 255 * 299 + g * 255 * 587 + b * 255 * 114) / 1000;
return o > 125 ? "black" : "white";
}
/**
@@ -397,26 +396,26 @@ function contrastingColor(r, g, b) {
* @param {number} l The lightness
* @return {Array} The RGB representation
*/
function hslToRgb(h, s, l){
function hslToRgb(h, s, l) {
var r, g, b;
if(s == 0){
if (s == 0) {
r = g = b = l; // achromatic
}else{
var hue2rgb = function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
} else {
var hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
};
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
b = hue2rgb(p, q, h - 1 / 3);
}
return [r, g, b];
@@ -431,12 +430,11 @@ export function generateTagStyle(tag) {
var brColor = `hsl(${hue}, 100%, ${lightness - 40}%)`;
var [r, g, b] = hslToRgb(hue, 1.0, lightness / 100);
var fgColor = contrastingColor(r, g, b)
var fgColor = contrastingColor(r, g, b);
return `background-color: ${bgColor}; color: ${fgColor}; border: 1px solid ${brColor}`
return `background-color: ${bgColor}; color: ${fgColor}; border: 1px solid ${brColor}`;
}
export default {
cssLoadFile: cssLoadFile,
cssGetAttribute: cssGetAttribute,
+1
View File
@@ -538,6 +538,7 @@
<datatype extension="biom2" type="galaxy.datatypes.binary:Biom2" mimetype="application/octet-stream" display_in_upload="true">
<converter file="biom2_to_biom1.xml" target_datatype="biom1"/>
</datatype>
<datatype extension="msh" type="galaxy.datatypes.binary:MashSketch" display_in_upload="True" />
<!-- Strand-specific Coordinate Count Datatype used by the Center for Eukaryotic Gene Regulation labs at Penn State -->
<datatype extension="scidx" type="galaxy.datatypes.interval:ScIdx" display_in_upload="true"/>
<!--Cheminformatics Datatypes -->
-4
View File
@@ -121,10 +121,6 @@
<tool file="phenotype_association/linkToGProfile.xml" />
<tool file="phenotype_association/linkToDavid.xml" />
<tool file="phenotype_association/ldtools.xml" />
<tool file="phenotype_association/pass.xml" />
<tool file="phenotype_association/gpass.xml" />
<tool file="phenotype_association/beam.xml" />
<tool file="phenotype_association/lps.xml" />
<tool file="phenotype_association/master2pg.xml" />
</section>
</toolbox>
+1 -1
View File
@@ -120,7 +120,7 @@ B: Update the dictionary structure in kwargs.
::
kwargs['runner_param_specs'].update(runner_param_specs)
kwargs.update({'runner_param_specs': runner_param_specs})
C: Now call the parent constructor to assign the values.
+12
View File
@@ -126,6 +126,18 @@ class Cel(Binary):
return False
class MashSketch(Binary):
"""
Mash Sketch file.
Sketches are used by the MinHash algorithm to allow fast distance estimations
with low storage and memory requirements. To make a sketch, each k-mer in a sequence
is hashed, which creates a pseudo-random identifier. By sorting these identifiers (hashes),
a small subset from the top of the sorted list can represent the entire sequence (these are min-hashes).
The more similar another sequence is, the more min-hashes it is likely to share.
"""
file_ext = "msh"
class CompressedArchive(Binary):
"""
Class describing an compressed binary file
+2 -5
View File
@@ -1441,11 +1441,9 @@ class JobWrapper(HasResourceParameters):
# why not re-use self.param_dict here?
param_dict = dict([(p.name, p.value) for p in job.parameters])
param_dict = self.tool.params_from_strings(param_dict, self.app)
# Create generated output children and primary datasets and add to param_dict
# Create generated output children and primary datasets and dynamic outputs.
tool_working_directory = self.tool_working_directory
collected_datasets = {
'primary': self.tool.collect_primary_datasets(out_data, self.get_tool_provided_job_metadata(), tool_working_directory, input_ext, input_dbkey)
}
self.tool.collect_primary_datasets(out_data, self.get_tool_provided_job_metadata(), tool_working_directory, input_ext, input_dbkey)
self.tool.collect_dynamic_outputs(
out_collections,
self.get_tool_provided_job_metadata(),
@@ -1454,7 +1452,6 @@ class JobWrapper(HasResourceParameters):
job=job,
input_dbkey=input_dbkey,
)
param_dict.update({'__collected_datasets__': collected_datasets})
# Certain tools require tasks to be completed after job execution
# ( this used to be performed in the "exec_after_process" hook, but hooks are deprecated ).
self.tool.exec_after_process(self.app, inp_data, out_data, param_dict, job=job)
+5 -3
View File
@@ -81,9 +81,11 @@
.. class:: infomark
This tool will take an input list and a text file with identifiers to
filter the list with. It will build two new lists - one "filtered" to
contain only the supplied identifiers and one of the discarded elements.
This tool will take an input list and a text file with
names (i.e. identifiers). It will split the input list into
two new lists - one filtered to contain only the list members
whose names are listed in the text file, and one containing
all the other elements.
This tool will create new history datasets from your collection
but your quota usage will not increase.
@@ -613,6 +613,7 @@ def collect_primary_datasets(tool, output, tool_provided_metadata, job_working_d
extra_files_path_joined = os.path.join(job_working_directory, extra_files_path)
for root, dirs, files in os.walk(extra_files_path_joined):
extra_dir = os.path.join(primary_data.extra_files_path, root.replace(extra_files_path_joined, '', 1).lstrip(os.path.sep))
extra_dir = os.path.normpath(extra_dir)
for f in files:
app.object_store.update_from_file(
primary_data.dataset,
@@ -620,7 +621,6 @@ def collect_primary_datasets(tool, output, tool_provided_metadata, job_working_d
alt_name=f,
file_name=os.path.join(root, f),
create=True,
dir_only=True,
preserve_symlinks=True
)
metadata_dict = new_primary_datasets_attributes.get('metadata', None)
@@ -631,8 +631,6 @@ def collect_primary_datasets(tool, output, tool_provided_metadata, job_working_d
else:
primary_data.set_meta()
primary_data.set_peek()
sa_session.add(primary_data)
sa_session.flush()
outdata.history.add_dataset(primary_data)
# Add dataset to return dict
primary_datasets[name][designation] = primary_data
@@ -651,7 +649,8 @@ def collect_primary_datasets(tool, output, tool_provided_metadata, job_working_d
outdata.set_meta()
outdata.set_peek()
sa_session.add(outdata)
sa_session.flush()
sa_session.flush()
return primary_datasets
+8 -6
View File
@@ -69,6 +69,9 @@ def verify(
errmsg += str(err)
raise AssertionError(errmsg)
if attributes is None:
attributes = {}
if filename is not None:
local_name = get_filename(filename)
temp_name = make_temp_fname(fname=filename)
@@ -88,8 +91,6 @@ def verify(
else:
log.debug('## GALAXY_TEST_SAVE=%s. saved %s' % (keep_outputs_dir, ofn))
try:
if attributes is None:
attributes = {}
compare = attributes.get('compare', 'diff')
if attributes.get('ftype', None) in ['bam', 'qname_sorted.bam', 'qname_input_sorted.bam', 'unsorted.bam']:
local_fh, temp_name = _bam_to_sam(local_name, temp_name)
@@ -110,10 +111,6 @@ def verify(
files_contains(local_name, temp_name, attributes=attributes)
else:
raise Exception('Unimplemented Compare type: %s' % compare)
if verify_extra_files:
extra_files = attributes.get('extra_files', None)
if extra_files:
verify_extra_files(extra_files)
except AssertionError as err:
errmsg = '%s different than expected, difference (using %s):\n' % (item_label, compare)
errmsg += "( %s v. %s )\n" % (local_name, temp_name)
@@ -123,6 +120,11 @@ def verify(
if 'GALAXY_TEST_NO_CLEANUP' not in os.environ:
os.remove(temp_name)
if verify_extra_files:
extra_files = attributes.get('extra_files', None)
if extra_files:
verify_extra_files(extra_files)
def make_temp_fname(fname=None):
"""Safe temp name - preserve the file extension for tools that interpret it."""
@@ -0,0 +1,61 @@
"""
API operations on annotations.
"""
import logging
from galaxy import queue_worker
from galaxy.web import expose_api, require_admin
from galaxy.web.base.controller import BaseAPIController
log = logging.getLogger(__name__)
class DisplayApplicationsController(BaseAPIController):
@expose_api
def index(self, trans, **kwd):
"""
GET /api/display_applications/
Returns the list of display applications.
:returns: list of available display applications
:rtype: list
"""
response = []
for display_app in trans.app.datatypes_registry.display_applications.values():
response.append({
'id' : display_app.id,
'name': display_app.name,
'version': display_app.version,
'filename_': display_app._filename,
'links': [{'name': l.name} for l in display_app.links.values()]
})
return response
@expose_api
@require_admin
def reload(self, trans, payload={}, **kwd):
"""
POST /api/display_applications/reload
Reloads the list of display applications.
:param ids: list containing ids of display to be reloaded
:type ids: list
"""
ids = payload.get('ids')
queue_worker.send_control_task(trans.app,
'reload_display_application',
noop_self=True,
kwargs={'display_application_ids': ids})
reloaded, failed = trans.app.datatypes_registry.reload_display_applications(ids)
if not reloaded and failed:
message = 'Unable to reload any of the %i requested display applications ("%s").' % (len(failed), '", "'.join(failed))
elif failed:
message = 'Reloaded %i display applications ("%s"), but failed to reload %i display applications ("%s").' % (len(reloaded), '", "'.join(reloaded), len(failed), '", "'.join(failed))
elif not reloaded:
message = 'You need to request at least one display application to reload.'
else:
message = 'Reloaded %i requested display applications ("%s").' % (len(reloaded), '", "'.join(reloaded))
return {'message': message, 'reloaded': reloaded, 'failed': failed}
+17
View File
@@ -104,6 +104,7 @@ def app_factory(global_conf, load_app_kwds={}, **kwargs):
webapp.add_client_route('/admin/data_manager{path_info:.*}', 'admin')
webapp.add_client_route('/admin/error_stack', 'admin')
webapp.add_client_route('/admin/users', 'admin')
webapp.add_client_route('/admin/display_applications', 'admin')
webapp.add_client_route('/admin/roles', 'admin')
webapp.add_client_route('/admin/forms', 'admin')
webapp.add_client_route('/admin/groups', 'admin')
@@ -555,6 +556,22 @@ def populate_api_routes(webapp, app):
action='get_api_key',
conditions=dict(method=["GET"]))
# ======================================
# ====== DISPLAY APPLICATIONS API ======
# ======================================
webapp.mapper.connect('index',
'/api/display_applications',
controller='display_applications',
action='index',
conditions=dict(method=["GET"]))
webapp.mapper.connect('reload',
'/api/display_applications/reload',
controller='display_applications',
action='reload',
conditions=dict(method=["POST"]))
# =====================
# ===== TOURS API =====
# =====================
@@ -902,29 +902,6 @@ class AdminGalaxy(controller.JSAppLauncher, AdminActions, UsesQuotaMixin, QuotaP
message=message,
status=status)
@web.expose
@web.require_admin
def display_applications(self, trans, **kwd):
return trans.fill_template('admin/view_display_applications.mako', display_applications=trans.app.datatypes_registry.display_applications)
@web.expose
@web.require_admin
def reload_display_application(self, trans, **kwd):
galaxy.queue_worker.send_control_task(trans.app,
'reload_display_application',
noop_self=True,
kwargs={'display_application_ids': kwd.get('id')})
reloaded, failed = trans.app.datatypes_registry.reload_display_applications(kwd.get('id'))
if not reloaded and failed:
return trans.show_error_message('Unable to reload any of the %i requested display applications ("%s").'
% (len(failed), '", "'.join(failed)))
if failed:
return trans.show_warn_message('Reloaded %i display applications ("%s"), but failed to reload %i display applications ("%s").'
% (len(reloaded), '", "'.join(reloaded), len(failed), '", "'.join(failed)))
if not reloaded:
return trans.show_warn_message('You need to request at least one display application to reload.')
return trans.show_ok_message('Reloaded %i requested display applications ("%s").' % (len(reloaded), '", "'.join(reloaded)))
@web.expose
@web.require_admin
def center(self, trans, **kwd):
@@ -1,48 +0,0 @@
<%inherit file="/base.mako"/>
<%namespace file="/message.mako" import="render_msg" />
%if message:
${render_msg( message, status )}
%endif
<div class="card" id="display-applications-grid">
<div class="card-header">There are currently ${len( display_applications )} <a class="icon-btn" href="${ h.url_for( controller='admin', action='reload_display_application' ) }" title="Reload all display applications" data-placement="bottom">
<span class="fa fa-refresh"></span>
</a> display applications loaded.</div>
<div class="card-body overflow-auto">
<table class="manage-table colored">
<tr>
<th>Reload</th>
<th>Name</th>
<th>ID</th>
<th>Version</th>
<th>Links</th>
<th>Filename</th>
</tr>
<% ctr = 0 %>
%for display_app in display_applications.values():
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr class="tr">
%endif
<td>
<a class="icon-btn" href="${ h.url_for( controller='admin', action='reload_display_application', id=display_app.id ) }" title="Reload ${ display_app.name | h } display application" data-placement="bottom">
<span class="fa fa-refresh"></span>
</a>
</td>
<td>${ display_app.name | h }</td>
<td>${ display_app.id | h }</td>
<td>${ display_app.version | h }</td>
<td><ul>
%for link in display_app.links.values():
<li>${ link.name | h }</li>
%endfor
</ul></td>
<td>${ display_app._filename | h }</td>
</tr>
<% ctr += 1 %>
%endfor
</table>
</div>
</div>
@@ -28,6 +28,9 @@
<tool file="tool_provided_metadata_7.xml" />
<tool file="tool_provided_metadata_8.xml" />
<tool file="tool_provided_metadata_9.xml" />
<tool file="tool_provided_metadata_10.xml" />
<tool file="tool_provided_metadata_11.xml" />
<tool file="tool_provided_metadata_12.xml" />
<tool file="empty_datasets.xml" />
<tool file="inputs_as_json.xml" />
<tool file="inputs_as_json_with_paths.xml" />
@@ -0,0 +1,38 @@
<tool id="tool_provided_metadata_10" name="tool_provided_metadata_10" profile="17.09" version="1.0.0">
<!-- Demonstrate setting datatype defined metadata for discovered datasets via galaxy.json for
profile >= 17.09 tools (see tool_provided_metadata_3 for legacy version of this test). -->
<command>
echo "1" > sample1.report.tsv;
cp $c1 galaxy.json;
</command>
<configfiles>
<configfile name="c1">{"sample": {
"datasets": [
{"filename": "sample1.report.tsv", "name": "cool name 1", "ext": "txt", "info": "cool 1 info", "dbkey": "hg19", "metadata": {"data_lines": 10}}
]
}}
</configfile>
</configfiles>
<inputs>
<param name="input" type="data" />
</inputs>
<outputs>
<data name="sample">
<discover_datasets pattern="(?P&lt;designation&gt;.+)\.report\.tsv" visible="true" />
</data>
</outputs>
<tests>
<test>
<param name="input" ftype="txt" value="simple_line.txt"/>
<output name="sample">
<discovered_dataset designation="sample1" ftype="txt">
<assert_contents><has_line line="1" /></assert_contents>
<metadata name="name" value="cool name 1" />
<metadata name="dbkey" value="hg19" />
<metadata name="info" value="cool 1 info" />
<metadata name="data_lines" value="10" />
</discovered_dataset>
</output>
</test>
</tests>
</tool>
@@ -0,0 +1,41 @@
<tool id="tool_provided_metadata_11" name="tool_provided_metadata_11">
<!-- Demonstrate setting simple dataset metadata for discovered datasets via galaxy.json for legacy tools (galaxy.json structure changes for profile >= 17.09 tools). -->
<command>
mkdir sample1data;
echo "1" > sample1.report.tsv;
echo "foo line" > sample1data/foo;
echo "moo cow bar" > sample1data/bar;
cp $c1 galaxy.json;
</command>
<configfiles>
<configfile name="c1">{"type": "new_primary_dataset", "filename": "sample1.report.tsv", "name": "cool name 1", "ext": "txt", "info": "cool 1 info", "dbkey": "hg19", "extra_files": "sample1data"}
</configfile>
</configfiles>
<inputs>
<param name="input" type="data" />
</inputs>
<outputs>
<data name="sample">
<discover_datasets pattern="(?P&lt;designation&gt;.+)\.report\.tsv" visible="true" />
</data>
</outputs>
<tests>
<test>
<param name="input" ftype="txt" value="simple_line.txt"/>
<output name="sample">
<discovered_dataset designation="sample1" ftype="txt">
<assert_contents><has_line line="1" /></assert_contents>
<metadata name="name" value="cool name 1" />
<metadata name="dbkey" value="hg19" />
<metadata name="info" value="cool 1 info" />
<extra_files type="file" name="foo">
<assert_contents><has_line line="foo line" /></assert_contents>
</extra_files>
<extra_files type="file" name="bar">
<assert_contents><has_line line="moo cow bar" /></assert_contents>
</extra_files>
</discovered_dataset>
</output>
</test>
</tests>
</tool>
@@ -0,0 +1,45 @@
<tool id="tool_provided_metadata_12" name="tool_provided_metadata_12" profile="17.09" version="1.0.0">
<!-- Demonstrate setting extra_files for discovered primary datasets in profile >= 17.09 tools. -->
<command>
mkdir sample1data;
echo "1" > sample1.report.tsv;
echo "foo line" > sample1data/foo;
echo "moo cow bar" > sample1data/bar;
cp $c1 galaxy.json;
</command>
<configfiles>
<configfile name="c1">{"sample": {
"datasets": [
{"filename": "sample1.report.tsv", "name": "cool name 1", "ext": "txt", "info": "cool 1 info", "dbkey": "hg19", "extra_files": "sample1data"}
]
}}
</configfile>
</configfiles>
<inputs>
<param name="input" type="data" />
</inputs>
<outputs>
<data name="sample">
<discover_datasets pattern="(?P&lt;designation&gt;.+)\.report\.tsv" visible="true" />
</data>
</outputs>
<tests>
<test>
<param name="input" ftype="txt" value="simple_line.txt"/>
<output name="sample">
<discovered_dataset designation="sample1" ftype="txt">
<assert_contents><has_line line="1" /></assert_contents>
<metadata name="name" value="cool name 1" />
<metadata name="dbkey" value="hg19" />
<metadata name="info" value="cool 1 info" />
<extra_files type="file" name="foo">
<assert_contents><has_line line="foo line" /></assert_contents>
</extra_files>
<extra_files type="file" name="bar">
<assert_contents><has_line line="moo cow bar" /></assert_contents>
</extra_files>
</discovered_dataset>
</output>
</test>
</tests>
</tool>
+1 -2
View File
@@ -23,8 +23,7 @@ class AdminAppTestCase(SeleniumTestCase):
self.screenshot("admin_data_tables")
admin_component.index.display_applications.wait_for_and_click()
with self.main_panel():
admin_component.display_applications_grid.wait_for_visible()
self.assert_warning_message("No display applications available.")
self.screenshot("admin_display_applications")
admin_component.index.jobs.wait_for_and_click()